Files
ECC/scripts/lib/install-targets/registry.js
T
Brian SwigerandCopilot d58441072d feat(install): add a copilot project target for skills and agents
GitHub Copilot CLI discovers SKILL.md skills from .github/skills/ and custom
agents from .github/agents/, so ECC's catalog can be installed there without a
conversion step. This adds a managed-project install target that places both.

Scope is deliberately limited to agents and skills. ECC hooks are written
against Claude Code's event model, and commands rely on slash-command argument
substitution Copilot does not provide, so neither is installed.

Agent frontmatter goes through an allowlist transform rather than a passthrough:
Copilot resolves the session model from user configuration, so a Claude model id
makes it warn and fall back on every invocation; Claude tool names are not
Copilot tool names, and Copilot governs tool access per session via
--allow-tool/--deny-tool. Unknown keys added to ECC agents later therefore
cannot leak into the Copilot copy.

Target selection stays permissive in supportsModule so dependency-anchor modules
still resolve; planOperations is what narrows the install, matching the
convention used by the other adapters.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-04 20:58:17 +01:00

94 lines
2.8 KiB
JavaScript

const adalProject = require('./adal-project');
const antigravityProject = require('./antigravity-project');
const claudeHome = require('./claude-home');
const claudeProject = require('./claude-project');
const codebuddyProject = require('./codebuddy-project');
const codexHome = require('./codex-home');
const copilotProject = require('./copilot-project');
const cursorProject = require('./cursor-project');
const geminiProject = require('./gemini-project');
const hermesHome = require('./hermes-home');
const joycodeProject = require('./joycode-project');
const kimiProject = require('./kimi-project');
const openclawHome = require('./openclaw-home');
const opencodeHome = require('./opencode-home');
const qwenHome = require('./qwen-home');
const zedProject = require('./zed-project');
const { resolveInvocationEnvironment } = require('../invocation-environment');
const ADAPTERS = Object.freeze([
claudeHome,
claudeProject,
cursorProject,
antigravityProject,
codexHome,
copilotProject,
geminiProject,
hermesHome,
opencodeHome,
openclawHome,
codebuddyProject,
joycodeProject,
kimiProject,
qwenHome,
zedProject,
adalProject,
]);
function listInstallTargetAdapters() {
return ADAPTERS.slice();
}
function getInstallTargetAdapter(targetOrAdapterId) {
const adapter = ADAPTERS.find(candidate => candidate.supports(targetOrAdapterId));
if (!adapter) {
throw new Error(`Unknown install target adapter: ${targetOrAdapterId}`);
}
return adapter;
}
function planInstallTargetScaffold(options = {}) {
const adapter = getInstallTargetAdapter(options.target);
const modules = Array.isArray(options.modules) ? options.modules : [];
const exemptValidationCodes = new Set(Array.isArray(options.exemptValidationCodes) ? options.exemptValidationCodes : []);
const planningInput = {
repoRoot: options.repoRoot,
projectRoot: options.projectRoot || options.repoRoot,
homeDir: options.homeDir,
env: resolveInvocationEnvironment(options),
};
const validationIssues = adapter.validate(planningInput);
const blockingIssues = validationIssues.filter(issue => (
issue.severity === 'error' && !exemptValidationCodes.has(issue.code)
));
if (blockingIssues.length > 0) {
throw new Error(blockingIssues.map(issue => issue.message).join('; '));
}
const targetRoot = adapter.resolveRoot(planningInput);
const installStatePath = adapter.getInstallStatePath(planningInput);
const operations = adapter.planOperations({
...planningInput,
modules,
});
return {
adapter: {
id: adapter.id,
target: adapter.target,
kind: adapter.kind,
},
targetRoot,
installStatePath,
validationIssues,
operations,
};
}
module.exports = {
getInstallTargetAdapter,
listInstallTargetAdapters,
planInstallTargetScaffold,
};