/**
 * Agent Registry — 53 AI coding agents with metadata.
 *
 * Each agent has detection paths, parent company info, and feature support.
 * Used to determine which agents can install/use verified skills.
 *
 * Source of truth for the count: `TOTAL_AGENTS` (derived from AGENTS_REGISTRY.length).
 * The numbers in this header are documentation only; tests assert against TOTAL_AGENTS.
 */
/** Feature support flags for an agent */
// 0706 T-002 / T-003: imports for function-based detectInstalled.
// `detectBinary` is the platform-aware replacement for literal "which <bin>"
// strings that fail on Windows cmd.exe. Named imports from node:fs/os/path
// let vitest mock the probes in unit tests without default-export shenanigans.
import { join as joinPath } from "node:path";
import { existsSync as fsExistsSync, readdirSync as fsReaddirSync } from "node:fs";
import { homedir as osHomedir } from "node:os";
import { detectBinary } from "../utils/resolve-binary.js";
// 0845 T-006..T-010 — per-tool format transformers wired onto Tier-2
// entries below. Each is a pure function (parsedSkill → files).
import { cursorTransformer } from "../installer/transformers/cursor.js";
import { windsurfTransformer } from "../installer/transformers/windsurf.js";
import { githubCopilotTransformer } from "../installer/transformers/github-copilot.js";
import { junieTransformer } from "../installer/transformers/junie.js";
import { kiroTransformer } from "../installer/transformers/kiro.js";
import { continueDevTransformer } from "../installer/transformers/continue-dev.js";
import { traeTransformer } from "../installer/transformers/trae.js";
import { aiderTransformer } from "../installer/transformers/aider.js";
/** 0694 (AC-US1-04): Backward-compat alias map for renamed agent ids.
 *  Consumed by `getAgent()` so existing scripts/lockfiles continue to work
 *  after a rename. Keys are legacy ids, values are current canonical ids. */
export const LEGACY_AGENT_IDS = Object.freeze({
    // 0694 US-001: split conflated `github-copilot` into VS Code extension
    // (renamed to `github-copilot-ext`) and standalone CLI (`copilot-cli`).
    "github-copilot": "github-copilot-ext",
});
/**
 * Complete registry of 53 AI coding agents.
 *
 * 8 universal agents, 45 non-universal agents.
 * Use TOTAL_AGENTS for programmatic access to the count.
 */
export const AGENTS_REGISTRY = [
    // ----------------------------------------------------------------
    // Universal agents (8)
    // ----------------------------------------------------------------
    {
        id: 'amp',
        displayName: 'Amp',
        localSkillsDir: '.amp/skills',
        globalSkillsDir: '~/.config/agents/skills',
        isUniversal: true,
        detectInstalled: () => detectBinary('amp'),
        parentCompany: 'Sourcegraph',
        featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
    },
    {
        id: 'cline',
        displayName: 'Cline',
        localSkillsDir: '.cline/skills',
        globalSkillsDir: '~/.cline/skills',
        isUniversal: true,
        detectInstalled: () => detectBinary('cline'),
        parentCompany: 'Cline',
        featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
    },
    {
        id: 'codex',
        displayName: 'Codex CLI',
        localSkillsDir: '.codex/skills',
        globalSkillsDir: '~/.codex/skills',
        isUniversal: true,
        detectInstalled: () => detectBinary('codex'),
        parentCompany: 'OpenAI',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
        // 0845 T-003 (AC-US3-04): explicit Tier 1 drop-in. Codex is a primary
        // Studio install target whose binary often isn't on PATH; tier annotation
        // ensures it surfaces under "Available to install" rather than relying on
        // the optional-field default.
        tier: 1,
        installMode: 'filesystem',
    },
    {
        id: 'cursor',
        displayName: 'Cursor',
        localSkillsDir: '.cursor/skills',
        globalSkillsDir: '~/.cursor/skills',
        isUniversal: true,
        detectInstalled: () => detectBinary('cursor'),
        parentCompany: 'Anysphere',
        featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
        // 0845 T-003: Tier 2 — emits `.cursor/rules/<name>.mdc` via transformer.
        tier: 2,
        installMode: 'filesystem',
        formatTransformer: cursorTransformer,
    },
    {
        id: 'gemini-cli',
        displayName: 'Gemini CLI',
        localSkillsDir: '.gemini/skills',
        globalSkillsDir: '~/.gemini/skills',
        isUniversal: true,
        detectInstalled: () => detectBinary('gemini'),
        parentCompany: 'Google',
        featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: true },
        tier: 1,
        installMode: 'filesystem',
    },
    {
        // 0694 US-001 (AC-US1-02): renamed from `github-copilot` to free that ID
        // for the standalone `copilot-cli` entry below. VS Code extensions live
        // under versioned dirs (`~/.vscode/extensions/github.copilot-<version>/`),
        // so `globalSkillsDir` uses a vskill-managed fallback. POSIX-only — a
        // Windows-equivalent extension glob is still TODO.
        id: 'github-copilot-ext',
        displayName: 'GitHub Copilot (VS Code)',
        localSkillsDir: '.github/copilot/skills',
        globalSkillsDir: '~/.config/github-copilot/skills',
        isUniversal: true,
        // 0706 T-003: pure-Node detection — the old shell pipe
        // (`which code && ls ~/.vscode/extensions/github.copilot-* 2>/dev/null`)
        // can't run on Windows cmd.exe (no `which`, no `~`, no `&&`, no
        // `2>/dev/null`). Replace with a portable probe: VS Code on PATH AND
        // at least one `github.copilot-*` dir under `~/.vscode/extensions`.
        detectInstalled: async () => {
            if (!(await detectBinary('code')))
                return false;
            const extDir = joinPath(osHomedir(), '.vscode', 'extensions');
            if (!fsExistsSync(extDir))
                return false;
            try {
                const entries = fsReaddirSync(extDir);
                return entries.some((e) => e.startsWith('github.copilot-'));
            }
            catch {
                return false;
            }
        },
        parentCompany: 'GitHub (Microsoft)',
        featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
        // 0845 T-003: Tier 2 — emits `.github/instructions/<name>.instructions.md`.
        tier: 2,
        installMode: 'filesystem',
        formatTransformer: githubCopilotTransformer,
    },
    {
        id: 'kimi-cli',
        displayName: 'Kimi CLI',
        localSkillsDir: '.kimi/skills',
        globalSkillsDir: '~/.config/agents/skills',
        isUniversal: true,
        detectInstalled: () => detectBinary('kimi'),
        parentCompany: 'Moonshot AI',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'opencode',
        displayName: 'OpenCode',
        localSkillsDir: '.opencode/skills',
        globalSkillsDir: '~/.config/opencode/skills',
        isUniversal: true,
        detectInstalled: () => detectBinary('opencode'),
        parentCompany: 'Community',
        featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: true },
        tier: 1,
        installMode: 'filesystem',
    },
    // ----------------------------------------------------------------
    // Non-universal agents (45)
    // ----------------------------------------------------------------
    {
        id: 'antigravity',
        displayName: 'Antigravity',
        localSkillsDir: '.agent/skills',
        globalSkillsDir: '~/.gemini/antigravity/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('antigravity'),
        parentCompany: 'Google',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
        // 0845 T-002 (AC-US3-01): explicit Tier 1 drop-in. globalSkillsDir override
        // via VSKILL_ANTIGRAVITY_SKILLS_DIR is handled in getSupportedAgents().
        tier: 1,
        installMode: 'filesystem',
    },
    {
        id: 'augment',
        displayName: 'Augment Code',
        localSkillsDir: '.augment/skills',
        globalSkillsDir: '~/.augment/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('augment'),
        parentCompany: 'Augment',
        featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
    },
    {
        id: 'claude-code',
        displayName: 'Claude Code',
        localSkillsDir: '.claude/skills',
        globalSkillsDir: '~/.claude/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('claude'),
        parentCompany: 'Anthropic',
        featureSupport: { slashCommands: true, hooks: true, mcp: true, customSystemPrompt: true },
        pluginCacheDir: '~/.claude/plugins/cache',
        pluginMarketplaceDir: '~/.claude/plugins/marketplaces',
        tier: 1,
        installMode: 'filesystem',
    },
    {
        id: 'openclaw',
        displayName: 'OpenClaw',
        localSkillsDir: '.openclaw/skills',
        globalSkillsDir: '~/.openclaw/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('openclaw'),
        parentCompany: 'Community',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
        tier: 1,
        installMode: 'filesystem',
    },
    {
        id: 'replit',
        displayName: 'Replit Agent',
        localSkillsDir: '.replit/skills',
        globalSkillsDir: '~/.config/agents/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('replit'),
        parentCompany: 'Replit',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
        isRemoteOnly: true,
    },
    {
        id: 'codebuddy',
        displayName: 'CodeBuddy',
        localSkillsDir: '.codebuddy/skills',
        globalSkillsDir: '~/.codebuddy/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('codebuddy'),
        parentCompany: 'CodeBuddy',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'command-code',
        displayName: 'Command Code',
        localSkillsDir: '.commandcode/skills',
        globalSkillsDir: '~/.commandcode/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('command-code'),
        parentCompany: 'Command',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'continue',
        displayName: 'Continue',
        localSkillsDir: '.continue/skills',
        globalSkillsDir: '~/.continue/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('continue'),
        parentCompany: 'Continue',
        featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
        // 0845 T-003: Tier 2 — emits `.continue/rules/<name>.md` (plain markdown).
        tier: 2,
        installMode: 'filesystem',
        formatTransformer: continueDevTransformer,
    },
    {
        id: 'crush',
        displayName: 'Crush',
        localSkillsDir: '.crush/skills',
        globalSkillsDir: '~/.config/crush/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('crush'),
        parentCompany: 'Crush',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'droid',
        displayName: 'Droid (Factory)',
        localSkillsDir: '.factory/skills',
        globalSkillsDir: '~/.factory/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('droid'),
        parentCompany: 'Factory',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'goose',
        displayName: 'Goose',
        localSkillsDir: '.goose/skills',
        globalSkillsDir: '~/.config/goose/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('goose'),
        parentCompany: 'Block',
        featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: true },
    },
    {
        id: 'junie',
        displayName: 'Junie',
        localSkillsDir: '.junie/skills',
        globalSkillsDir: '~/.junie/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('junie'),
        parentCompany: 'JetBrains',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
        // 0845 T-003: Tier 2 — emits `.junie/rules/<name>.md` (plain markdown).
        tier: 2,
        installMode: 'filesystem',
        formatTransformer: junieTransformer,
    },
    {
        id: 'iflow-cli',
        displayName: 'iFlow CLI',
        localSkillsDir: '.iflow/skills',
        globalSkillsDir: '~/.iflow/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('iflow'),
        parentCompany: 'iFlow',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'kilo',
        displayName: 'Kilo Code',
        localSkillsDir: '.kilocode/skills',
        globalSkillsDir: '~/.kilocode/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('kilocode'),
        parentCompany: 'Kilo Code',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'kiro-cli',
        displayName: 'Kiro CLI',
        localSkillsDir: '.kiro/skills',
        globalSkillsDir: '~/.kiro/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('kiro'),
        parentCompany: 'AWS',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
        // 0845 T-003: Tier 2 — emits `.kiro/steering/<name>.md` (plain markdown).
        tier: 2,
        installMode: 'filesystem',
        formatTransformer: kiroTransformer,
    },
    {
        id: 'kode',
        displayName: 'Kode',
        localSkillsDir: '.kode/skills',
        globalSkillsDir: '~/.kode/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('kode'),
        parentCompany: 'Kode',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'mcpjam',
        displayName: 'MCPJam',
        localSkillsDir: '.mcpjam/skills',
        globalSkillsDir: '~/.mcpjam/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('mcpjam'),
        parentCompany: 'MCPJam',
        featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: false },
    },
    {
        id: 'mistral-vibe',
        displayName: 'Mistral Vibe',
        localSkillsDir: '.vibe/skills',
        globalSkillsDir: '~/.vibe/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('vibe'),
        parentCompany: 'Mistral AI',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'mux',
        displayName: 'Mux',
        localSkillsDir: '.mux/skills',
        globalSkillsDir: '~/.mux/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('mux'),
        parentCompany: 'Mux',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'openhands',
        displayName: 'OpenHands',
        localSkillsDir: '.openhands/skills',
        globalSkillsDir: '~/.openhands/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('openhands'),
        parentCompany: 'All Hands AI',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'pi',
        displayName: 'Pi',
        localSkillsDir: '.pi/skills',
        globalSkillsDir: '~/.pi/agent/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('pi'),
        parentCompany: 'Pi',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'qoder',
        displayName: 'Qoder',
        localSkillsDir: '.qoder/skills',
        globalSkillsDir: '~/.qoder/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('qoder'),
        parentCompany: 'Qoder',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'qwen-code',
        displayName: 'Qwen Code',
        localSkillsDir: '.qwen/skills',
        globalSkillsDir: '~/.qwen/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('qwen-code'),
        parentCompany: 'Alibaba',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'roo',
        displayName: 'Roo Code',
        localSkillsDir: '.roo/skills',
        globalSkillsDir: '~/.roo/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('roo'),
        parentCompany: 'Roo',
        featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
    },
    {
        id: 'trae',
        displayName: 'Trae',
        localSkillsDir: '.trae/skills',
        globalSkillsDir: '~/.trae/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('trae'),
        parentCompany: 'ByteDance',
        featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: true },
        // 0845 T-003: Tier 2 — emits `.trae/<name>.md` (plain markdown).
        tier: 2,
        installMode: 'filesystem',
        formatTransformer: traeTransformer,
    },
    {
        id: 'trae-cn',
        displayName: 'Trae CN',
        localSkillsDir: '.trae-cn/skills',
        globalSkillsDir: '~/.trae-cn/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('trae-cn'),
        parentCompany: 'ByteDance',
        featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: true },
    },
    {
        id: 'windsurf',
        displayName: 'Windsurf',
        localSkillsDir: '.windsurf/skills',
        globalSkillsDir: '~/.codeium/windsurf/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('windsurf'),
        parentCompany: 'Codeium',
        featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
        // 0845 T-003: Tier 2 — emits `.windsurf/rules/<name>.md` (plain markdown).
        tier: 2,
        installMode: 'filesystem',
        formatTransformer: windsurfTransformer,
    },
    {
        id: 'zencoder',
        displayName: 'ZenCoder',
        localSkillsDir: '.zencoder/skills',
        globalSkillsDir: '~/.zencoder/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('zencoder'),
        parentCompany: 'ZenCoder',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'neovate',
        displayName: 'Neovate',
        localSkillsDir: '.neovate/skills',
        globalSkillsDir: '~/.neovate/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('neovate'),
        parentCompany: 'Neovate',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'pochi',
        displayName: 'Pochi',
        localSkillsDir: '.pochi/skills',
        globalSkillsDir: '~/.pochi/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('pochi'),
        parentCompany: 'Pochi',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'adal',
        displayName: 'Adal',
        localSkillsDir: '.adal/skills',
        globalSkillsDir: '~/.adal/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('adal'),
        parentCompany: 'Adal',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'cortex',
        displayName: 'Cortex',
        localSkillsDir: '.cortex/skills',
        globalSkillsDir: '~/.snowflake/cortex/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('cortex'),
        parentCompany: 'Cortex',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'aider',
        displayName: 'Aider',
        localSkillsDir: '.aider/skills',
        globalSkillsDir: '~/.aider/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('aider'),
        parentCompany: 'Aider',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
        // 0845 T-003: Tier 2 — emits conventions file + appends to ~/.aider.conf.yml.
        tier: 2,
        installMode: 'filesystem',
        formatTransformer: aiderTransformer,
    },
    {
        id: 'tabnine',
        displayName: 'Tabnine Chat',
        localSkillsDir: '.tabnine/skills',
        globalSkillsDir: '~/.tabnine/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('tabnine'),
        parentCompany: 'Tabnine',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        // 0694 US-004: Devin is a hosted web service — no local CLI to install
        // skills into. Catalog entry retained for discoverability; isRemoteOnly
        // suppresses install affordances and shows a "Remote" badge in Studio.
        id: 'devin',
        displayName: 'Devin',
        localSkillsDir: '.devin/skills',
        globalSkillsDir: '~/.devin/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('devin'),
        parentCompany: 'Cognition',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
        isRemoteOnly: true,
    },
    {
        // 0694 US-004: bolt.new is a browser-only product. See devin entry above.
        // 0845 T-003: kept isRemoteOnly: true for legacy callers (detectInstalledAgents
        // skips remote-only). The new `installMode: "clipboard"` is the gate Studio
        // uses to surface it under "Cloud only (paste required)". See ADR-0845-01.
        id: 'bolt-new',
        displayName: 'bolt.new',
        localSkillsDir: '.bolt/skills',
        globalSkillsDir: '~/.bolt/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('bolt'),
        parentCompany: 'StackBlitz',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
        isRemoteOnly: true,
        tier: 3,
        installMode: 'clipboard',
        pasteInstructionsUrl: 'https://bolt.new/',
        docsUrl: 'https://bolt.new/',
    },
    {
        // 0845 T-003 (AC-US5-01, plan.md §12 concern #1): NEW entry for ChatGPT
        // Custom Instructions / Project Instructions / Custom GPT. A single
        // registry row covers all three paste destinations; the UI may later
        // split them if per-flavor blob shaping is needed. isRemoteOnly stays
        // true so legacy detectInstalledAgents skips it; installMode=clipboard
        // is the new gate that makes Studio surface it as a paste-only target.
        id: 'chatgpt',
        displayName: 'ChatGPT',
        localSkillsDir: '.chatgpt/skills',
        globalSkillsDir: '~/.chatgpt/skills',
        isUniversal: false,
        detectInstalled: async () => false,
        parentCompany: 'OpenAI',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
        isRemoteOnly: true,
        tier: 3,
        installMode: 'clipboard',
        pasteInstructionsUrl: 'https://help.openai.com/en/articles/8096356-chatgpt-custom-instructions',
        docsUrl: 'https://chatgpt.com/#settings/Personalization',
    },
    {
        // 0694 US-004: v0 is a Vercel hosted UI generator. See devin entry above.
        // 0845 T-003: Tier 3 clipboard — see bolt-new note for the isRemoteOnly
        // + installMode dual-flag pattern.
        id: 'v0',
        displayName: 'v0',
        localSkillsDir: '.v0/skills',
        globalSkillsDir: '~/.v0/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('v0'),
        parentCompany: 'Vercel',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
        isRemoteOnly: true,
        tier: 3,
        installMode: 'clipboard',
        pasteInstructionsUrl: 'https://v0.dev/docs',
        docsUrl: 'https://v0.dev/',
    },
    {
        id: 'gpt-pilot',
        displayName: 'GPT Pilot',
        localSkillsDir: '.gpt-pilot/skills',
        globalSkillsDir: '~/.gpt-pilot/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('gpt-pilot'),
        parentCompany: 'Pythagora',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'plandex',
        displayName: 'Plandex',
        localSkillsDir: '.plandex/skills',
        globalSkillsDir: '~/.plandex/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('plandex'),
        parentCompany: 'Plandex',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'sweep',
        displayName: 'Sweep',
        localSkillsDir: '.sweep/skills',
        globalSkillsDir: '~/.sweep/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('sweep'),
        parentCompany: 'Sweep AI',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    {
        id: 'mentat',
        displayName: 'Mentat',
        localSkillsDir: '.mentat/skills',
        globalSkillsDir: '~/.mentat/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('mentat'),
        parentCompany: 'AbanteAI',
        featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
    },
    // ----------------------------------------------------------------
    // 0694: New CLI adapters (US-001 / US-002 / US-003 / US-005)
    // ----------------------------------------------------------------
    {
        // 0694 US-001 (AC-US1-01): standalone `copilot` binary (GA Apr 2026),
        // separate from the VS Code Copilot extension above.
        id: 'copilot-cli',
        displayName: 'GitHub Copilot CLI',
        localSkillsDir: '.copilot/skills',
        globalSkillsDir: '~/.copilot/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('copilot'),
        parentCompany: 'GitHub (Microsoft)',
        featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
    },
    {
        // 0694 US-002 (AC-US2-01): Warp Terminal "Oz" Agent Mode.
        // VERIFY: docs.warp.dev/agent-platform did not document a skills/rules
        // path at the time of this entry; using community-default `.warp/skills`
        // and `~/.warp/skills` until vendor confirms a canonical location.
        id: 'warp',
        displayName: 'Warp',
        localSkillsDir: '.warp/skills',
        globalSkillsDir: '~/.warp/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('warp'),
        parentCompany: 'Warp',
        featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: true },
    },
    {
        // 0694 US-003 (AC-US3-01): Amazon Q Developer CLI (`q` binary).
        // Confirmed binary name from github.com/aws/amazon-q-developer-cli.
        // VERIFY: skills dir convention not documented at time of entry; using
        // `~/.aws/amazonq/skills` (matches `~/.aws/amazonq` config root pattern).
        id: 'amazon-q-cli',
        displayName: 'Amazon Q CLI',
        localSkillsDir: '.amazonq/skills',
        globalSkillsDir: '~/.aws/amazonq/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('q'),
        parentCompany: 'AWS',
        featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: true },
    },
    {
        // 0694 US-005 (AC-US5-01): Zed editor agent panel.
        // Schema parity with specweave/src/adapters/registry.yaml (id: zed,
        // .zed/skills, MCP support). VERIFY: zed.dev/docs/ai/agent-panel does
        // not document a skills dir; following the .zed/ + .zed/skills/
        // convention used elsewhere in the registry.
        id: 'zed',
        displayName: 'Zed',
        localSkillsDir: '.zed/skills',
        globalSkillsDir: '~/.config/zed/skills',
        isUniversal: false,
        detectInstalled: () => detectBinary('zed'),
        parentCompany: 'Zed Industries',
        featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: true },
    },
];
/** Total number of registered agents */
export const TOTAL_AGENTS = AGENTS_REGISTRY.length;
// ---------------------------------------------------------------------------
// Non-agent config dirs — co-located here so all known config-dir
// prefixes have a single audit point alongside AGENTS_REGISTRY.
//
// These dirs are *not* agent install targets; they are sibling config dirs
// that the skill scanner / path validator must still recognize as
// "installed-style" first-segments (e.g., to avoid treating files inside
// .vscode or .github as user-authored skills).
// ---------------------------------------------------------------------------
export const NON_AGENT_CONFIG_DIRS = Object.freeze([
    ".specweave",
    ".vscode",
    ".idea",
    ".zed",
    ".devcontainer",
    ".github",
    ".agents",
    ".agent",
]);
/** Claude-specific frontmatter fields that non-Claude agents don't support */
const CLAUDE_STRIP_FIELDS = [
    'user-invocable',
    'allowed-tools',
    'model',
    'argument-hint',
    'context',
];
/**
 * Filter agents by feature requirements.
 * Returns agents where ALL specified features are true (AND logic).
 * Empty requirements object returns all agents.
 */
export function filterAgentsByFeatures(requirements) {
    const entries = Object.entries(requirements);
    if (entries.length === 0)
        return [...AGENTS_REGISTRY];
    return AGENTS_REGISTRY.filter((agent) => entries.every(([key, value]) => agent.featureSupport[key] === value));
}
/**
 * Get the creation profile for a specific agent.
 * Returns guidance on what to strip/add when generating skills for this agent.
 *
 * - Claude Code: empty stripFields, empty addGuidance (full feature support)
 * - Non-Claude: stripFields lists Claude-specific fields, addGuidance warns about unsupported features
 */
export function getAgentCreationProfile(agentId) {
    const agent = AGENTS_REGISTRY.find((a) => a.id === agentId);
    if (!agent)
        return undefined;
    const { featureSupport } = agent;
    // Claude Code has full feature support — no stripping or guidance needed
    if (agent.id === 'claude-code') {
        return { agent, stripFields: [], addGuidance: [], featureSupport };
    }
    const addGuidance = [];
    if (!featureSupport.slashCommands) {
        addGuidance.push('Do NOT reference slash commands (/command syntax) in the skill body.');
    }
    if (!featureSupport.hooks) {
        addGuidance.push('Do NOT include hook examples or lifecycle hook references.');
    }
    if (!featureSupport.mcp) {
        addGuidance.push('Do NOT reference MCP tools; use CLI/filesystem alternatives only.');
    }
    if (!featureSupport.customSystemPrompt) {
        addGuidance.push('Keep the skill as plain instructions without system prompt framing.');
    }
    return {
        agent,
        stripFields: CLAUDE_STRIP_FIELDS,
        addGuidance,
        featureSupport,
    };
}
/**
 * Returns all universal agents (support the universal skill format).
 */
export function getUniversalAgents() {
    return AGENTS_REGISTRY.filter((a) => a.isUniversal);
}
/**
 * Returns all non-universal agents.
 */
export function getNonUniversalAgents() {
    return AGENTS_REGISTRY.filter((a) => !a.isUniversal);
}
/**
 * Gets a single agent by ID. Honors `LEGACY_AGENT_IDS` so callers using a
 * renamed legacy id (e.g. `github-copilot`) still resolve to the current
 * canonical entry (e.g. `github-copilot-ext`).
 *
 * @param id - Agent identifier (current or legacy alias)
 * @returns The agent definition, or undefined if not found
 */
export function getAgent(id) {
    const canonical = LEGACY_AGENT_IDS[id] ?? id;
    return AGENTS_REGISTRY.find((a) => a.id === canonical);
}
/**
 * 0694 (AC-US4-03): Returns agents that can be installed locally.
 * Excludes any entry flagged `isRemoteOnly: true` (web-only tools like
 * Devin / bolt.new / v0 / Replit). Used by `vskill add`, the Studio
 * AgentScopePicker install affordances, and any caller that needs the
 * "real" installable agent list.
 */
export function getInstallableAgents() {
    return AGENTS_REGISTRY.filter((a) => a.isRemoteOnly !== true);
}
/**
 * Resolve a tilde-prefixed path to an absolute path.
 */
function expandHome(p) {
    if (p.startsWith('~/')) {
        const home = process.env.HOME || process.env.USERPROFILE || '';
        return home + p.slice(1);
    }
    return p;
}
/**
 * Detects which agents are installed on the current system.
 *
 * Detection strategy (in order):
 * 1. Run agent's `detectInstalled` shell command (typically `which <binary>`)
 * 2. Fallback: check if the agent's global config directory exists
 *    (e.g. ~/.cursor, ~/.windsurf — derived from globalSkillsDir parent)
 *
 * This two-tier approach catches desktop apps and IDE extensions that
 * create config directories but don't install CLI binaries in PATH.
 *
 * @returns Array of installed agent definitions
 */
export async function detectInstalledAgents() {
    const { exec } = await import('node:child_process');
    const { promisify } = await import('node:util');
    const { existsSync } = await import('node:fs');
    const { dirname } = await import('node:path');
    const execAsync = promisify(exec);
    const results = [];
    await Promise.allSettled(AGENTS_REGISTRY.map(async (agent) => {
        // Remote-only agents (devin, bolt-new, v0, replit) have no local install
        // surface. Several share `~/.config/agents/skills` with universal CLIs,
        // so the Tier 2 directory fallback would false-positive without this guard.
        if (agent.isRemoteOnly === true)
            return;
        // Tier 1: CLI binary detection.
        //
        // 0706 T-002: `detectInstalled` can be a legacy shell string (e.g.
        // `'which claude'`) OR a function returning `Promise<boolean>`. Strings
        // still go through exec (kept for backward compat with any external
        // consumer that built rows the old way); functions are called directly
        // — they own their own platform branching via `detectBinary()`.
        try {
            const detector = agent.detectInstalled;
            if (typeof detector === 'function') {
                const ok = await detector();
                if (ok) {
                    results.push(agent);
                    return;
                }
            }
            else {
                await execAsync(detector);
                results.push(agent);
                return;
            }
        }
        catch {
            // Binary not found — try directory fallback
        }
        // Tier 2: Config directory detection
        // Only check if the agent's global skills dir itself exists.
        // Checking the parent config dir (e.g. ~/.cursor/) caused massive false positives —
        // many tools leave config dirs behind even when not actively used,
        // leading to unwanted local skill directories being created for every detected agent.
        try {
            const globalDir = expandHome(agent.globalSkillsDir);
            const home = process.env.HOME || process.env.USERPROFILE || '';
            const configDir = dirname(globalDir);
            // Guard: don't match on generic dirs like $HOME or $HOME/.config
            if (configDir !== home && configDir !== `${home}/.config`) {
                if (existsSync(globalDir)) {
                    results.push(agent);
                }
            }
        }
        catch {
            // Skip on any error
        }
    }));
    return results.sort((a, b) => a.id.localeCompare(b.id));
}
/**
 * Resolve the global skills dir for a single agent, honoring env overrides.
 * The Antigravity entry supports `VSKILL_ANTIGRAVITY_SKILLS_DIR` so test
 * harnesses (and power users) can redirect installs without forking the
 * registry. See AC-US3-03.
 */
function resolveGlobalDirForAgent(agent) {
    if (agent.id === "antigravity" && process.env.VSKILL_ANTIGRAVITY_SKILLS_DIR) {
        return process.env.VSKILL_ANTIGRAVITY_SKILLS_DIR;
    }
    return expandHome(agent.globalSkillsDir);
}
/**
 * Probe a single agent's binary or config dir in isolation. Used by
 * getSupportedAgents() in parallel — never throws (returns false on
 * any error). Mirrors the two-tier detection logic in
 * detectInstalledAgents() so the two functions agree on "detected".
 */
async function probeAgentDetected(agent) {
    // Remote-only agents have no local binary to probe. They still surface
    // in getSupportedAgents() when they declare installMode=clipboard, but
    // they are by definition undetectable.
    if (agent.isRemoteOnly === true)
        return false;
    try {
        const detector = agent.detectInstalled;
        if (typeof detector === "function") {
            if (await detector())
                return true;
        }
        else {
            const { exec } = await import("node:child_process");
            const { promisify } = await import("node:util");
            const execAsync = promisify(exec);
            try {
                await execAsync(detector);
                return true;
            }
            catch {
                // fall through to dir fallback
            }
        }
    }
    catch {
        // fall through to dir fallback
    }
    try {
        const { existsSync } = await import("node:fs");
        const { dirname } = await import("node:path");
        const globalDir = expandHome(agent.globalSkillsDir);
        const home = process.env.HOME || process.env.USERPROFILE || "";
        const configDir = dirname(globalDir);
        if (configDir !== home && configDir !== `${home}/.config`) {
            if (existsSync(globalDir))
                return true;
        }
    }
    catch {
        // ignore — agent is simply undetected
    }
    return false;
}
/**
 * 0845 (AC-US1-01, AC-US6-02, AC-US6-03): Returns every registered agent
 * the Studio knows how to install to — Tier 1 / 2 (filesystem) and Tier 3
 * (clipboard). Independent of binary detection: undetected agents are
 * still included, with `detected: false`.
 *
 * The exclusion gate is `installMode`. Entries without an explicit
 * `installMode` default to "filesystem" UNLESS they are flagged
 * `isRemoteOnly: true` — in which case they are excluded (preserves
 * the pre-existing semantics for Devin and Replit, which have no
 * clipboard fallback). Tier 3 entries (chatgpt, bolt-new, v0) are
 * kept by virtue of setting `installMode: "clipboard"` explicitly.
 *
 * Detection probes run in parallel via `Promise.allSettled`
 * (AC-US6-02). `detectInstalledAgents()` is UNCHANGED (AC-US6-03).
 */
export async function getSupportedAgents() {
    const candidates = AGENTS_REGISTRY.filter((a) => {
        if (a.installMode === "clipboard")
            return true;
        if (a.installMode === "filesystem")
            return true;
        // No explicit installMode — default to "filesystem" only when not
        // remote-only. Pure remote-only agents (devin, replit) drop out here.
        return a.isRemoteOnly !== true;
    });
    const detectResults = await Promise.allSettled(candidates.map((a) => probeAgentDetected(a)));
    const supported = candidates.map((a, idx) => {
        const r = detectResults[idx];
        const detected = r.status === "fulfilled" ? r.value : false;
        const result = {
            id: a.id,
            displayName: a.displayName,
            detected,
            tier: (a.tier ?? 1),
            installMode: a.installMode ?? "filesystem",
            resolvedGlobalDir: resolveGlobalDirForAgent(a),
            resolvedLocalDir: a.localSkillsDir,
        };
        if (a.pasteInstructionsUrl)
            result.pasteInstructionsUrl = a.pasteInstructionsUrl;
        if (a.docsUrl)
            result.docsUrl = a.docsUrl;
        return result;
    });
    return supported.sort((a, b) => a.id.localeCompare(b.id));
}
//# sourceMappingURL=agents-registry.js.map