rush-ai 0.11.2 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/output/formatters/csv.ts","../src/output/formatters/table.ts","../src/output/formatters/index.ts","../src/util/ci.ts","../src/util/require-auth.ts","../src/commands/agent/index.ts","../src/commands/auth/browser-login.ts","../src/commands/auth/index.ts","../src/commands/check/index.ts","../src/commands/check/checks/database.ts","../src/commands/check/checks/framework.ts","../src/commands/check/checks/gitignore.ts","../src/util/git.ts","../src/commands/check/checks/lock-file.ts","../src/commands/check/checks/package-json.ts","../src/commands/check/checks/port-env.ts","../src/commands/completion/index.ts","../src/commands/config/index.ts","../src/commands/doctor/index.ts","../src/commands/doctor/checks/auth.ts","../src/commands/doctor/checks/config.ts","../src/commands/doctor/checks/connectivity.ts","../src/commands/doctor/checks/environment.ts","../src/commands/doctor/checks/plugins.ts","../src/marketplaces/cache.ts","../src/marketplaces/directory.ts","../src/marketplaces/manifest.ts","../src/marketplaces/source.ts","../src/marketplaces/github.ts","../src/commands/marketplace/add.ts","../src/installers/registry.ts","../src/commands/marketplace/list.ts","../src/commands/marketplace/remove.ts","../src/commands/marketplace/update.ts","../src/commands/marketplace/index.ts","../src/commands/mcp/index.ts","../src/installers/claude-code/installer.ts","../src/installers/claude-code/mcp.ts","../src/installers/claude-code/paths.ts","../src/installers/codex/installer.ts","../src/installers/codex/mcp.ts","../src/installers/codex/paths.ts","../src/installers/codex/toml.ts","../src/installers/cursor/installer.ts","../src/installers/cursor/marker.ts","../src/installers/cursor/mcp.ts","../src/installers/cursor/paths.ts","../src/installers/cursor/rules.ts","../src/installers/cursor/skills.ts","../src/migration/cleanup.ts","../src/migration/detect.ts","../src/migration/log.ts","../src/migration/migrate.ts","../src/plugins/resolver.ts","../src/plugins/capabilities.ts","../src/plugins/errors.ts","../src/plugins/manifest.ts","../src/commands/plugin/common.ts","../src/commands/plugin/install.ts","../src/commands/plugin/list.ts","../src/commands/plugin/uninstall.ts","../src/commands/plugin/update.ts","../src/commands/plugin/index.ts","../src/commands/skill/index.ts","../src/commands/task/index.ts","../src/output/diff.ts","../src/util/stdin.ts","../src/util/api-task.ts","../src/util/publish-pod-stream.ts","../src/util/rush-domain.ts","../src/commands/task/push.ts","../src/util/env-file.ts","../src/commands/task/deploy.ts","../src/commands/task/domain.ts","../src/commands/task/link.ts","../src/commands/task/versions.ts","../src/commands/index.ts","../src/util/update-check.ts"],"sourcesContent":["import chalk from 'chalk';\nimport { Command } from 'commander';\nimport { registerCommands } from './commands/index.js';\nimport { output } from './output/logger.js';\nimport { getAuthMethod } from './util/auth.js';\nimport { isCIMode } from './util/ci.js';\nimport { isRushError } from './util/errors.js';\nimport { checkForUpdate } from './util/update-check.js';\nimport { setVerbosity } from './util/verbosity.js';\nimport { VERSION } from './version.js';\n\nconst BANNER = `\n ${chalk.bold.cyan('Rush CLI')} ${chalk.dim(`v${VERSION}`)}\n ${chalk.dim('Call Rush agents from your terminal — relay the context, not the prompt.')}\n\n ${chalk.dim('Use cases:')}\n ${chalk.dim('·')} From Cursor / Claude Code, hand off a task to a Rush agent without restating context.\n ${chalk.dim('·')} Compose workflows where a Rush specialist agent runs as your sub-agent.\n\n ${chalk.dim('For agents:')} ${chalk.cyan('rush-ai skill')} prints ready-to-consume usage playbooks.\n`;\n\nfunction showWelcomeGuide(): void {\n const lines = [\n '',\n ` ${chalk.bold.cyan('Rush CLI')} ${chalk.dim(`v${VERSION}`)}`,\n ` ${chalk.dim('Call Rush agents from your terminal — relay the context, not the prompt.')}`,\n '',\n chalk.dim(' Quick start:'),\n ` ${chalk.cyan('rush-ai auth login')} Log in to the Rush platform`,\n ` ${chalk.cyan('rush-ai agent list')} Browse available agents`,\n ` ${chalk.cyan('rush-ai task create -a web-builder')} Hand a task to a Rush agent`,\n '',\n chalk.dim(' For AI agents:'),\n ` ${chalk.cyan('rush-ai skill')} Print usage playbooks (markdown)`,\n ` ${chalk.cyan('rush-ai skill hand-off')} IDE → Rush task hand-off`,\n ` ${chalk.cyan('rush-ai skill agent-shelf')} Calling a Rush agent as a sub-agent`,\n '',\n ];\n\n try {\n const method = getAuthMethod();\n if (method) {\n lines.push(chalk.dim(' Status:'), ` Logged in via ${method}`, '');\n }\n } catch {\n // Ignore auth errors in welcome guide\n }\n\n lines.push(chalk.dim(' Run rush-ai --help for all commands.'), '');\n console.error(lines.join('\\n'));\n}\n\nexport function createProgram(): Command {\n const program = new Command();\n\n program\n .name('rush-ai')\n .version(VERSION, '-v, --version')\n .description('Rush CLI - Command-line interface for the Rush AI platform')\n .configureHelp({\n sortSubcommands: true,\n })\n .addHelpText('before', BANNER)\n .option('--no-color', 'Disable color output')\n .option('--json', 'Output as JSON where supported')\n .option('-f, --format <type>', 'Output format (table, json, csv)')\n .option(\n '--ci',\n 'CI mode: JSON output, no interactive prompts, strict exit codes'\n )\n .option('--verbose', 'Show detailed output including HTTP requests')\n .option('--debug', 'Show debug-level output (implies --verbose)');\n\n // CI mode: auto-enable --json before any command runs\n // Set verbosity BEFORE any command action runs\n program.hook('preAction', (thisCommand) => {\n const opts = thisCommand.optsWithGlobals();\n if (isCIMode(opts)) {\n if (!opts.json) {\n thisCommand.setOptionValue('json', true);\n }\n }\n setVerbosity(!!opts.verbose, !!opts.debug);\n });\n\n // No-argument interactive guide (Commander handles --help/--version first)\n program.action(() => {\n if (process.stdout.isTTY) {\n showWelcomeGuide();\n } else {\n program.help();\n }\n });\n\n registerCommands(program);\n\n return program;\n}\n\nasync function main(): Promise<void> {\n // Disable color in non-TTY environments, --no-color, or CI mode\n if (\n !process.stdout.isTTY ||\n process.argv.includes('--no-color') ||\n isCIMode()\n ) {\n process.env.NO_COLOR = '1';\n }\n\n const program = createProgram();\n\n try {\n await program.parseAsync(process.argv);\n } catch (error) {\n const ci = isCIMode(program.opts());\n\n if (isRushError(error)) {\n if (ci) {\n output.log(\n JSON.stringify({\n error: true,\n code: error.code,\n message: error.message,\n exitCode: error.exitCode,\n })\n );\n } else {\n output.error(error.message);\n if (error.code === 'AUTH_ERROR') {\n output.newline();\n output.dim('Run `rush-ai auth login` to authenticate.');\n }\n }\n process.exit(error.exitCode);\n }\n\n if (error instanceof Error) {\n const exitCode = 2;\n if (ci) {\n output.log(\n JSON.stringify({\n error: true,\n code: 'UNKNOWN_ERROR',\n message: error.message,\n exitCode,\n })\n );\n } else {\n output.error(error.message);\n if (process.env.DEBUG) {\n console.error(error.stack);\n }\n }\n process.exit(exitCode);\n }\n\n if (ci) {\n output.log(\n JSON.stringify({\n error: true,\n code: 'UNKNOWN_ERROR',\n message: 'An unexpected error occurred.',\n exitCode: 2,\n })\n );\n } else {\n output.error('An unexpected error occurred.');\n }\n process.exit(2);\n }\n\n // Non-blocking version check after command completes\n void checkForUpdate(VERSION);\n}\n\n// Only run when executed as entry point, not when imported by tests\nif (!process.env.VITEST) {\n main();\n}\n","/**\n * RFC 4180 compliant CSV formatter.\n * No external dependencies.\n */\n\nfunction escapeField(value: string): string {\n // If value contains comma, quote, newline, or leading/trailing spaces, wrap in quotes\n if (\n value.includes(',') ||\n value.includes('\"') ||\n value.includes('\\n') ||\n value.includes('\\r') ||\n value !== value.trim()\n ) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n\nexport function formatCsv(rows: Record<string, string>[]): string {\n if (rows.length === 0) return '';\n\n const keys = Object.keys(rows[0]);\n\n // Header row\n const header = keys.map(escapeField).join(',');\n\n // Data rows\n const lines = rows.map((row) =>\n keys.map((k) => escapeField(row[k] ?? '')).join(',')\n );\n\n return [header, ...lines].join('\\n');\n}\n","import chalk from 'chalk';\n\nexport interface TableColumnOptions {\n maxWidth?: number;\n align?: 'left' | 'right';\n}\n\nexport interface TableFormatOptions {\n columns?: Record<string, TableColumnOptions>;\n}\n\nconst STATUS_COLORS: Record<string, (s: string) => string> = {\n completed: chalk.green,\n active: chalk.green,\n installed: chalk.green,\n running: chalk.yellow,\n pending: chalk.yellow,\n available: chalk.dim,\n failed: chalk.red,\n cancelled: chalk.red,\n error: chalk.red,\n};\n\nfunction colorizeStatus(value: string): string {\n const colorFn = STATUS_COLORS[value.toLowerCase()];\n return colorFn ? colorFn(value) : value;\n}\n\nfunction truncate(str: string, max: number): string {\n if (str.length <= max) return str;\n return `${str.slice(0, max - 3)}...`;\n}\n\nexport function formatTableEnhanced(\n rows: Record<string, string>[],\n options?: TableFormatOptions\n): string {\n if (rows.length === 0) return '';\n\n const keys = Object.keys(rows[0]);\n const colOpts = options?.columns ?? {};\n const widths: Record<string, number> = {};\n\n // Calculate column widths\n for (const key of keys) {\n widths[key] = key.length;\n }\n for (const row of rows) {\n for (const key of keys) {\n const val = row[key] ?? '';\n const maxW = colOpts[key]?.maxWidth;\n const display = maxW ? truncate(val, maxW) : val;\n if (display.length > widths[key]) {\n widths[key] = display.length;\n }\n }\n }\n\n // Build header\n const header = keys.map((k) => chalk.bold(k.padEnd(widths[k]))).join(' ');\n const separator = keys.map((k) => '-'.repeat(widths[k])).join(' ');\n\n // Build rows\n const lines = rows.map((row) =>\n keys\n .map((k) => {\n const raw = row[k] ?? '-';\n const display = raw === '' ? '-' : raw;\n const maxW = colOpts[k]?.maxWidth;\n const truncated = maxW ? truncate(display, maxW) : display;\n const align = colOpts[k]?.align ?? 'left';\n const padded =\n align === 'right'\n ? truncated.padStart(widths[k])\n : truncated.padEnd(widths[k]);\n\n // Colorize status-like values\n if (k.toLowerCase() === 'status' || k.toLowerCase() === 'state') {\n return colorizeStatus(padded);\n }\n return padded;\n })\n .join(' ')\n );\n\n return [header, separator, ...lines].join('\\n');\n}\n","import { formatCsv } from './csv.js';\nimport { formatTableEnhanced, type TableFormatOptions } from './table.js';\n\nexport type OutputFormat = 'table' | 'json' | 'csv';\n\nconst VALID_FORMATS: readonly string[] = ['table', 'json', 'csv'];\n\nexport interface FormatOptions extends TableFormatOptions {}\n\n/**\n * Resolve the effective output format from CLI options.\n * Throws if --json and --format conflict, or if format value is invalid.\n */\nexport function resolveFormat(opts: {\n json?: boolean;\n format?: string;\n}): OutputFormat {\n const hasJson = opts.json === true;\n const hasFormat = opts.format != null && opts.format !== undefined;\n\n // Validate format value\n if (hasFormat && !VALID_FORMATS.includes(opts.format as string)) {\n throw new Error(\n `Invalid format: \"${opts.format}\". Supported formats: ${VALID_FORMATS.join(', ')}`\n );\n }\n\n if (hasJson && hasFormat && opts.format !== 'json') {\n throw new Error(\n `Conflicting options: --json cannot be used with --format ${opts.format}. Use one or the other.`\n );\n }\n\n if (hasJson) return 'json';\n if (hasFormat) return opts.format as OutputFormat;\n return 'table';\n}\n\n/**\n * Format row data as a string in the specified format.\n * Note: JSON mode should be handled separately per-command to preserve payload structure.\n */\nexport function formatOutput(\n rows: Record<string, string>[],\n format: OutputFormat,\n options?: FormatOptions\n): string {\n switch (format) {\n case 'csv':\n return formatCsv(rows);\n case 'json':\n return JSON.stringify(rows, null, 2);\n default:\n return formatTableEnhanced(rows, options);\n }\n}\n\nexport { formatCsv } from './csv.js';\nexport type { TableColumnOptions, TableFormatOptions } from './table.js';\n// Re-export for convenience\nexport { formatTableEnhanced } from './table.js';\n","/**\n * CI mode detection utility.\n *\n * CI mode is activated by any of:\n * - `--ci` CLI flag\n * - `CI` environment variable (set by GitHub Actions, GitLab CI, etc.)\n * - `RUSH_CI` environment variable (explicit opt-in)\n */\n\n/**\n * Strict env-var truthy check.\n * Returns true only for '1', 'true', 'yes' (case-insensitive).\n */\nfunction isEnvTruthy(value: string | undefined): boolean {\n if (!value) return false;\n return ['1', 'true', 'yes'].includes(value.trim().toLowerCase());\n}\n\n/**\n * Detect CI mode from CLI flag or environment variables.\n *\n * The `process.argv` check runs unconditionally as a reliable fallback\n * that works even before Commander parses (e.g. in early error paths).\n */\nexport function isCIMode(programOpts?: { ci?: boolean }): boolean {\n if (process.argv.includes('--ci')) return true;\n if (programOpts?.ci) return true;\n if (isEnvTruthy(process.env.CI)) return true;\n if (isEnvTruthy(process.env.RUSH_CI)) return true;\n return false;\n}\n","import { output } from '../output/logger.js';\nimport { getAuthToken } from './auth.js';\nimport { isCIMode } from './ci.js';\nimport { checkAuthSourceMismatch, getAuthConfig } from './config.js';\nimport { AuthError } from './errors.js';\n\n/**\n * Guard that throws AuthError if not authenticated.\n * Checks RUSH_API_KEY env and stored CAS token (including expiry).\n *\n * Also surfaces a soft warning when the stored token's origin URL doesn't\n * match the profile's current API URL (see #832 — silent 401s after\n * `config set api` were a common way users lost their session without a\n * clear next-step).\n */\nexport function requireAuth(): void {\n const token = getAuthToken();\n if (!token) {\n throw new AuthError();\n }\n // Check expiry for stored tokens (env API keys never expire client-side)\n if (!process.env.RUSH_API_KEY) {\n const auth = getAuthConfig();\n if (auth.expiresAt && Date.now() > auth.expiresAt) {\n // CAS tokens with refresh_token can still be renewed lazily\n if (auth.method === 'cas' && auth.refreshToken) {\n return;\n }\n throw new AuthError(\n 'Token expired. Run `rush-ai auth login` to re-authenticate.'\n );\n }\n\n // Non-fatal URL mismatch warning — the token might still work, but this\n // is the single most common reason users see 401s without a clear hint.\n // Suppress in CI and JSON modes: the warning on every invocation turns\n // into pipe-log noise that automation has no way to act on. Interactive\n // users keep getting the guidance; scripted users can opt in via\n // `auth status` or `doctor` when they want to verify the binding.\n if (!isCIMode() && !isJsonMode()) {\n const mismatch = checkAuthSourceMismatch();\n if (mismatch) {\n output.warn(\n `Token was issued against ${mismatch.stored} but the current API is ${mismatch.current}.`\n );\n output.dim('If requests start failing, run `rush-ai auth login`.');\n }\n }\n }\n}\n\nfunction isJsonMode(): boolean {\n // Cheap argv scan — requireAuth runs before Commander parses in some\n // paths, so we can't rely on program.opts().json here.\n return process.argv.includes('--json');\n}\n","import type { Command } from 'commander';\nimport { formatOutput, resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport { createClient } from '../../util/client.js';\nimport { ApiError } from '../../util/errors.js';\nimport { requireAuth } from '../../util/require-auth.js';\n\n/** Matches DbAgent from the real API */\ninterface ApiAgent {\n id: string;\n name: string;\n description: string | null;\n capabilities: string[] | null;\n skills: string[] | null;\n mcp_servers: unknown[] | null;\n status: string;\n visibility: string;\n provider_type: string;\n provider_model: string | null;\n system_prompt: string | null;\n created_at: string;\n updated_at: string;\n created_by_id: string | null;\n is_builtin: boolean;\n}\n\ninterface AgentListResponse {\n agents: ApiAgent[];\n pagination: {\n total: number;\n page: number;\n limit: number;\n totalPages: number;\n };\n}\n\ninterface AgentDetailResponse {\n agent: ApiAgent;\n}\n\ninterface AgentCandidateItem {\n id: string;\n name: string;\n status: string;\n visibility: string;\n created_at: string;\n}\n\ninterface AgentCandidatesBody {\n error: string;\n candidates: AgentCandidateItem[];\n}\n\nconst AGENT_HELP_AFTER = `\nWhen to use:\n Rush hosts a shelf of specialist agents — call them as sub-agents from\n your own workflows instead of writing the prompts yourself. Always\n discover first, then invoke.\n\nTypical flows:\n $ rush-ai agent list --default --json # just the built-in defaults\n $ rush-ai agent list --search \"人力\" --json # fuzzy-filter by name / desc\n $ rush-ai agent list --all --json # everything — may be long\n $ rush-ai agent info <name> --json # inspect one agent's skills / MCP\n\n $ rush-ai task create -a <name> -p \"...\" # call the agent as a sub-agent\n $ rush-ai task status <id> --json # grab its output\n\nBuilt-in default agents (see --default):\n rush general-purpose agent\n web-builder site / landing-page builder (returns previewUrl + gitRepoUrl)\n skill-publisher publishes reusable skills back onto the platform\n\nFor agents: run \\`rush-ai skill agent-shelf\\` for the full playbook.\n`;\n\nexport function registerAgentCommand(program: Command): void {\n const agent = program\n .command('agent')\n .description('Manage and inspect agents')\n .addHelpText('after', AGENT_HELP_AFTER);\n\n agent\n .command('list')\n .alias('ls')\n .description('List available agents')\n .option(\n '-l, --limit <n>',\n 'Max agents per page (1–500, default 50)',\n (v) => Number.parseInt(v, 10),\n 50\n )\n .option(\n '-p, --page <n>',\n 'Page number (1-indexed, default 1)',\n (v) => Number.parseInt(v, 10),\n 1\n )\n .option('-q, --search <text>', 'Filter agents by name / description')\n .option(\n '--default',\n 'Only show built-in default agents (web-builder, rush, skill-publisher, ...)'\n )\n .option(\n '--all',\n 'Fetch every page and return the full list (may issue several requests)'\n )\n .action(\n async (opts: {\n limit: number;\n page: number;\n search?: string;\n default?: boolean;\n all?: boolean;\n }) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n const baseParams = new URLSearchParams();\n const rawLimit = Number.isFinite(opts.limit) ? opts.limit : 50;\n const apiLimit = Math.min(Math.max(rawLimit, 1), 500);\n const rawPage = Number.isFinite(opts.page) ? opts.page : 1;\n const requestedPage = Math.max(rawPage, 1);\n if (opts.search) baseParams.set('q', opts.search);\n if (opts.default) baseParams.set('is_builtin', 'true');\n\n async function fetchPage(\n page: number,\n limit: number\n ): Promise<AgentListResponse> {\n const params = new URLSearchParams(baseParams);\n params.set('page', String(page));\n params.set('limit', String(limit));\n const { data } = await client.get<AgentListResponse>(\n `/api/agents?${params.toString()}`\n );\n return data;\n }\n\n let data: AgentListResponse;\n if (opts.all) {\n // Pull every page so the caller really sees the whole shelf.\n // API caps `limit` at 500, so for realistic shelf sizes this is 1 request.\n const first = await fetchPage(1, 500);\n const totalPages = first.pagination?.totalPages ?? 1;\n const all = [...first.agents];\n for (let p = 2; p <= totalPages; p++) {\n const next = await fetchPage(p, 500);\n all.push(...next.agents);\n }\n data = {\n agents: all,\n pagination: first.pagination\n ? { ...first.pagination, page: 1, limit: all.length }\n : {\n total: all.length,\n page: 1,\n limit: all.length,\n totalPages: 1,\n },\n };\n } else {\n data = await fetchPage(requestedPage, apiLimit);\n }\n\n if (format === 'json') {\n output.log(JSON.stringify(data, null, 2));\n return;\n }\n\n if (data.agents.length === 0) {\n output.info('No agents found.');\n return;\n }\n\n const total = data.pagination?.total ?? data.agents.length;\n const shown = data.agents.length;\n const page = data.pagination?.page ?? 1;\n const totalPages = data.pagination?.totalPages ?? 1;\n\n output.log(output.bold(`Agents (showing ${shown} of ${total}):`));\n output.newline();\n\n const rows = data.agents.map((a) => ({\n Name: a.name,\n Description: truncate(a.description ?? '', 50),\n Status: a.status,\n Skills: String(a.skills?.length ?? 0),\n MCP: String(a.mcp_servers?.length ?? 0),\n }));\n\n output.log(\n formatOutput(rows, format, {\n columns: { Description: { maxWidth: 50 } },\n })\n );\n\n if (!opts.all && shown < total) {\n output.newline();\n output.dim(\n `Page ${page}/${totalPages}. Use --page <n>, --limit <n>, --search <text>, or --all to see more.`\n );\n }\n }\n );\n\n agent\n .command('info')\n .description('Show agent details')\n .argument('<name>', 'Agent name or ID')\n .action(async (nameOrId: string) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n let data: AgentDetailResponse;\n try {\n ({ data } = await client.get<AgentDetailResponse>(\n `/api/agents/${encodeURIComponent(nameOrId)}`\n ));\n } catch (err: unknown) {\n if (err instanceof ApiError && err.status === 409) {\n let parsed: AgentCandidatesBody | undefined;\n try {\n parsed = JSON.parse(err.meta.body as string);\n } catch {\n // body 不是合法 JSON,走默认错误处理\n }\n\n // 运行时守卫:确保 candidates 是合法数组且每项有必要字段\n const candidates = parsed?.candidates;\n const isValid =\n Array.isArray(candidates) &&\n candidates.length > 0 &&\n candidates.every(\n (c) => typeof c.id === 'string' && typeof c.name === 'string'\n );\n\n if (isValid) {\n if (format === 'json') {\n output.log(JSON.stringify(parsed, null, 2));\n process.exitCode = 1;\n return;\n }\n\n output.error(`Multiple agents found with name \"${nameOrId}\":`);\n output.newline();\n for (const c of candidates!) {\n const date = c.created_at?.split('T')[0] ?? '';\n output.dim(` ${c.id} ${c.name} ${c.status ?? ''} ${date}`);\n }\n output.newline();\n output.info(`Use agent ID to specify: rush agent info <id>`);\n process.exitCode = 1;\n return;\n }\n }\n throw err;\n }\n\n const a = data.agent;\n\n if (format === 'json') {\n output.log(JSON.stringify(a, null, 2));\n return;\n }\n\n output.log(output.bold(a.name));\n output.log(` ID: ${a.id}`);\n output.log(` Description: ${a.description ?? '(none)'}`);\n output.log(` Status: ${a.status}`);\n output.log(` Visibility: ${a.visibility}`);\n output.log(\n ` Provider: ${a.provider_type}${a.provider_model ? ` (${a.provider_model})` : ''}`\n );\n output.log(` Capabilities: ${a.capabilities?.join(', ') || 'none'}`);\n output.newline();\n\n if (a.skills && a.skills.length > 0) {\n output.log(output.bold(' Skills:'));\n for (const skill of a.skills) {\n output.log(` - ${skill}`);\n }\n output.newline();\n }\n\n if (a.mcp_servers && a.mcp_servers.length > 0) {\n output.log(output.bold(` MCP Servers (${a.mcp_servers.length}):`));\n for (const mcp of a.mcp_servers) {\n if (typeof mcp === 'object' && mcp !== null) {\n const m = mcp as Record<string, unknown>;\n const name = String(m.serverName ?? m.name ?? 'unknown');\n const disabled = m.disabled ? ' (disabled)' : '';\n output.log(` - ${name}${disabled}`);\n } else {\n output.log(` - ${JSON.stringify(mcp)}`);\n }\n }\n output.newline();\n }\n\n output.dim(` Updated: ${new Date(a.updated_at).toLocaleString()}`);\n });\n}\n\nfunction truncate(str: string, max: number): string {\n if (str.length <= max) return str;\n return `${str.slice(0, max - 3)}...`;\n}\n","import { randomBytes } from 'node:crypto';\nimport { createServer } from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport open from 'open';\nimport { output } from '../../output/logger.js';\nimport { getGlobalConfig, setAuthConfig } from '../../util/config.js';\nimport { AuthError } from '../../util/errors.js';\n\ninterface TokenResponse {\n token: string;\n token_id: string;\n expires_at: string;\n}\n\nfunction getApiBaseUrl(): string {\n return process.env.RUSH_API_URL ?? getGlobalConfig().api;\n}\n\nexport async function loginViaBrowser(jsonMode?: boolean): Promise<void> {\n const baseUrl = getApiBaseUrl();\n const state = randomBytes(16).toString('hex');\n\n return new Promise<void>((resolve, reject) => {\n const server = createServer(async (req, res) => {\n try {\n const url = new URL(req.url ?? '/', 'http://localhost');\n\n if (url.pathname !== '/callback') {\n res.writeHead(404);\n res.end('Not found');\n return;\n }\n\n const code = url.searchParams.get('code');\n const returnedState = url.searchParams.get('state');\n const error = url.searchParams.get('error');\n\n if (error) {\n const desc = url.searchParams.get('error_description') ?? '认证失败';\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(\n getHtmlPage(\n '认证失败',\n `错误:${desc}。你可以关闭此页面。`,\n baseUrl\n )\n );\n server.close();\n reject(new AuthError(`Browser authentication failed: ${desc}`));\n return;\n }\n\n if (!code || returnedState !== state) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(\n getHtmlPage(\n '认证失败',\n '回调参数无效,请重新登录。你可以关闭此页面。',\n baseUrl\n )\n );\n server.close();\n reject(\n new AuthError('Invalid callback: missing code or state mismatch')\n );\n return;\n }\n\n // Exchange code for Platform Token\n const tokenUrl = `${baseUrl}/api/cli/auth/token`;\n const tokenResponse = await fetch(tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ code, state }),\n });\n\n if (!tokenResponse.ok) {\n const errorText = await tokenResponse\n .text()\n .catch(() => 'Unknown error');\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(\n getHtmlPage(\n '认证失败',\n 'Token 交换失败,请重试。你可以关闭此页面。',\n baseUrl\n )\n );\n server.close();\n reject(new AuthError(`Token exchange failed: ${errorText}`));\n return;\n }\n\n const tokenData = (await tokenResponse.json()) as TokenResponse;\n\n const expiresAt = tokenData.expires_at\n ? new Date(tokenData.expires_at).getTime()\n : null;\n\n setAuthConfig({\n token: tokenData.token,\n expiresAt,\n refreshToken: null,\n method: 'platform_token',\n tokenId: tokenData.token_id,\n sourceUrl: baseUrl,\n });\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(\n getHtmlPage(\n '认证成功',\n '你已成功登录,可以关闭此页面并返回终端。',\n baseUrl,\n expiresAt ?? undefined\n )\n );\n\n server.close();\n\n if (jsonMode) {\n output.log(\n JSON.stringify({\n status: 'authenticated',\n method: 'platform_token',\n expiresAt,\n })\n );\n } else {\n output.success('Logged in successfully.');\n if (expiresAt) {\n output.dim(\n `Token expires: ${new Date(expiresAt).toLocaleString()}`\n );\n }\n }\n\n resolve();\n } catch (err) {\n server.close();\n reject(\n err instanceof AuthError\n ? err\n : new AuthError(\n `Authentication error: ${err instanceof Error ? err.message : 'Unknown'}`\n )\n );\n }\n });\n\n server.listen(0, '127.0.0.1', () => {\n const { port } = server.address() as AddressInfo;\n\n const authUrl = `${baseUrl}/api/cli/auth/authorize?callback_port=${port}&state=${encodeURIComponent(state)}`;\n\n if (!jsonMode) {\n output.log('Opening browser for authentication...');\n output.dim(\"If the browser doesn't open, visit:\");\n output.dim(authUrl);\n output.newline();\n output.dim('Waiting for authentication...');\n }\n\n open(authUrl).catch(() => {\n if (!jsonMode) {\n output.warn(\n 'Could not open browser. Please visit the URL above manually.'\n );\n }\n });\n });\n\n // Timeout after 5 minutes\n const timeoutId = setTimeout(() => {\n server.close();\n reject(new AuthError('Authentication timed out after 5 minutes'));\n }, 300_000);\n\n server.on('close', () => clearTimeout(timeoutId));\n });\n}\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction getHtmlPage(\n title: string,\n message: string,\n serverUrl: string,\n expiresAt?: number\n): string {\n const metaLines: string[] = [\n `<span class=\"label\">服务器</span> <span class=\"value\">${escapeHtml(serverUrl)}</span>`,\n ];\n if (expiresAt) {\n metaLines.push(\n `<span class=\"label\">有效期至</span> <span class=\"value\">${escapeHtml(new Date(expiresAt).toLocaleString('zh-CN'))}</span>`\n );\n }\n const metaHtml = metaLines\n .map((line) => `<div class=\"meta-row\">${line}</div>`)\n .join('\\n ');\n\n return `<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head><meta charset=\"utf-8\"><title>Rush CLI - ${escapeHtml(title)}</title>\n<style>\n body { font-family: -apple-system, BlinkMacSystemFont, \"PingFang SC\", \"Microsoft YaHei\", sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #f5f5f5; }\n .card { background: white; border-radius: 12px; padding: 40px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); text-align: center; max-width: 420px; }\n h1 { color: #333; font-size: 1.5em; margin-bottom: 8px; }\n p { color: #666; line-height: 1.6; }\n .meta { margin-top: 16px; padding-top: 16px; border-top: 1px solid #eee; text-align: left; }\n .meta-row { color: #888; font-size: 0.85em; line-height: 2; }\n .meta-row .label { color: #999; }\n .meta-row .value { color: #555; font-family: \"SF Mono\", Menlo, monospace; }\n</style>\n</head>\n<body>\n <div class=\"card\">\n <h1>${escapeHtml(title)}</h1>\n <p>${escapeHtml(message)}</p>\n <div class=\"meta\">\n ${metaHtml}\n </div>\n </div>\n</body>\n</html>`;\n}\n","import type { Command } from 'commander';\nimport { output } from '../../output/logger.js';\nimport { getAuthMethod } from '../../util/auth.js';\nimport {\n revokeCurrentSession,\n verifyCurrentAuthSession,\n} from '../../util/auth-session.js';\nimport {\n clearAuthConfig,\n getAuthConfig,\n getConfigDir,\n getGlobalConfig,\n isLoggedIn,\n setAuthConfig,\n} from '../../util/config.js';\nimport { RushError } from '../../util/errors.js';\nimport { loginViaBrowser } from './browser-login.js';\n\nexport function registerAuthCommand(program: Command): void {\n const auth = program.command('auth').description('Manage authentication');\n\n auth\n .command('login')\n .description('Log in to the Rush platform')\n .option('--api-key <key>', 'Authenticate with an API key instead of CAS')\n .option('--no-verify', 'Skip online validation for API key login')\n .action(async (options: { apiKey?: string; verify?: boolean }) => {\n const jsonMode = program.opts().json;\n\n if (options.apiKey) {\n setAuthConfig({\n token: options.apiKey,\n expiresAt: null,\n refreshToken: null,\n method: 'api_key',\n // Bind the API key to whichever URL it was activated against so\n // switching profiles / `config set api` can warn about mismatches.\n sourceUrl: process.env.RUSH_API_URL ?? getGlobalConfig().api,\n });\n\n let verification:\n | Awaited<ReturnType<typeof verifyCurrentAuthSession>>\n | undefined;\n if (options.verify !== false) {\n verification = await verifyCurrentAuthSession();\n if (!verification.valid) {\n clearAuthConfig();\n throw new RushError(\n `API key validation failed: ${verification.message ?? 'invalid credentials'}`,\n {},\n 'AUTH_ERROR'\n );\n }\n }\n\n if (jsonMode) {\n output.log(\n JSON.stringify({\n status: 'authenticated',\n method: 'api_key',\n verified: options.verify !== false,\n ...(verification ? { serverStatus: verification.status } : {}),\n })\n );\n } else {\n output.success('Authenticated with API key.');\n if (options.verify !== false) {\n output.dim('Server validation: OK');\n }\n }\n return;\n }\n\n // CAS PKCE flow\n if (isLoggedIn()) {\n if (jsonMode) {\n output.log(JSON.stringify({ status: 'already_authenticated' }));\n } else {\n output.warn('You are already logged in.');\n output.dim('Run `rush-ai auth logout` first to log out.');\n }\n return;\n }\n\n await loginViaBrowser(jsonMode);\n });\n\n auth\n .command('status')\n .description('Show the current authentication status')\n .option('--verify', 'Validate the current credential against the server')\n .action(async (options: { verify?: boolean }) => {\n const jsonMode = program.opts().json;\n const method = getAuthMethod();\n const authConfig = getAuthConfig();\n // Consider authenticated if: env API key set, OR stored token is valid\n const loggedIn =\n method !== null && (method === 'api_key' || isLoggedIn());\n const verification =\n loggedIn && options.verify ? await verifyCurrentAuthSession() : null;\n\n if (jsonMode) {\n output.log(\n JSON.stringify({\n authenticated: loggedIn,\n method: method ?? null,\n expiresAt: authConfig.expiresAt,\n hasRefreshToken: Boolean(authConfig.refreshToken),\n configDir: getConfigDir(),\n ...(verification\n ? {\n serverAuthenticated: verification.valid,\n serverStatus: verification.status,\n serverMessage: verification.message,\n }\n : {}),\n })\n );\n } else {\n if (loggedIn) {\n output.success(`Authenticated via ${method ?? 'token'}`);\n if (authConfig.expiresAt) {\n const expiresDate = new Date(authConfig.expiresAt).toLocaleString();\n output.dim(`Token expires: ${expiresDate}`);\n }\n if (authConfig.refreshToken) {\n output.dim('Refresh token: available');\n }\n } else {\n output.warn('Not authenticated.');\n output.dim('Run `rush-ai auth login` to log in.');\n }\n if (verification) {\n if (verification.valid) {\n output.dim('Server validation: OK');\n } else {\n output.warn(\n `Server validation failed: ${verification.message ?? 'token rejected'}`\n );\n }\n }\n output.dim(`Config: ${getConfigDir()}`);\n }\n });\n\n auth\n .command('logout')\n .description('Log out and clear stored credentials')\n .option('--no-revoke', 'Skip best-effort CAS revoke request')\n .action(async (options: { revoke?: boolean }) => {\n const jsonMode = program.opts().json;\n const authConfig = getAuthConfig();\n\n const hasToken = authConfig.token !== null;\n if (!hasToken) {\n if (jsonMode) {\n output.log(JSON.stringify({ status: 'not_authenticated' }));\n } else {\n output.warn('You are not currently logged in.');\n }\n return;\n }\n\n const shouldRevoke =\n options.revoke !== false &&\n (authConfig.method === 'cas' || authConfig.method === 'platform_token');\n const revoked = shouldRevoke ? await revokeCurrentSession() : false;\n\n clearAuthConfig();\n if (jsonMode) {\n output.log(\n JSON.stringify({\n status: 'logged_out',\n ...(shouldRevoke ? { revoked } : {}),\n })\n );\n } else {\n output.success('Logged out successfully.');\n if (shouldRevoke) {\n if (revoked) {\n output.dim('Remote token revoked.');\n } else {\n output.warn(\n 'Could not confirm remote revoke. Local credentials were cleared.'\n );\n }\n }\n }\n });\n}\n","import { resolve } from 'node:path';\nimport chalk from 'chalk';\nimport type { Command } from 'commander';\nimport { output } from '../../output/logger.js';\nimport { checkDatabase } from './checks/database.js';\nimport { checkFramework, detectFramework } from './checks/framework.js';\nimport { checkGitignore } from './checks/gitignore.js';\nimport { checkLockFile, detectPackageManager } from './checks/lock-file.js';\nimport { checkPackageJson } from './checks/package-json.js';\nimport { checkPortEnv } from './checks/port-env.js';\nimport type { DeployCheck, DeployCheckReport } from './types.js';\n\nfunction severityIcon(severity: DeployCheck['severity']): string {\n switch (severity) {\n case 'error':\n return chalk.red('✗');\n case 'warning':\n return chalk.yellow('!');\n case 'info':\n return chalk.dim('·');\n }\n}\n\nfunction displayResults(report: DeployCheckReport): void {\n output.newline();\n output.log(chalk.bold(' Rush Deploy Check'));\n output.log(\n chalk.dim(\n ` Framework: ${report.framework} | Package Manager: ${report.packageManager ?? 'unknown'}`\n )\n );\n output.newline();\n\n const grouped = {\n error: report.checks.filter((c) => c.severity === 'error'),\n warning: report.checks.filter((c) => c.severity === 'warning'),\n info: report.checks.filter((c) => c.severity === 'info'),\n };\n\n for (const [severity, checks] of Object.entries(grouped)) {\n if (checks.length === 0) continue;\n for (const check of checks) {\n const icon = severityIcon(check.severity);\n output.log(` ${icon} ${check.message}`);\n if (check.fix && severity !== 'info') {\n output.log(chalk.dim(` → ${check.fix}`));\n }\n }\n }\n\n output.newline();\n\n const { errors, warnings } = report.summary;\n const parts: string[] = [];\n if (errors > 0) parts.push(chalk.red(`${errors} errors`));\n if (warnings > 0) parts.push(chalk.yellow(`${warnings} warnings`));\n if (errors === 0 && warnings === 0)\n parts.push(chalk.green('All checks passed'));\n output.log(` ${parts.join(', ')}`);\n\n if (report.deployable) {\n output.newline();\n output.log(\n chalk.green(' Ready to push! Run `rush-ai task push` to proceed.')\n );\n } else {\n output.newline();\n output.log(chalk.red(' Fix the errors above before deploying.'));\n }\n\n output.newline();\n}\n\nexport function runChecks(projectPath: string): DeployCheckReport {\n const framework = detectFramework(projectPath);\n const packageManager = detectPackageManager(projectPath);\n\n const checks: DeployCheck[] = [\n ...checkPackageJson(projectPath),\n ...checkFramework(projectPath),\n ...checkLockFile(projectPath),\n ...checkPortEnv(projectPath, framework),\n ...checkGitignore(projectPath),\n ];\n\n const { checks: dbChecks, info: dbInfo } = checkDatabase(\n projectPath,\n framework\n );\n checks.push(...dbChecks);\n\n const errors = checks.filter((c) => c.severity === 'error').length;\n const warnings = checks.filter((c) => c.severity === 'warning').length;\n const info = checks.filter((c) => c.severity === 'info').length;\n\n return {\n framework,\n packageManager,\n database: dbInfo,\n checks,\n summary: { errors, warnings, info },\n deployable: errors === 0,\n };\n}\n\nexport function registerCheckCommand(program: Command): void {\n program\n .command('check')\n .description('Check if the current project is ready for Rush deployment')\n .option('-p, --path <dir>', 'Project directory to check', '.')\n .action(async (opts: { path: string }) => {\n const projectPath = resolve(opts.path);\n const jsonMode = program.opts().json;\n\n const report = runChecks(projectPath);\n\n if (jsonMode) {\n output.log(JSON.stringify(report, null, 2));\n } else {\n displayResults(report);\n }\n\n if (!report.deployable) {\n process.exit(1);\n }\n });\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { DatabaseInfo, DeployCheck } from '../types.js';\nimport type { FrameworkType } from './framework.js';\n\nconst PLACEHOLDER_PATTERNS = [\n 'your-',\n 'placeholder',\n 'xxx',\n 'example',\n '<',\n '>',\n '${',\n 'todo',\n];\n\nfunction isPlaceholder(value: string): boolean {\n const lower = value.toLowerCase();\n return PLACEHOLDER_PATTERNS.some((p) => lower.includes(p));\n}\n\nfunction parseEnvFile(filePath: string): Record<string, string> {\n if (!existsSync(filePath)) return {};\n try {\n const content = readFileSync(filePath, 'utf-8');\n const vars: Record<string, string> = {};\n for (const line of content.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const match = trimmed.match(/^([^=]+)=(.*)$/);\n if (match) vars[match[1]] = match[2];\n }\n return vars;\n } catch {\n return {};\n }\n}\n\nexport function checkDatabase(\n projectPath: string,\n framework: FrameworkType\n): { checks: DeployCheck[]; info: DatabaseInfo } {\n const defaultInfo: DatabaseInfo = {\n detected: false,\n type: null,\n autoCreate: false,\n };\n\n if (framework !== 'nextjs') {\n return {\n checks: [],\n info: defaultInfo,\n };\n }\n\n const checks: DeployCheck[] = [];\n const envVars = parseEnvFile(join(projectPath, '.env.local'));\n\n const postgresUrl = envVars.USER_POSTGRESQL_URL;\n if (\n postgresUrl &&\n !isPlaceholder(postgresUrl) &&\n (postgresUrl.startsWith('postgresql://') ||\n postgresUrl.startsWith('postgres://'))\n ) {\n checks.push({\n name: 'database_config',\n severity: 'info',\n message:\n 'Valid PostgreSQL config found in .env.local — Rush will use your existing database',\n });\n return {\n checks,\n info: { detected: true, type: 'postgresql', autoCreate: false },\n };\n }\n\n const supabaseUrl = envVars.SUPABASE_URL;\n const supabaseKey = envVars.SUPABASE_SERVICE_ROLE_KEY || envVars.SUPABASE_KEY;\n if (\n supabaseUrl &&\n supabaseKey &&\n !isPlaceholder(supabaseUrl) &&\n !isPlaceholder(supabaseKey) &&\n supabaseUrl.startsWith('https://') &&\n supabaseKey.length > 20\n ) {\n checks.push({\n name: 'database_config',\n severity: 'info',\n message:\n 'Valid Supabase config found in .env.local — Rush will use your existing database',\n });\n return {\n checks,\n info: { detected: true, type: 'supabase', autoCreate: false },\n };\n }\n\n // Next.js project without valid database config → Rush will auto-create\n const hasPlaceholder =\n (postgresUrl && isPlaceholder(postgresUrl)) ||\n (supabaseUrl && isPlaceholder(supabaseUrl));\n\n if (hasPlaceholder) {\n checks.push({\n name: 'database_config',\n severity: 'warning',\n message:\n 'Database env vars in .env.local contain placeholder values — Rush will auto-create a Supabase database and overwrite them',\n fix: 'Either set real database credentials or remove the placeholder values',\n });\n } else {\n checks.push({\n name: 'database_config',\n severity: 'info',\n message:\n 'No database config detected — Rush will auto-create a Supabase database for this Next.js project',\n });\n }\n\n return {\n checks,\n info: { detected: false, type: null, autoCreate: true },\n };\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { DeployCheck } from '../types.js';\n\nexport type FrameworkType = 'nextjs' | 'vite' | 'unknown';\n\nconst NEXT_CONFIG_FILES = [\n 'next.config.js',\n 'next.config.ts',\n 'next.config.mjs',\n];\n\nconst VITE_CONFIG_FILES = ['vite.config.ts', 'vite.config.js'];\n\nexport function detectFramework(projectPath: string): FrameworkType {\n const pkgPath = join(projectPath, 'package.json');\n if (!existsSync(pkgPath)) return 'unknown';\n\n try {\n const raw = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n const deps = { ...raw.dependencies, ...raw.devDependencies };\n if (deps.next) return 'nextjs';\n if (\n deps.vite ||\n VITE_CONFIG_FILES.some((f) => existsSync(join(projectPath, f)))\n ) {\n return 'vite';\n }\n return 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\nexport function checkFramework(projectPath: string): DeployCheck[] {\n const checks: DeployCheck[] = [];\n const framework = detectFramework(projectPath);\n\n checks.push({\n name: 'framework_detected',\n severity: 'info',\n message: `Detected framework: ${framework}`,\n });\n\n if (framework === 'nextjs') {\n const hasConfig = NEXT_CONFIG_FILES.some((f) =>\n existsSync(join(projectPath, f))\n );\n if (!hasConfig) {\n checks.push({\n name: 'config_file',\n severity: 'warning',\n message: 'No next.config.js/ts/mjs found',\n fix: 'Create a next.config.js file',\n });\n }\n } else if (framework === 'vite') {\n const hasConfig = VITE_CONFIG_FILES.some((f) =>\n existsSync(join(projectPath, f))\n );\n if (hasConfig) {\n checks.push({\n name: 'config_file',\n severity: 'info',\n message: 'Vite config file found',\n });\n }\n\n if (existsSync(join(projectPath, 'index.html'))) {\n checks.push({\n name: 'index_html',\n severity: 'info',\n message: 'index.html found (Vite entry point)',\n });\n } else {\n checks.push({\n name: 'index_html',\n severity: 'warning',\n message:\n 'index.html not found — Vite projects typically need this as entry point',\n fix: 'Create an index.html at the project root',\n });\n }\n }\n\n return checks;\n}\n","/**\n * Gitignore check: `.env.local` 等敏感文件必须被 git 忽略,且不能已经被 tracked。\n *\n * Deploy 时 rush-ai 会对工作区跑 `git add -A` 然后 `git push ... --force`\n * 到 Rush 的 GitLab 仓库。一旦 dashboard 密码、service-role key 被带上去,\n * 就等于泄漏给仓库成员。\n *\n * 两类不安全状态:\n * 1. 文件存在但 `.gitignore` 没覆盖 → push 时被 `add -A` 带走\n * 2. 文件已被 git tracking(历史上 commit 过)→ 就算 `.gitignore` 现在\n * 覆盖了,已 tracked 的文件 push 照样带走\n *\n * 解法:用 git 自身的 `check-ignore` + `ls-files --error-unmatch` 判断,\n * 比解析 .gitignore 文本更可靠(支持负向模式、全局 ignore、.git/info/exclude)。\n *\n * 非 git 仓库场景:rush-ai deploy 会 `git init`;此时检查退化为只看\n * 文件是否存在 + .gitignore 文本匹配。\n */\n\nimport { execFileSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n isFileIgnoredByGit,\n isFileTrackedByGit,\n isGitRepo,\n} from '../../../util/git.js';\nimport type { DeployCheck } from '../types.js';\n\nconst SENSITIVE_ENV_FILES = ['.env.local', '.env.production.local'];\n\nfunction isGitAvailable(): boolean {\n try {\n execFileSync('git', ['--version'], { stdio: 'pipe' });\n return true;\n } catch {\n return false;\n }\n}\n\nfunction readGitignorePatterns(projectPath: string): string[] {\n const gitignorePath = join(projectPath, '.gitignore');\n if (!existsSync(gitignorePath)) return [];\n try {\n return readFileSync(gitignorePath, 'utf-8')\n .split('\\n')\n .map((l) => l.trim())\n .filter((l) => l && !l.startsWith('#'));\n } catch {\n return [];\n }\n}\n\n/**\n * 非 git 仓库时的 fallback:只看 .gitignore 文本,不能检测 tracked 状态。\n * 覆盖常见 pattern(完全匹配 / *前缀 / 后缀 / 单 * glob),足以覆盖 99% 场景。\n */\nfunction matchesGitignoreText(filename: string, patterns: string[]): boolean {\n for (const raw of patterns) {\n if (raw.startsWith('!')) continue; // 负向模式保守不处理\n const p = raw.replace(/^\\/+/, '').replace(/\\/+$/, '');\n if (!p) continue;\n\n if (p === filename) return true;\n if (p === `*${filename}`) return true;\n\n if (p.includes('*')) {\n const regex = new RegExp(\n `^${p.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&').replace(/\\*/g, '.*')}$`\n );\n if (regex.test(filename)) return true;\n }\n }\n return false;\n}\n\nexport function checkGitignore(projectPath: string): DeployCheck[] {\n const checks: DeployCheck[] = [];\n\n const envFilesPresent = SENSITIVE_ENV_FILES.filter((f) =>\n existsSync(join(projectPath, f))\n );\n if (envFilesPresent.length === 0) {\n return checks;\n }\n\n const gitRepo = isGitRepo(projectPath) && isGitAvailable();\n\n if (!gitRepo) {\n // 非 git 仓库(或 git 不可用):回退到 .gitignore 文本匹配\n const patterns = readGitignorePatterns(projectPath);\n const hasGitignore = existsSync(join(projectPath, '.gitignore'));\n const unignored = envFilesPresent.filter(\n (f) => !matchesGitignoreText(f, patterns)\n );\n if (unignored.length === 0) return checks;\n\n const joined = unignored.join(', ');\n const fix = hasGitignore\n ? `Add \"${unignored.join('\" and \"')}\" to .gitignore`\n : `Create a .gitignore with \"${unignored.join('\\n')}\" to exclude sensitive files`;\n checks.push({\n name: 'gitignore_env_local',\n severity: 'error',\n message: hasGitignore\n ? `${joined} is not ignored by .gitignore — secrets would be pushed to GitLab on deploy`\n : `Found ${joined} but no .gitignore — secrets would be pushed to GitLab on deploy`,\n fix,\n });\n return checks;\n }\n\n // Git 仓库:既要 git check-ignore 覆盖,也要确保没有被 tracked。\n const trackedSecrets = envFilesPresent.filter((f) =>\n isFileTrackedByGit(projectPath, f)\n );\n if (trackedSecrets.length > 0) {\n const joined = trackedSecrets.join(', ');\n checks.push({\n name: 'gitignore_env_local_tracked',\n severity: 'error',\n message: `${joined} is tracked by git — even if .gitignore covers it, push will still leak the secrets`,\n fix: `Remove from git but keep locally: \\`git rm --cached ${trackedSecrets.join(' ')}\\` then commit`,\n });\n }\n\n const unignored = envFilesPresent.filter(\n (f) => !trackedSecrets.includes(f) && !isFileIgnoredByGit(projectPath, f)\n );\n if (unignored.length > 0) {\n const joined = unignored.join(', ');\n checks.push({\n name: 'gitignore_env_local',\n severity: 'error',\n message: `${joined} is not ignored by git — secrets would be pushed to GitLab on deploy`,\n fix: `Add \"${unignored.join('\" and \"')}\" to .gitignore`,\n });\n }\n\n return checks;\n}\n","import { execFileSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport function isGitRepo(projectPath: string): boolean {\n return existsSync(join(projectPath, '.git'));\n}\n\nexport function gitInit(projectPath: string): void {\n execFileSync('git', ['init'], { cwd: projectPath, stdio: 'pipe' });\n}\n\nexport function hasUncommittedChanges(projectPath: string): boolean {\n try {\n const status = execFileSync('git', ['status', '--porcelain'], {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n return status.trim().length > 0;\n } catch {\n return false;\n }\n}\n\nexport function gitAddAndCommit(projectPath: string, message: string): void {\n execFileSync('git', ['add', '-A'], { cwd: projectPath, stdio: 'pipe' });\n execFileSync('git', ['commit', '-m', message, '--allow-empty'], {\n cwd: projectPath,\n stdio: 'pipe',\n env: {\n ...process.env,\n GIT_AUTHOR_NAME: process.env.GIT_AUTHOR_NAME || 'rush-ai',\n GIT_AUTHOR_EMAIL: process.env.GIT_AUTHOR_EMAIL || 'rush-ai@rush.dev',\n GIT_COMMITTER_NAME: process.env.GIT_COMMITTER_NAME || 'rush-ai',\n GIT_COMMITTER_EMAIL:\n process.env.GIT_COMMITTER_EMAIL || 'rush-ai@rush.dev',\n },\n });\n}\n\nexport function setRemote(\n projectPath: string,\n remoteName: string,\n url: string\n): void {\n try {\n const remotes = execFileSync('git', ['remote'], {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n if (remotes.split('\\n').includes(remoteName)) {\n execFileSync('git', ['remote', 'set-url', remoteName, url], {\n cwd: projectPath,\n stdio: 'pipe',\n });\n } else {\n execFileSync('git', ['remote', 'add', remoteName, url], {\n cwd: projectPath,\n stdio: 'pipe',\n });\n }\n } catch {\n execFileSync('git', ['remote', 'add', remoteName, url], {\n cwd: projectPath,\n stdio: 'pipe',\n });\n }\n}\n\nexport function gitPushUrl(\n projectPath: string,\n url: string\n): { success: boolean; stderr: string } {\n try {\n // Use `HEAD` (no explicit destination) instead of `HEAD:main`.\n //\n // The rush GitLab templates use `master` as the default branch; pushing\n // to `HEAD:main` silently CREATES a new `main` branch on the remote\n // while leaving `master` — the branch the pod actually tracks — at the\n // old commit. `git push` exits 0, the CLI reports success, the pod's\n // `git pull origin master` no-ops, and the user's code never arrives\n // (#1158).\n //\n // Plain `HEAD` (no `:<dst>`) delegates branch resolution to git's\n // default push behaviour: it pushes the current local branch to the\n // remote branch of the *same name*. This is the same shape a user's\n // `git push <url> HEAD` produces — master checkouts push to master,\n // main checkouts push to main, no upstream-tracking config required.\n // `--force` is preserved — the original intent (overwrite pod-side\n // agent commits) is unchanged.\n execFileSync('git', ['push', url, 'HEAD', '--force'], {\n cwd: projectPath,\n stdio: 'pipe',\n timeout: 300_000,\n maxBuffer: 10 * 1024 * 1024,\n env: {\n ...process.env,\n // 失败时不要弹密码 / SSH key 提示,否则在非交互环境(CI / 测试)\n // 会一直 hang 到外层 timeout(300s)才返回。\n GIT_TERMINAL_PROMPT: '0',\n GIT_ASKPASS: 'true',\n SSH_ASKPASS: 'true',\n },\n });\n return { success: true, stderr: '' };\n } catch (err: unknown) {\n const rawStderr =\n err && typeof err === 'object' && 'stderr' in err\n ? String((err as { stderr: Buffer }).stderr)\n : 'unknown error';\n return { success: false, stderr: sanitizeGitOutput(rawStderr) };\n }\n}\n\nexport function sanitizeGitOutput(text: string): string {\n return text.replace(/https?:\\/\\/[^@]*@/g, 'https://***@');\n}\n\n/**\n * Read `remote.origin.url` from the project's git config. Returns `null` when\n * the directory is not a git repo, the remote is unset, or the command fails.\n *\n * Used by `task push` to identify the rush project from a checked-out repo\n * (see #1149) without requiring `.rush/env.md` to be present — the git remote\n * is the authoritative source of \"which rush project does this working tree\n * belong to\".\n */\nexport function getRemoteUrl(\n projectPath: string,\n remoteName = 'origin'\n): string | null {\n try {\n const url = execFileSync(\n 'git',\n ['config', '--get', `remote.${remoteName}.url`],\n { cwd: projectPath, encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] }\n ).trim();\n return url.length > 0 ? url : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract a rush projectId from a git remote URL that points at the rush\n * GitLab namespace. Accepts both HTTPS (with or without an embedded token)\n * and SSH forms:\n *\n * https://gitlab-ee.zhenguanyu.com/rush/dev/<id>[.git]\n * https://user:token@gitlab-ee.zhenguanyu.com/rush/dev/<id>[.git]\n * git@gitlab-ee.zhenguanyu.com:rush/dev/<id>[.git]\n *\n * Returns `null` on no match. projectIds are lowercase alphanumerics — the\n * strict character class guards against accidentally consuming query strings\n * or path suffixes as the id.\n */\nexport function extractRushProjectId(remoteUrl: string): string | null {\n const match = remoteUrl.match(\n /gitlab-ee\\.zhenguanyu\\.com[/:]rush\\/dev\\/([a-z0-9]+)(?:\\.git)?(?:[/?#]|$)/\n );\n return match ? match[1] : null;\n}\n\n/**\n * 判断 git 是否已经在跟踪该文件。\n *\n * 用途:敏感文件(.env.local 之类)有可能以前 commit 过,之后才加进\n * .gitignore;此时 `.gitignore` 文本看似覆盖,但 git push 还是会带走它。\n * 用 git 自身的 `ls-files --error-unmatch` 判断才可靠。\n *\n * @param projectPath 项目根\n * @param relativePath 相对项目根的文件路径\n * @returns true = 已被 git tracking;false = 未 track(或 git 命令失败)\n */\nexport function isFileTrackedByGit(\n projectPath: string,\n relativePath: string\n): boolean {\n try {\n execFileSync('git', ['ls-files', '--error-unmatch', relativePath], {\n cwd: projectPath,\n stdio: 'pipe',\n });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * 用 git 自身判断路径是否会被 gitignore 规则忽略。\n *\n * `git check-ignore` 原生理解 .gitignore / .git/info/exclude / 全局 gitignore /\n * 负向模式、glob 等,比解析 .gitignore 文本更可靠。\n *\n * @returns true = git 会忽略(即推送时不会带上);false = 不忽略或判断失败\n */\nexport function isFileIgnoredByGit(\n projectPath: string,\n relativePath: string\n): boolean {\n try {\n execFileSync('git', ['check-ignore', '--quiet', relativePath], {\n cwd: projectPath,\n stdio: 'pipe',\n });\n // exit 0 = ignored\n return true;\n } catch {\n // exit 1 = not ignored;exit 128 = fatal(比如非 git 仓库)\n return false;\n }\n}\n","import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { DeployCheck } from '../types.js';\n\nexport type PackageManagerType = 'pnpm' | 'yarn' | 'npm' | null;\n\nexport function detectPackageManager(projectPath: string): PackageManagerType {\n if (existsSync(join(projectPath, 'pnpm-lock.yaml'))) return 'pnpm';\n if (existsSync(join(projectPath, 'yarn.lock'))) return 'yarn';\n if (existsSync(join(projectPath, 'package-lock.json'))) return 'npm';\n return null;\n}\n\nexport function checkLockFile(projectPath: string): DeployCheck[] {\n const checks: DeployCheck[] = [];\n const pm = detectPackageManager(projectPath);\n\n if (pm) {\n checks.push({\n name: 'lock_file_exists',\n severity: 'info',\n message: `Package manager: ${pm}`,\n });\n } else {\n checks.push({\n name: 'lock_file_exists',\n severity: 'warning',\n message:\n 'No lock file found (pnpm-lock.yaml, yarn.lock, or package-lock.json)',\n fix: 'Run `npm install` to generate package-lock.json',\n autoFixable: true,\n });\n }\n\n return checks;\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { DeployCheck } from '../types.js';\n\nexport interface PackageJsonInfo {\n exists: boolean;\n scripts?: Record<string, string>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\nexport function readPackageJson(projectPath: string): PackageJsonInfo {\n const pkgPath = join(projectPath, 'package.json');\n if (!existsSync(pkgPath)) {\n return { exists: false };\n }\n try {\n const raw = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n return {\n exists: true,\n scripts: raw.scripts,\n dependencies: raw.dependencies,\n devDependencies: raw.devDependencies,\n };\n } catch {\n return { exists: false };\n }\n}\n\nexport function checkPackageJson(projectPath: string): DeployCheck[] {\n const checks: DeployCheck[] = [];\n const pkg = readPackageJson(projectPath);\n\n if (!pkg.exists) {\n checks.push({\n name: 'package_json_exists',\n severity: 'error',\n message: 'package.json not found',\n fix: 'Run `npm init -y` to create one',\n });\n return checks;\n }\n\n if (!pkg.scripts?.dev) {\n checks.push({\n name: 'scripts_dev',\n severity: 'error',\n message: 'Missing \"dev\" script in package.json',\n fix: 'Add a dev script (e.g., \"dev\": \"vite\" or \"dev\": \"next dev\")',\n autoFixable: true,\n });\n }\n\n if (!pkg.scripts?.build) {\n checks.push({\n name: 'scripts_build',\n severity: 'warning',\n message:\n 'Missing \"build\" script in package.json — deployment may skip build step',\n fix: 'Add a build script (e.g., \"build\": \"vite build\" or \"build\": \"next build\")',\n autoFixable: true,\n });\n }\n\n if (pkg.scripts?.build) {\n const build = pkg.scripts.build;\n if (/^(?:vue-)?tsc\\s.*&&/.test(build)) {\n checks.push({\n name: 'build_tsc_prefix',\n severity: 'info',\n message: `Build script starts with tsc: \"${build}\" — Rush will auto-strip the tsc check during build`,\n });\n }\n }\n\n return checks;\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { DeployCheck } from '../types.js';\nimport type { FrameworkType } from './framework.js';\n\nconst VITE_CONFIG_FILES = ['vite.config.ts', 'vite.config.js'];\n\nfunction readDevScript(projectPath: string): string | null {\n const pkgPath = join(projectPath, 'package.json');\n if (!existsSync(pkgPath)) return null;\n try {\n const raw = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n return raw.scripts?.dev ?? null;\n } catch {\n return null;\n }\n}\n\nfunction viteConfigReadsPort(projectPath: string): boolean {\n for (const file of VITE_CONFIG_FILES) {\n const filePath = join(projectPath, file);\n if (!existsSync(filePath)) continue;\n try {\n const content = readFileSync(filePath, 'utf-8');\n if (/process\\.env\\.PORT/.test(content)) return true;\n if (/env\\s*\\(\\s*['\"]PORT['\"]\\s*\\)/.test(content)) return true;\n } catch {\n // ignore read errors\n }\n }\n return false;\n}\n\nfunction devScriptReadsPort(devScript: string): boolean {\n if (/\\$PORT|\\$\\{PORT\\}|%PORT%/.test(devScript)) return true;\n if (/--port\\s+\\$/.test(devScript)) return true;\n return false;\n}\n\nexport function checkPortEnv(\n projectPath: string,\n framework: FrameworkType\n): DeployCheck[] {\n const checks: DeployCheck[] = [];\n\n if (framework === 'nextjs') {\n checks.push({\n name: 'port_env',\n severity: 'info',\n message: 'Next.js natively supports PORT env — no config change needed',\n });\n return checks;\n }\n\n if (framework === 'vite') {\n if (viteConfigReadsPort(projectPath)) {\n checks.push({\n name: 'port_env',\n severity: 'info',\n message: 'Vite config reads process.env.PORT',\n });\n return checks;\n }\n\n const devScript = readDevScript(projectPath);\n if (devScript && devScriptReadsPort(devScript)) {\n checks.push({\n name: 'port_env',\n severity: 'info',\n message: 'Dev script references PORT',\n });\n return checks;\n }\n\n checks.push({\n name: 'port_env',\n severity: 'error',\n message:\n 'Vite config does not read process.env.PORT — Rush Pod passes PORT=8000',\n fix: 'Add `server: { port: parseInt(process.env.PORT || \"5173\") }` to vite.config.ts',\n autoFixable: true,\n });\n return checks;\n }\n\n // Unknown framework — check dev script heuristically\n const devScript = readDevScript(projectPath);\n if (devScript && devScriptReadsPort(devScript)) {\n checks.push({\n name: 'port_env',\n severity: 'info',\n message: 'Dev script references PORT',\n });\n } else {\n checks.push({\n name: 'port_env',\n severity: 'error',\n message: 'Dev server must read PORT env var — Rush Pod passes PORT=8000',\n fix: 'Ensure your dev server reads process.env.PORT or accepts --port $PORT',\n autoFixable: false,\n });\n }\n\n return checks;\n}\n","import { existsSync } from 'node:fs';\nimport { appendFile, mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, dirname, join } from 'node:path';\nimport type { Command } from 'commander';\nimport { output } from '../../output/logger.js';\n\nconst MARKER_BEGIN = '###-begin-rush-ai-completion-###';\nconst MARKER_END = '###-end-rush-ai-completion-###';\n\nconst COMPLETION_SCRIPTS: Record<string, string> = {\n bash: `\n${MARKER_BEGIN}\n_rush_cli_completion() {\n local cur=\"\\${COMP_WORDS[COMP_CWORD]}\"\n local prev=\"\\${COMP_WORDS[COMP_CWORD-1]}\"\n local cmds=\"auth agent task mcp plugin completion config doctor\"\n\n case \"$prev\" in\n rush-ai) COMPREPLY=( $(compgen -W \"$cmds\" -- \"$cur\") ) ;;\n auth) COMPREPLY=( $(compgen -W \"login logout status\" -- \"$cur\") ) ;;\n agent) COMPREPLY=( $(compgen -W \"list info\" -- \"$cur\") ) ;;\n task) COMPREPLY=( $(compgen -W \"run create status result list watch cancel\" -- \"$cur\") ) ;;\n plugin) COMPREPLY=( $(compgen -W \"install list status update uninstall\" -- \"$cur\") ) ;;\n completion) COMPREPLY=( $(compgen -W \"install bash zsh fish\" -- \"$cur\") ) ;;\n config) COMPREPLY=( $(compgen -W \"show set use create delete list\" -- \"$cur\") ) ;;\n esac\n}\ncomplete -F _rush_cli_completion rush-ai\n${MARKER_END}`.trimStart(),\n\n zsh: `\n${MARKER_BEGIN}\n_rush_cli_completion() {\n local -a cmds\n cmds=(\n 'auth:Authentication commands'\n 'agent:Agent management'\n 'task:Task operations'\n 'mcp:MCP server'\n 'plugin:Plugin management'\n 'completion:Shell completion'\n 'config:Configuration management'\n 'doctor:System diagnostics'\n )\n _describe 'command' cmds\n}\ncompdef _rush_cli_completion rush-ai\n${MARKER_END}`.trimStart(),\n\n fish: `\n${MARKER_BEGIN}\ncomplete -c rush-ai -n '__fish_use_subcommand' -a auth -d 'Authentication'\ncomplete -c rush-ai -n '__fish_use_subcommand' -a agent -d 'Agent management'\ncomplete -c rush-ai -n '__fish_use_subcommand' -a task -d 'Task operations'\ncomplete -c rush-ai -n '__fish_use_subcommand' -a mcp -d 'MCP server'\ncomplete -c rush-ai -n '__fish_use_subcommand' -a plugin -d 'Plugin management'\ncomplete -c rush-ai -n '__fish_use_subcommand' -a completion -d 'Shell completion'\ncomplete -c rush-ai -n '__fish_use_subcommand' -a config -d 'Configuration management'\ncomplete -c rush-ai -n '__fish_use_subcommand' -a doctor -d 'System diagnostics'\ncomplete -c rush-ai -n '__fish_seen_subcommand_from auth' -a 'login logout status'\ncomplete -c rush-ai -n '__fish_seen_subcommand_from agent' -a 'list info'\ncomplete -c rush-ai -n '__fish_seen_subcommand_from task' -a 'run create status result list watch cancel'\ncomplete -c rush-ai -n '__fish_seen_subcommand_from plugin' -a 'install list status update uninstall'\ncomplete -c rush-ai -n '__fish_seen_subcommand_from completion' -a 'install bash zsh fish'\ncomplete -c rush-ai -n '__fish_seen_subcommand_from config' -a 'show set use create delete list'\n${MARKER_END}`.trimStart(),\n};\n\nfunction detectShell(): string | null {\n const shell = process.env.SHELL;\n if (!shell) return null;\n const name = basename(shell);\n return COMPLETION_SCRIPTS[name] ? name : null;\n}\n\nfunction getShellRcFile(shell: string): string {\n const home = homedir();\n switch (shell) {\n case 'bash':\n return existsSync(join(home, '.bashrc'))\n ? join(home, '.bashrc')\n : join(home, '.bash_profile');\n case 'zsh':\n return join(home, '.zshrc');\n case 'fish':\n return join(home, '.config', 'fish', 'completions', 'rush-ai.fish');\n default:\n throw new Error(`Unsupported shell: ${shell}`);\n }\n}\n\nexport function registerCompletionCommand(program: Command): void {\n const completion = program\n .command('completion')\n .description('Shell completion utilities');\n\n // Print completion script to stdout\n completion\n .command('bash')\n .description('Print bash completion script')\n .action(() => {\n output.log(COMPLETION_SCRIPTS.bash);\n });\n\n completion\n .command('zsh')\n .description('Print zsh completion script')\n .action(() => {\n output.log(COMPLETION_SCRIPTS.zsh);\n });\n\n completion\n .command('fish')\n .description('Print fish completion script')\n .action(() => {\n output.log(COMPLETION_SCRIPTS.fish);\n });\n\n // Auto-install subcommand\n completion\n .command('install')\n .description('Auto-install shell completion to your shell rc file')\n .option('--shell <shell>', 'Shell type (auto-detect if omitted)')\n .action(async (opts: { shell?: string }) => {\n const shell = opts.shell ?? detectShell();\n if (!shell) {\n output.error('Could not detect shell. Use --shell <bash|zsh|fish>.');\n process.exit(1);\n }\n\n if (!COMPLETION_SCRIPTS[shell]) {\n output.error(\n `Unsupported shell: ${shell}. Supported: bash, zsh, fish.`\n );\n process.exit(1);\n }\n\n const script = COMPLETION_SCRIPTS[shell];\n const rcFile = getShellRcFile(shell);\n\n // Fish: write standalone completion file\n if (shell === 'fish') {\n try {\n await mkdir(dirname(rcFile), { recursive: true });\n await writeFile(rcFile, script + '\\n');\n output.success(`Fish completion installed to ${rcFile}`);\n } catch {\n output.error(`Failed to write to ${rcFile}`);\n output.dim(\n ' You can manually add the following to your fish completions:'\n );\n output.log(script);\n }\n return;\n }\n\n // Bash/Zsh: append to rc file (idempotent)\n try {\n const existing = await readFile(rcFile, 'utf-8');\n if (existing.includes(MARKER_BEGIN)) {\n output.info(`Completion already installed in ${rcFile}`);\n return;\n }\n } catch {\n // File doesn't exist — will be created by appendFile\n }\n\n try {\n await appendFile(rcFile, '\\n' + script + '\\n');\n output.success(`Completion installed to ${rcFile}`);\n output.dim(\n ` Run \\`source ${rcFile}\\` or restart your shell to activate.`\n );\n } catch {\n output.error(`Failed to write to ${rcFile}`);\n output.dim(\n ' You can manually add the following to your shell rc file:'\n );\n output.log(script);\n }\n });\n}\n","import type { Command } from 'commander';\nimport { formatTable } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport {\n checkAuthSourceMismatch,\n createProfile,\n deleteProfile,\n getActiveProfile,\n getAuthConfig,\n getConfigDir,\n getGlobalConfig,\n getProfileAuth,\n getProfileConfig,\n listProfiles,\n setActiveProfile,\n setGlobalConfig,\n} from '../../util/config.js';\n\nconst SETTABLE_KEYS = ['api', 'collectMetrics', 'currentTeam'] as const;\ntype SettableKey = (typeof SETTABLE_KEYS)[number];\n\nfunction maskToken(token: string | null): string {\n if (!token) return '(none)';\n if (token.length <= 8) return '****';\n return `${token.slice(0, 4)}...****`;\n}\n\nexport function registerConfigCommand(program: Command): void {\n const config = program\n .command('config')\n .description('Manage CLI configuration and profiles');\n\n config\n .command('show')\n .description('Show current configuration')\n .action(() => {\n const jsonMode = program.opts().json;\n const profile = getActiveProfile();\n const globalConf = getGlobalConfig();\n const authConf = getAuthConfig();\n const configDir = getConfigDir();\n\n const apiOverride = process.env.RUSH_API_URL;\n const profileOverride = process.env.RUSH_PROFILE;\n\n if (jsonMode) {\n output.log(\n JSON.stringify(\n {\n profile,\n api: apiOverride ?? globalConf.api,\n apiOverridden: Boolean(apiOverride),\n profileOverridden: Boolean(profileOverride),\n authMethod: authConf.method,\n collectMetrics: globalConf.collectMetrics,\n currentTeam: globalConf.currentTeam,\n configDir,\n },\n null,\n 2\n )\n );\n return;\n }\n\n output.log(output.bold('Configuration'));\n output.newline();\n\n const profileLabel = profileOverride\n ? `${profile} (overridden by RUSH_PROFILE env)`\n : profile;\n output.log(` Active profile: ${profileLabel}`);\n\n const apiLabel = apiOverride\n ? `${apiOverride} (overridden by RUSH_API_URL env)`\n : globalConf.api;\n output.log(` API: ${apiLabel}`);\n\n output.log(\n ` Auth method: ${authConf.method ?? '(not authenticated)'}`\n );\n output.log(` Token: ${maskToken(authConf.token)}`);\n output.log(\n ` Metrics: ${globalConf.collectMetrics ? 'enabled' : 'disabled'}`\n );\n output.log(` Config dir: ${configDir}`);\n });\n\n config\n .command('set')\n .description('Set a configuration value')\n .argument('<key>', `Config key (${SETTABLE_KEYS.join(', ')})`)\n .argument('<value>', 'Config value')\n .action((key: string, value: string) => {\n const jsonMode = program.opts().json;\n const profile = getActiveProfile();\n\n if (!SETTABLE_KEYS.includes(key as SettableKey)) {\n output.error(\n `Unknown config key '${key}'. Valid keys: ${SETTABLE_KEYS.join(', ')}`\n );\n process.exit(1);\n }\n\n let typedValue: unknown = value;\n\n if (key === 'collectMetrics') {\n if (value !== 'true' && value !== 'false') {\n output.error(\"'collectMetrics' must be 'true' or 'false'.\");\n process.exit(1);\n }\n typedValue = value === 'true';\n }\n\n if (key === 'currentTeam' && value === 'null') {\n typedValue = null;\n }\n\n // Compute the mismatch against the NEW URL BEFORE writing so the\n // comparison uses the same normalization (trailing slash, case) as\n // `requireAuth()`'s own check — see #832. Passing `overrideCurrentUrl`\n // is the simpler path than mutating state and then comparing.\n const mismatch =\n key === 'api' ? checkAuthSourceMismatch(String(typedValue)) : null;\n\n setGlobalConfig({ [key]: typedValue });\n\n if (jsonMode) {\n output.log(JSON.stringify({ key, value: typedValue, profile }));\n } else {\n output.success(\n `Updated '${key}' to '${String(typedValue)}' (profile: ${profile})`\n );\n\n if (mismatch) {\n output.newline();\n output.warn(\n `Stored token was issued against ${mismatch.stored} — it may not work on ${mismatch.current}.`\n );\n output.dim('Run `rush-ai auth login` to re-authenticate.');\n }\n }\n });\n\n config\n .command('use')\n .description('Switch active profile')\n .argument('<profile>', 'Profile name to switch to')\n .action((profileName: string) => {\n const jsonMode = program.opts().json;\n\n try {\n setActiveProfile(profileName);\n } catch (err) {\n output.error(\n err instanceof Error ? err.message : 'Failed to switch profile'\n );\n const available = listProfiles();\n if (!jsonMode) {\n output.dim(`Available profiles: ${available.join(', ')}`);\n output.dim(\n `Run \\`rush-ai config create ${profileName}\\` to create it.`\n );\n }\n process.exit(1);\n }\n\n const globalConf = getGlobalConfig();\n\n if (jsonMode) {\n output.log(\n JSON.stringify({\n profile: profileName,\n api: globalConf.api,\n })\n );\n } else {\n output.success(`Switched to profile '${profileName}'`);\n output.dim(`API: ${globalConf.api}`);\n }\n });\n\n config\n .command('create')\n .description('Create a new profile')\n .argument('<profile>', 'Profile name')\n .option('--api <url>', 'API base URL')\n .action((profileName: string, options: { api?: string }) => {\n const jsonMode = program.opts().json;\n\n try {\n createProfile(\n profileName,\n options.api ? { api: options.api } : undefined\n );\n } catch (err) {\n output.error(\n err instanceof Error ? err.message : 'Failed to create profile'\n );\n process.exit(1);\n }\n\n if (jsonMode) {\n output.log(JSON.stringify({ profile: profileName, created: true }));\n } else {\n output.success(`Created profile '${profileName}'`);\n output.dim(\n `Run \\`rush-ai config use ${profileName}\\` to switch to it.`\n );\n }\n });\n\n config\n .command('delete')\n .description('Delete a profile')\n .argument('<profile>', 'Profile name to delete')\n .action((profileName: string) => {\n const jsonMode = program.opts().json;\n\n try {\n deleteProfile(profileName);\n } catch (err) {\n output.error(\n err instanceof Error ? err.message : 'Failed to delete profile'\n );\n process.exit(1);\n }\n\n if (jsonMode) {\n output.log(JSON.stringify({ profile: profileName, deleted: true }));\n } else {\n output.success(`Deleted profile '${profileName}'`);\n }\n });\n\n config\n .command('list')\n .alias('ls')\n .description('List all profiles')\n .action(() => {\n const jsonMode = program.opts().json;\n const profiles = listProfiles();\n const active = getActiveProfile();\n\n if (jsonMode) {\n const data = profiles.map((name) => {\n const conf = getProfileConfig(name);\n const auth = getProfileAuth(name);\n return {\n name,\n active: name === active,\n api: conf?.api ?? '',\n authMethod: auth.method,\n loggedIn: Boolean(auth.token),\n };\n });\n output.log(JSON.stringify(data, null, 2));\n return;\n }\n\n if (profiles.length === 0) {\n output.info('No profiles configured.');\n return;\n }\n\n const rows = profiles.map((name) => {\n const conf = getProfileConfig(name);\n const auth = getProfileAuth(name);\n const marker = name === active ? '*' : ' ';\n const authStatus = auth.token\n ? `${auth.method ?? 'unknown'} (logged in)`\n : 'not authenticated';\n return {\n ' ': marker,\n Profile: name,\n API: conf?.api ?? '',\n Auth: authStatus,\n };\n });\n\n output.log(formatTable(rows));\n });\n}\n","import chalk from 'chalk';\nimport type { Command } from 'commander';\nimport { output } from '../../output/logger.js';\nimport { checkAuth } from './checks/auth.js';\nimport { checkConfig } from './checks/config.js';\nimport { checkConnectivity } from './checks/connectivity.js';\nimport { checkEnvironment } from './checks/environment.js';\nimport { checkPlugins } from './checks/plugins.js';\nimport type { CheckRunner, DoctorCheck } from './types.js';\n\nconst CHECK_RUNNERS: CheckRunner[] = [\n checkEnvironment,\n checkAuth,\n checkConnectivity,\n checkConfig,\n checkPlugins,\n];\n\nasync function runAllChecks(): Promise<DoctorCheck[]> {\n const checks: DoctorCheck[] = [];\n for (const runner of CHECK_RUNNERS) {\n try {\n const results = await runner();\n checks.push(...results);\n } catch (err) {\n // Isolate runner failures so remaining checks still run\n checks.push({\n name: 'runner_error',\n group: 'Environment',\n status: 'fail',\n label: 'Check error',\n value: `A check group failed: ${err instanceof Error ? err.message : 'unknown error'}`,\n });\n }\n }\n return checks;\n}\n\nfunction getSummary(checks: DoctorCheck[]): {\n pass: number;\n warn: number;\n fail: number;\n} {\n let pass = 0;\n let warn = 0;\n let fail = 0;\n for (const c of checks) {\n if (c.status === 'pass') pass++;\n else if (c.status === 'warn') warn++;\n else if (c.status === 'fail') fail++;\n // info is excluded from summary\n }\n return { pass, warn, fail };\n}\n\nfunction statusIcon(status: DoctorCheck['status']): string {\n switch (status) {\n case 'pass':\n return chalk.green('✓');\n case 'warn':\n return chalk.yellow('!');\n case 'fail':\n return chalk.red('✗');\n case 'info':\n return chalk.dim('·');\n }\n}\n\nconst LABEL_WIDTH = 20;\n\nfunction displayResults(checks: DoctorCheck[]): void {\n // Group checks\n const groups = new Map<string, DoctorCheck[]>();\n for (const check of checks) {\n const existing = groups.get(check.group) ?? [];\n existing.push(check);\n groups.set(check.group, existing);\n }\n\n output.newline();\n\n for (const [groupName, groupChecks] of groups) {\n output.log(` ${chalk.bold(groupName)}`);\n for (const check of groupChecks) {\n const icon = statusIcon(check.status);\n const label = check.label.padEnd(LABEL_WIDTH);\n output.log(` ${icon} ${label} ${check.value}`);\n }\n output.log('');\n }\n\n const summary = getSummary(checks);\n const parts: string[] = [];\n parts.push(chalk.green(`${summary.pass} passed`));\n if (summary.warn > 0) parts.push(chalk.yellow(`${summary.warn} warnings`));\n if (summary.fail > 0) parts.push(chalk.red(`${summary.fail} failed`));\n if (summary.warn === 0 && summary.fail === 0) {\n parts.push('0 warnings');\n parts.push('0 failed');\n }\n output.log(` ${chalk.bold('Summary:')} ${parts.join(', ')}`);\n\n if (summary.fail > 0 || summary.warn > 0) {\n const fixable = checks.filter(\n (c) => c.status !== 'pass' && c.status !== 'info' && c.autoFix\n );\n if (fixable.length > 0) {\n output.log(\n chalk.dim(` Run \\`rush-ai doctor --fix\\` to attempt auto-fixes.`)\n );\n }\n }\n\n output.log('');\n}\n\nfunction outputJson(checks: DoctorCheck[]): void {\n const summary = getSummary(checks);\n const result = {\n checks: checks.map((c) => ({\n name: c.name,\n group: c.group,\n status: c.status,\n label: c.label,\n value: c.value,\n ...(c.fix ? { fix: c.fix } : {}),\n })),\n summary,\n };\n output.log(JSON.stringify(result, null, 2));\n}\n\nasync function runAutoFixes(checks: DoctorCheck[]): Promise<void> {\n const fixable = checks.filter(\n (c) => c.status !== 'pass' && c.status !== 'info' && c.autoFix\n );\n\n if (fixable.length === 0) {\n output.info('No auto-fixable issues found.');\n return;\n }\n\n output.newline();\n for (const check of fixable) {\n output.info(`Fixing: ${check.label}...`);\n try {\n await check.autoFix!();\n output.success(`Fixed: ${check.label}`);\n } catch (err) {\n output.error(\n `Failed to fix ${check.label}: ${err instanceof Error ? err.message : 'unknown error'}`\n );\n }\n }\n output.newline();\n}\n\nexport function registerDoctorCommand(program: Command): void {\n program\n .command('doctor')\n .description('Diagnose environment, auth, and connectivity issues')\n .option('--fix', 'Attempt to auto-fix detected issues')\n .action(async (opts: { fix?: boolean }) => {\n const jsonMode = program.opts().json;\n\n let checks = await runAllChecks();\n\n if (opts.fix) {\n await runAutoFixes(checks);\n // Re-run all checks after fixes\n checks = await runAllChecks();\n }\n\n if (jsonMode) {\n outputJson(checks);\n } else {\n displayResults(checks);\n }\n\n const hasFail = checks.some((c) => c.status === 'fail');\n if (hasFail) {\n process.exit(1);\n }\n });\n}\n","import { getAuthMethod } from '../../../util/auth.js';\nimport { getAuthConfig } from '../../../util/config.js';\nimport type { CheckRunner, DoctorCheck } from '../types.js';\n\nexport const checkAuth: CheckRunner = async () => {\n const checks: DoctorCheck[] = [];\n const method = getAuthMethod();\n\n // Auth method\n if (method === 'platform_token') {\n checks.push({\n name: 'auth_method',\n group: 'Authentication',\n status: 'pass',\n label: 'Auth method',\n value: 'Platform Token (browser login)',\n });\n } else if (method === 'cas') {\n checks.push({\n name: 'auth_method',\n group: 'Authentication',\n status: 'pass',\n label: 'Auth method',\n value: 'CAS PKCE',\n });\n } else if (method === 'api_key') {\n checks.push({\n name: 'auth_method',\n group: 'Authentication',\n status: 'pass',\n label: 'Auth method',\n value: 'API Key',\n });\n } else {\n checks.push({\n name: 'auth_method',\n group: 'Authentication',\n status: 'warn',\n label: 'Auth method',\n value: 'Not authenticated',\n fix: 'Run `rush-ai auth login` to authenticate',\n });\n }\n\n // Token status — depends on auth method\n if (method === 'api_key') {\n checks.push({\n name: 'token_status',\n group: 'Authentication',\n status: 'info',\n label: 'Token status',\n value: 'Using API key auth',\n });\n } else if (method === 'cas' || method === 'platform_token') {\n const auth = getAuthConfig();\n if (auth.token && auth.expiresAt) {\n const remaining = auth.expiresAt - Date.now();\n if (remaining > 0) {\n const hours = Math.floor(remaining / (1000 * 60 * 60));\n const minutes = Math.floor(\n (remaining % (1000 * 60 * 60)) / (1000 * 60)\n );\n const timeStr = hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;\n checks.push({\n name: 'token_status',\n group: 'Authentication',\n status: 'pass',\n label: 'Token status',\n value: `Valid (expires in ${timeStr})`,\n });\n } else {\n checks.push({\n name: 'token_status',\n group: 'Authentication',\n status: 'warn',\n label: 'Token status',\n value: 'Expired',\n fix: 'Run `rush-ai auth login` to re-authenticate',\n });\n }\n } else if (auth.token) {\n // Token exists but no expiry info\n checks.push({\n name: 'token_status',\n group: 'Authentication',\n status: 'pass',\n label: 'Token status',\n value: 'Valid (no expiry set)',\n });\n } else {\n checks.push({\n name: 'token_status',\n group: 'Authentication',\n status: 'warn',\n label: 'Token status',\n value: 'No token found',\n fix: 'Run `rush-ai auth login` to authenticate',\n });\n }\n } else {\n // Not authenticated\n checks.push({\n name: 'token_status',\n group: 'Authentication',\n status: 'warn',\n label: 'Token status',\n value: 'Not authenticated',\n fix: 'Run `rush-ai auth login` to authenticate',\n });\n }\n\n // API Key env\n const apiKey = process.env.RUSH_API_KEY;\n checks.push({\n name: 'api_key',\n group: 'Authentication',\n status: 'info',\n label: 'API Key',\n value: apiKey ? 'Set (RUSH_API_KEY)' : 'Not set',\n });\n\n return checks;\n};\n","import { accessSync, constants, existsSync, mkdirSync } from 'node:fs';\nimport {\n getAuthConfig,\n getConfigDir,\n getGlobalConfig,\n} from '../../../util/config.js';\nimport type { CheckRunner, DoctorCheck } from '../types.js';\n\nexport const checkConfig: CheckRunner = async () => {\n const checks: DoctorCheck[] = [];\n const configDir = getConfigDir();\n\n // Config directory existence + writability\n const dirExists = existsSync(configDir);\n let writable = false;\n if (dirExists) {\n try {\n accessSync(configDir, constants.W_OK);\n writable = true;\n } catch {\n // not writable\n }\n }\n\n if (dirExists && writable) {\n checks.push({\n name: 'config_dir',\n group: 'Configuration',\n status: 'pass',\n label: 'Config directory',\n value: configDir,\n });\n } else if (dirExists && !writable) {\n checks.push({\n name: 'config_dir',\n group: 'Configuration',\n status: 'fail',\n label: 'Config directory',\n value: `${configDir} (not writable)`,\n fix: `Check permissions on ${configDir}`,\n });\n } else {\n checks.push({\n name: 'config_dir',\n group: 'Configuration',\n status: 'fail',\n label: 'Config directory',\n value: `${configDir} (not found)`,\n fix: `Run \\`rush-ai doctor --fix\\` to create it`,\n autoFix: async () => {\n mkdirSync(configDir, { recursive: true });\n },\n });\n }\n\n // Config files validity\n try {\n getGlobalConfig();\n getAuthConfig();\n checks.push({\n name: 'config_validity',\n group: 'Configuration',\n status: 'pass',\n label: 'Config files',\n value: 'Valid',\n });\n } catch (err) {\n checks.push({\n name: 'config_validity',\n group: 'Configuration',\n status: 'fail',\n label: 'Config files',\n value: `Error: ${err instanceof Error ? err.message : 'unknown'}`,\n fix: 'Check config files in ' + configDir,\n });\n }\n\n // API URL\n try {\n const config = getGlobalConfig();\n checks.push({\n name: 'api_url',\n group: 'Configuration',\n status: 'info',\n label: 'API URL',\n value: config.api,\n });\n } catch {\n checks.push({\n name: 'api_url',\n group: 'Configuration',\n status: 'warn',\n label: 'API URL',\n value: 'Unable to read',\n });\n }\n\n return checks;\n};\n","import { getGlobalConfig } from '../../../util/config.js';\nimport type { CheckRunner, DoctorCheck } from '../types.js';\n\nexport const checkConnectivity: CheckRunner = async () => {\n const checks: DoctorCheck[] = [];\n const config = getGlobalConfig();\n const apiUrl = process.env.RUSH_API_URL ?? config.api;\n\n // API endpoint — raw fetch without auth, measure RTT\n try {\n const start = Date.now();\n const response = await fetch(apiUrl, {\n method: 'HEAD',\n signal: AbortSignal.timeout(5000),\n });\n const rtt = Date.now() - start;\n\n // Any HTTP response (even 401/403) means the server is reachable\n checks.push({\n name: 'api_endpoint',\n group: 'Connectivity',\n status: 'pass',\n label: 'API endpoint',\n value: `${apiUrl} (${rtt}ms, HTTP ${response.status})`,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Unknown error';\n const isTimeout = message.includes('timeout') || message.includes('abort');\n checks.push({\n name: 'api_endpoint',\n group: 'Connectivity',\n status: 'fail',\n label: 'API endpoint',\n value: isTimeout\n ? `${apiUrl} (timeout after 5s)`\n : `${apiUrl} (${message})`,\n fix: 'Check your network connection and API URL in config',\n });\n }\n\n // Proxy environment variables\n const httpProxy = process.env.HTTP_PROXY || process.env.http_proxy;\n const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy;\n\n if (httpProxy || httpsProxy) {\n const parts: string[] = [];\n if (httpProxy) parts.push(`HTTP_PROXY=${maskUrl(httpProxy)}`);\n if (httpsProxy) parts.push(`HTTPS_PROXY=${maskUrl(httpsProxy)}`);\n checks.push({\n name: 'proxy',\n group: 'Connectivity',\n status: 'info',\n label: 'Proxy',\n value: parts.join(', '),\n });\n } else {\n checks.push({\n name: 'proxy',\n group: 'Connectivity',\n status: 'info',\n label: 'Proxy',\n value: 'Not configured',\n });\n }\n\n return checks;\n};\n\n/** Mask proxy URL credentials: http://user:pass@host → http://***@host */\nfunction maskUrl(url: string): string {\n try {\n const parsed = new URL(url);\n if (parsed.username || parsed.password) {\n parsed.username = '***';\n parsed.password = '';\n return parsed.toString().replace(/\\/$/, '');\n }\n // URL parsed but no standard credentials detected — check for @ in raw string\n // (non-standard schemes like `foo:user@host` won't expose username/password)\n if (url.includes('@')) {\n return url.replace(/[^/@]+@/, '***@');\n }\n return parsed.toString().replace(/\\/$/, '');\n } catch {\n // If URL parsing fails, strip anything that looks like credentials\n if (url.includes('@')) {\n return url.replace(/[^/@]+@/, '***@');\n }\n return url;\n }\n}\n","import { execFileSync } from 'node:child_process';\nimport { arch, platform, release, type } from 'node:os';\nimport { VERSION } from '../../../version.js';\nimport type { CheckRunner, DoctorCheck } from '../types.js';\n\nfunction formatOS(): string {\n const p = platform();\n const r = release();\n const a = arch();\n\n let osName: string;\n switch (p) {\n case 'darwin':\n osName = 'macOS';\n break;\n case 'win32':\n osName = 'Windows';\n break;\n case 'linux':\n osName = 'Linux';\n break;\n default:\n osName = type();\n }\n\n return `${osName} ${r} (${p} ${a})`;\n}\n\nconst KNOWN_SHELLS = ['bash', 'zsh', 'fish', 'sh', 'dash', 'ksh', 'tcsh'];\n\nfunction detectShell(): { name: string; version: string | null } {\n const shell = process.env.SHELL;\n if (!shell) {\n return { name: 'unknown', version: null };\n }\n\n const name = shell.split('/').pop() ?? shell;\n\n // Only execute known shells to avoid running arbitrary binaries\n if (!KNOWN_SHELLS.includes(name)) {\n return { name, version: null };\n }\n\n let version: string | null = null;\n\n try {\n const raw = execFileSync(shell, ['--version'], {\n timeout: 3000,\n stdio: ['pipe', 'pipe', 'pipe'],\n encoding: 'utf-8',\n });\n // Extract first line, try to find version number\n const firstLine = raw.split('\\n')[0] ?? '';\n const match = firstLine.match(/(\\d+\\.\\d+[\\d.]*)/);\n version = match?.[1] ?? null;\n } catch {\n // Some shells (e.g. fish) use --version differently, or may fail\n }\n\n return { name, version };\n}\n\nexport const checkEnvironment: CheckRunner = async () => {\n const checks: DoctorCheck[] = [];\n\n // CLI version\n checks.push({\n name: 'cli_version',\n group: 'Environment',\n status: 'info',\n label: 'CLI version',\n value: VERSION,\n });\n\n // Node.js version\n const nodeVersion = process.version;\n const major = parseInt(nodeVersion.slice(1), 10);\n const nodeOk = major >= 18;\n checks.push({\n name: 'node_version',\n group: 'Environment',\n status: nodeOk ? 'pass' : 'fail',\n label: 'Node.js version',\n value: `${nodeVersion}${nodeOk ? '' : ' (>=18.0.0 required)'}`,\n fix: nodeOk ? undefined : 'Upgrade Node.js to version 18 or higher',\n });\n\n // OS\n checks.push({\n name: 'os',\n group: 'Environment',\n status: 'info',\n label: 'OS',\n value: formatOS(),\n });\n\n // Shell\n const shell = detectShell();\n const shellValue =\n shell.name === 'unknown'\n ? 'Unknown'\n : shell.version\n ? `${shell.name} ${shell.version}`\n : shell.name;\n checks.push({\n name: 'shell',\n group: 'Environment',\n status: shell.name === 'unknown' ? 'warn' : 'pass',\n label: 'Shell',\n value: shellValue,\n });\n\n // Terminal\n const terminal = process.env.TERM_PROGRAM ?? 'Unknown';\n checks.push({\n name: 'terminal',\n group: 'Environment',\n status: 'info',\n label: 'Terminal',\n value: terminal,\n });\n\n return checks;\n};\n","import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { resolve } from 'node:path';\nimport { getGlobalConfig } from '../../../util/config.js';\nimport type { CheckRunner, DoctorCheck } from '../types.js';\n\ninterface PluginManifest {\n name: string;\n version: string;\n type: 'claude-code' | 'cursor';\n installedAt: string;\n}\n\ninterface InstalledPlugins {\n plugins: Record<string, PluginManifest>;\n}\n\nconst PLUGINS_FILE = resolve(homedir(), '.rush', 'plugins', 'installed.json');\n\n/** Map plugin type to its MCP config file path */\nconst MCP_CONFIG_PATHS: Record<string, string> = {\n 'claude-code': resolve(homedir(), '.claude', 'settings.json'),\n cursor: resolve(homedir(), '.cursor', 'mcp.json'),\n};\n\nfunction safeReadJson<T>(filePath: string, fallback: T): T {\n if (!existsSync(filePath)) return fallback;\n try {\n return JSON.parse(readFileSync(filePath, 'utf-8')) as T;\n } catch {\n return fallback;\n }\n}\n\nfunction injectMcpConfig(configPath: string): void {\n const config = safeReadJson<Record<string, unknown>>(configPath, {});\n const mcpServers = (config.mcpServers ?? {}) as Record<string, unknown>;\n const globalConfig = getGlobalConfig();\n\n mcpServers.rush = {\n command: 'rush-ai',\n args: ['mcp', 'serve'],\n env: {\n RUSH_API_URL: globalConfig.api,\n },\n };\n config.mcpServers = mcpServers;\n writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');\n}\n\nfunction checkMcpForPlugin(\n name: string,\n manifest: PluginManifest\n): DoctorCheck {\n const configPath = MCP_CONFIG_PATHS[manifest.type];\n if (!configPath) {\n return {\n name: `plugin_${name}`,\n group: 'Plugins',\n status: 'warn',\n label: name,\n value: `Installed (v${manifest.version}), unknown type: ${manifest.type}`,\n };\n }\n\n if (!existsSync(configPath)) {\n return {\n name: `plugin_${name}`,\n group: 'Plugins',\n status: 'warn',\n label: name,\n value: `Installed, MCP config missing (${configPath})`,\n fix: `Run \\`rush-ai plugin install ${name}\\``,\n autoFix: async () => {\n injectMcpConfig(configPath);\n },\n };\n }\n\n try {\n const config = JSON.parse(readFileSync(configPath, 'utf-8'));\n const servers = config.mcpServers as Record<string, unknown> | undefined;\n\n if (!servers || !('rush' in servers)) {\n return {\n name: `plugin_${name}`,\n group: 'Plugins',\n status: 'warn',\n label: name,\n value: 'Installed, MCP server not configured',\n fix: `Run \\`rush-ai doctor --fix\\` to inject MCP config`,\n autoFix: async () => {\n injectMcpConfig(configPath);\n },\n };\n }\n\n const rushServer = servers.rush as Record<string, unknown>;\n const command = rushServer?.command as string | undefined;\n\n if (command !== 'rush-ai') {\n return {\n name: `plugin_${name}`,\n group: 'Plugins',\n status: 'warn',\n label: name,\n value: `Installed, MCP command is \"${command}\" (expected \"rush-ai\")`,\n fix: `Run \\`rush-ai plugin update ${name}\\``,\n };\n }\n\n return {\n name: `plugin_${name}`,\n group: 'Plugins',\n status: 'pass',\n label: name,\n value: `Installed (v${manifest.version}), MCP config valid`,\n };\n } catch {\n return {\n name: `plugin_${name}`,\n group: 'Plugins',\n status: 'warn',\n label: name,\n value: `Installed, failed to parse MCP config`,\n fix: `Check ${configPath} for JSON syntax errors`,\n };\n }\n}\n\nexport const checkPlugins: CheckRunner = async () => {\n const data = safeReadJson<InstalledPlugins>(PLUGINS_FILE, { plugins: {} });\n const plugins =\n data &&\n typeof data === 'object' &&\n data.plugins &&\n typeof data.plugins === 'object'\n ? data.plugins\n : {};\n const entries = Object.entries(plugins);\n\n if (entries.length === 0) {\n return [\n {\n name: 'plugins_none',\n group: 'Plugins',\n status: 'info',\n label: 'Plugins',\n value: 'No plugins installed',\n },\n ];\n }\n\n return entries.map(([name, manifest]) => checkMcpForPlugin(name, manifest));\n};\n","/**\n * 本地 marketplace cache 目录管理。\n *\n * 文件布局(默认 `~/.rush/marketplaces/`,测试可注入):\n * ```\n * <cacheBaseDir>/\n * ├── <mkt-name-1>/\n * │ ├── .rush-marketplace-source.json ← rush-ai 维护的元数据文件\n * │ ├── .claude-plugin/marketplace.json (clone/copy 下来的)\n * │ ├── .git/ (仅 github: / git: source)\n * │ └── ... (其他 plugin 目录)\n * └── <mkt-name-2>/ ...\n * ```\n *\n * `.rush-marketplace-source.json` 是 rush-ai 记录\"这个 cache 来自哪个 source\"的\n * 元数据文件,方便 `list()` / `update()` 不依赖其他配置还原 source。\n *\n * API(对齐 `specs/cli-commands.md` §1):\n * - `add(source, {name?, runner?})` → 把 source 落到 cache,返回 ResolvedMarketplace\n * - `remove(name)` → 删 cache 目录\n * - `update(name, {runner?})` → 重新 fetch(仅对可更新的 source)\n * - `list()` → 枚举当前 cache 里的 marketplace\n * - `get(name)` → 读单个 marketplace\n *\n * **测试铁律**:所有操作通过 `cacheBaseDir` 或 `home` 注入临时目录,禁止触碰真实 `~/.rush/`。\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { constants as fsConstants } from 'node:fs';\nimport {\n access,\n mkdir,\n readdir,\n readFile,\n rename,\n rm,\n writeFile,\n} from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, resolve } from 'node:path';\nimport {\n ensureDirectoryExists,\n resolveDirectoryMarketplace,\n} from './directory.js';\nimport {\n cloneGithubMarketplace,\n type GitRunner,\n updateGithubMarketplace,\n} from './github.js';\nimport { readMarketplaceJson } from './manifest.js';\nimport {\n defaultNameFromSource,\n MarketplaceError,\n NotImplementedError,\n parseSource,\n} from './source.js';\nimport type {\n CachedMarketplace,\n MarketplaceSource,\n ResolvedMarketplace,\n} from './types.js';\n\n/**\n * Cache root 相对 HOME 的路径(`~/.rush/marketplaces/`)。\n *\n * 暴露常量便于 CLI 测试断言。\n */\nexport const MARKETPLACE_CACHE_RELATIVE_PATH = '.rush/marketplaces' as const;\n\n/**\n * Cache 目录里 rush-ai 自己维护的 source 元数据文件名。\n */\nexport const CACHE_META_FILENAME = '.rush-marketplace-source.json' as const;\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\n/**\n * 同名 marketplace 已存在于 cache 且 source 与请求不一致。\n *\n * 注意字段命名:用 `marketplaceName`(不是 `name`),避免和 `Error.name`\n * 冲突——父类构造里 `this.name = 'MarketplaceAlreadyExistsError'` 会\n * 覆盖同名 public 参数属性,导致调用方拿不到真实 marketplace 名。\n */\nexport class MarketplaceAlreadyExistsError extends MarketplaceError {\n constructor(\n public readonly marketplaceName: string,\n public readonly existingSourceRaw: string,\n public readonly requestedSourceRaw: string\n ) {\n super(\n `Marketplace '${marketplaceName}' already exists with source '${existingSourceRaw}'; ` +\n `refusing to overwrite with '${requestedSourceRaw}'. ` +\n `Use 'rush-ai marketplace remove ${marketplaceName}' first, or pass --as <different-name>.`\n );\n this.name = 'MarketplaceAlreadyExistsError';\n }\n}\n\n/**\n * cache 里找不到指定 marketplace。同样用 `marketplaceName` 命名字段避免\n * 与 `Error.name` 冲突。\n */\nexport class MarketplaceNotFoundError extends MarketplaceError {\n constructor(public readonly marketplaceName: string) {\n super(`Marketplace '${marketplaceName}' not found in local cache.`);\n this.name = 'MarketplaceNotFoundError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface MarketplaceCacheOptions {\n /**\n * 注入 HOME 目录(测试用,默认 `os.homedir()`)。\n *\n * 和 `cacheBaseDir` 二选一——优先 `cacheBaseDir`。\n */\n readonly home?: string;\n /**\n * 直接注入 cache 根目录绝对路径(优先级高于 `home`)。\n */\n readonly cacheBaseDir?: string;\n /**\n * 注入 git runner(测试 mock;生产走 defaultGitRunner)。\n *\n * 作用于整个 store 的所有 github/git 操作;单次 add/update 也可以临时覆盖。\n */\n readonly gitRunner?: GitRunner;\n}\n\nexport interface AddOptions {\n /** 自定义 marketplace 名(覆盖 source 推导出来的默认名) */\n readonly as?: string;\n /** 本次调用专属 gitRunner(覆盖 store 级别注入) */\n readonly gitRunner?: GitRunner;\n /**\n * `--force`:若同名 cache 存在且 source 不同,仍覆盖。\n *\n * 默认 false → 抛 `MarketplaceAlreadyExistsError`,避免误删用户数据。\n */\n readonly force?: boolean;\n}\n\nexport interface UpdateOptions {\n readonly gitRunner?: GitRunner;\n}\n\n// ---------------------------------------------------------------------------\n// MarketplaceCache class\n// ---------------------------------------------------------------------------\n\n/**\n * Local marketplace cache 的唯一入口。\n *\n * 典型用法:\n * ```ts\n * const cache = new MarketplaceCache(); // 默认 ~/.rush/marketplaces\n * await cache.add(parseSource('github:a/b')); // add\n * const list = await cache.list(); // list\n * await cache.update('b'); // update\n * await cache.remove('b'); // remove\n * ```\n *\n * 所有操作都是 async;单次调用内部保证\"add 完整落地再返回 / remove 完整清理再返回\",\n * 不留半成品。\n *\n * 本 class 本身**不**持有锁——调用方(CLI)负责序列化,同一时刻不并发 add 同名。\n */\nexport class MarketplaceCache {\n public readonly cacheBaseDir: string;\n private readonly gitRunner?: GitRunner;\n\n constructor(opts: MarketplaceCacheOptions = {}) {\n if (opts.cacheBaseDir) {\n this.cacheBaseDir = resolve(opts.cacheBaseDir);\n } else {\n const home = opts.home ?? homedir();\n this.cacheBaseDir = resolve(home, MARKETPLACE_CACHE_RELATIVE_PATH);\n }\n if (opts.gitRunner) {\n this.gitRunner = opts.gitRunner;\n }\n }\n\n /** 给定 name 得到其 cache 目录绝对路径(可能不存在)。 */\n pathFor(name: string): string {\n if (!name || name.includes('/') || name.includes('..')) {\n throw new MarketplaceError(\n `Invalid marketplace name '${name}'. Names must be non-empty and cannot contain '/' or '..'.`\n );\n }\n return resolve(this.cacheBaseDir, name);\n }\n\n /**\n * 添加一个 marketplace 到本地 cache。\n *\n * 行为:\n * - `directory:` source:不 cache,但仍记录 meta(方便 list + remove 时去注册)\n * - `github:` source:shallow clone 到 `<cacheBaseDir>/<name>/`\n * - 其他 source:抛 NotImplementedError\n *\n * 幂等性:\n * - 同名 + 同 source 已存在 → 直接返回现有 ResolvedMarketplace(no-op)\n * - 同名 + 不同 source → `MarketplaceAlreadyExistsError`(除非 `force=true`)\n */\n async add(\n source: MarketplaceSource,\n opts: AddOptions = {}\n ): Promise<ResolvedMarketplace> {\n const name = opts.as ?? defaultNameFromSource(source);\n const targetDir = this.pathFor(name);\n\n // 检查是否已存在同名 cache。注意:**不吞** readMeta 抛出的错误——\n // corrupt/shape-invalid meta 应该让 add 失败(避免误覆盖用户数据)。\n // 只有\"meta 文件不存在\"才返回 null 走新建分支。\n const existingMeta = await this.readMeta(name);\n if (existingMeta) {\n if (existingMeta.source.raw === source.raw) {\n // 幂等:相同 source 直接返回现有\n return this.resolveExisting(name, existingMeta.source);\n }\n if (!opts.force) {\n throw new MarketplaceAlreadyExistsError(\n name,\n existingMeta.source.raw,\n source.raw\n );\n }\n // force:删掉旧 cache 再继续\n await this.remove(name);\n }\n\n // 按 source kind 分派\n const gitRunner = opts.gitRunner ?? this.gitRunner;\n\n switch (source.kind) {\n case 'directory': {\n // directory: 不 cache;但仍验证 + 写 meta(meta 文件放在 cacheBaseDir 里\n // 的 <name>/ 下,不污染用户的 directory: path)\n await ensureDirectoryExists(source);\n const resolved = await resolveDirectoryMarketplace(source, name);\n await this.writeMeta(name, source);\n return resolved;\n }\n case 'github': {\n await mkdir(dirname(targetDir), { recursive: true });\n const resolved = await cloneGithubMarketplace(\n source,\n targetDir,\n name,\n gitRunner ? { runner: gitRunner } : {}\n );\n await this.writeMeta(name, source);\n return resolved;\n }\n case 'git':\n case 'rush':\n case 'npm':\n throw new NotImplementedError(source.kind, source.raw);\n }\n }\n\n /**\n * 删除 cache 里指定 marketplace。\n *\n * - directory: source:只删 meta 目录(<cacheBaseDir>/<name>/),不触碰用户目录\n * - github: source:删整个 <cacheBaseDir>/<name>/\n * - 不存在 → no-op(不报错;CLI 层决定是否提示)\n */\n async remove(name: string): Promise<void> {\n const target = this.pathFor(name);\n if (!(await pathExists(target))) {\n return;\n }\n await rm(target, { recursive: true, force: true });\n }\n\n /**\n * 重新 fetch 远端 source(仅对 github: / git: / rush:// / npm:)。\n *\n * - directory: source → no-op(返回现有 ResolvedMarketplace)\n * - github: source → fetch + checkout\n * - 不存在 → MarketplaceNotFoundError\n */\n async update(\n name: string,\n opts: UpdateOptions = {}\n ): Promise<ResolvedMarketplace> {\n const meta = await this.readMeta(name);\n if (!meta) {\n throw new MarketplaceNotFoundError(name);\n }\n const runner = opts.gitRunner ?? this.gitRunner;\n\n switch (meta.source.kind) {\n case 'directory':\n // directory: 不 cache,update 是 no-op;重新 resolve 确保还在\n return resolveDirectoryMarketplace(meta.source, name);\n case 'github': {\n const cacheDir = this.pathFor(name);\n const resolved = await updateGithubMarketplace(\n meta.source,\n cacheDir,\n name,\n runner ? { runner } : {}\n );\n await this.writeMeta(name, meta.source); // 更新 cachedAt\n return resolved;\n }\n case 'git':\n case 'rush':\n case 'npm':\n throw new NotImplementedError(meta.source.kind, meta.source.raw);\n }\n }\n\n /**\n * 列出所有 cache 里已注册的 marketplace。\n *\n * 读 cacheBaseDir 下每个子目录的 meta 文件;**缺 meta 或 meta 损坏**的子目录\n * 跳过(宽容枚举语义)——避免单个坏 cache 阻塞 `marketplace list` 整体命令。\n * `add()` / `has()` / `get()` 路径仍然对损坏 meta 严格抛错,以保护写操作。\n * v1 暂不单列损坏项;若未来需要,可加 `options.includeCorrupt` 返回 warning。\n */\n async list(): Promise<CachedMarketplace[]> {\n if (!(await pathExists(this.cacheBaseDir))) {\n return [];\n }\n const entries = await readdir(this.cacheBaseDir, { withFileTypes: true });\n const result: CachedMarketplace[] = [];\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n // 宽容:吞 corrupt meta 错误(list 语义),其他路径(add/has/get)不吞\n const meta = await this.readMeta(entry.name).catch(() => null);\n if (!meta) continue;\n result.push({\n name: entry.name,\n path: resolve(this.cacheBaseDir, entry.name),\n source: meta.source,\n cachedAt: meta.cachedAt,\n });\n }\n // 字典序便于人眼扫\n result.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));\n return result;\n }\n\n /**\n * 查询单个 marketplace,解析 + 返回完整 ResolvedMarketplace。\n *\n * 不存在 → `MarketplaceNotFoundError`。\n */\n async get(name: string): Promise<ResolvedMarketplace> {\n const meta = await this.readMeta(name);\n if (!meta) {\n throw new MarketplaceNotFoundError(name);\n }\n return this.resolveExisting(name, meta.source);\n }\n\n /**\n * 判断 marketplace 是否存在于 cache(不 resolve,纯看 meta 文件是否存在)。\n *\n * 注意:**不吞** meta 损坏错误——若 meta 文件存在但损坏/shape 错误,抛\n * `MarketplaceError`,让调用方能区分\"不存在\"和\"cache 需要修复\"。\n * 仅当 meta 文件缺失时返回 false。\n */\n async has(name: string): Promise<boolean> {\n const meta = await this.readMeta(name);\n return meta !== null;\n }\n\n // -------------------------------------------------------------------------\n // Internals\n // -------------------------------------------------------------------------\n\n private async resolveExisting(\n name: string,\n source: MarketplaceSource\n ): Promise<ResolvedMarketplace> {\n switch (source.kind) {\n case 'directory':\n return resolveDirectoryMarketplace(source, name);\n case 'github': {\n const cacheDir = this.pathFor(name);\n const manifest = await readMarketplaceJson(cacheDir);\n return { name, rootDir: cacheDir, source, manifest };\n }\n case 'git':\n case 'rush':\n case 'npm':\n throw new NotImplementedError(source.kind, source.raw);\n }\n }\n\n private metaPathFor(name: string): string {\n return resolve(this.pathFor(name), CACHE_META_FILENAME);\n }\n\n private async readMeta(\n name: string\n ): Promise<{ source: MarketplaceSource; cachedAt: string } | null> {\n const metaPath = this.metaPathFor(name);\n if (!(await pathExists(metaPath))) {\n return null;\n }\n const raw = await readFile(metaPath, 'utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new MarketplaceError(\n `Marketplace cache meta at '${metaPath}' is corrupt: ${\n (err as Error).message\n }`\n );\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new MarketplaceError(\n `Marketplace cache meta at '${metaPath}' has wrong shape (expected object)`\n );\n }\n const obj = parsed as Record<string, unknown>;\n if (typeof obj.sourceRaw !== 'string') {\n throw new MarketplaceError(\n `Marketplace cache meta at '${metaPath}' missing 'sourceRaw' string`\n );\n }\n if (typeof obj.cachedAt !== 'string') {\n throw new MarketplaceError(\n `Marketplace cache meta at '${metaPath}' missing 'cachedAt' string`\n );\n }\n const source = parseSource(obj.sourceRaw);\n return { source, cachedAt: obj.cachedAt };\n }\n\n private async writeMeta(\n name: string,\n source: MarketplaceSource\n ): Promise<void> {\n const metaPath = this.metaPathFor(name);\n await mkdir(dirname(metaPath), { recursive: true });\n const payload = {\n sourceRaw: source.raw,\n cachedAt: new Date().toISOString(),\n // 冗余字段便于人工检查,不被 readMeta 消费\n kind: source.kind,\n };\n await atomicWriteJson(metaPath, payload);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p, fsConstants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function atomicWriteJson(\n filePath: string,\n payload: unknown\n): Promise<void> {\n const content = `${JSON.stringify(payload, null, 2)}\\n`;\n const tmp = `${filePath}.${randomUUID()}.tmp`;\n try {\n await writeFile(tmp, content, { encoding: 'utf8' });\n await rename(tmp, filePath);\n } catch (err) {\n await rm(tmp, { force: true }).catch(() => {});\n throw err;\n }\n}\n","/**\n * `directory:` source 的 IO 实现。\n *\n * Phase 1 特性:**不做 cache、不 copy、不 symlink**——直接引用用户给的绝对路径,\n * 验证目录存在 + 读 `.claude-plugin/marketplace.json` 即可。\n *\n * 适用场景:\n * - 本地开发 marketplace(边改边测)\n * - CI / test fixture\n * - 企业内网挂载的 NFS 路径\n */\n\nimport { stat as fsStat } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { parseMarketplaceJson, readMarketplaceJson } from './manifest.js';\nimport {\n InvalidSourceError,\n MarketplaceError,\n NotImplementedError,\n} from './source.js';\nimport type {\n DirectoryMarketplaceSource,\n MarketplaceManifest,\n ResolvedMarketplace,\n} from './types.js';\n\n/**\n * Marketplace 源目录不存在或不可读。\n */\nexport class MarketplaceDirectoryNotFoundError extends MarketplaceError {\n constructor(public readonly path: string) {\n super(\n `Marketplace directory not found or unreadable: '${path}'. ` +\n `Verify the path exists and the current user has read permission.`\n );\n this.name = 'MarketplaceDirectoryNotFoundError';\n }\n}\n\n/**\n * 给定 `directory:` source,解析为 ResolvedMarketplace。\n *\n * 步骤:\n * 1. `stat` 目录 → 不存在 / 非目录 / 权限不够 → MarketplaceDirectoryNotFoundError\n * 2. 读 `<path>/.claude-plugin/marketplace.json`(由 `manifest.ts` 负责)\n * - 文件不存在 → MarketplaceCorruptError(reason='missing')\n * - 文件存在但 JSON 损坏 / 缺 `plugins` 字段 → MarketplaceCorruptError(reason='corrupt')\n * 3. 返回 `{ name, rootDir: path, source, manifest }`\n *\n * 错误类型一览(方便 CLI 错误映射):\n * - `MarketplaceDirectoryNotFoundError`:目录层问题(路径不存在/不是目录)\n * - `MarketplaceCorruptError`:marketplace.json 层问题(缺失或损坏),见 `manifest.ts`\n *\n * 注意:本函数不 mutate cache,不写 registry——只是 resolve。CLI 层再决定是否\n * 要把 source 登记到 cache(`cache.ts` 的 add / remove)。\n */\nexport async function resolveDirectoryMarketplace(\n source: DirectoryMarketplaceSource,\n name: string\n): Promise<ResolvedMarketplace> {\n if (!name || typeof name !== 'string') {\n throw new InvalidSourceError(\n source.raw,\n 'marketplace name must be non-empty string'\n );\n }\n\n const rootDir = resolve(source.path);\n let stats: Awaited<ReturnType<typeof fsStat>>;\n try {\n stats = await fsStat(rootDir);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT' || code === 'ENOTDIR' || code === 'EACCES') {\n throw new MarketplaceDirectoryNotFoundError(rootDir);\n }\n throw err;\n }\n\n if (!stats.isDirectory()) {\n throw new MarketplaceDirectoryNotFoundError(rootDir);\n }\n\n const manifest = await readMarketplaceJson(rootDir);\n\n return {\n name,\n rootDir,\n source,\n manifest,\n };\n}\n\n/**\n * 专给 parser 侧 \"validate-only\" 使用——只检查目录存在,不解析 JSON。\n *\n * 测试里偶尔需要只验证路径而不触发 JSON 解析;生产 CLI 路径请用\n * `resolveDirectoryMarketplace`。\n */\nexport async function ensureDirectoryExists(\n source: DirectoryMarketplaceSource\n): Promise<void> {\n try {\n const stats = await fsStat(source.path);\n if (!stats.isDirectory()) {\n throw new MarketplaceDirectoryNotFoundError(source.path);\n }\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT' || code === 'ENOTDIR' || code === 'EACCES') {\n throw new MarketplaceDirectoryNotFoundError(source.path);\n }\n throw err;\n }\n}\n\n// Re-export 便于 barrel 引用(避免 import 路径两跳)\nexport { parseMarketplaceJson };\nexport type { MarketplaceManifest };\n\n// 确保 NotImplementedError 可从本文件也导出(CLI 拦截 Phase 2 source 时需要)\nexport { NotImplementedError };\n","/**\n * `.claude-plugin/marketplace.json` 读取与校验。\n *\n * 读取策略(spec §3 \"最小必要字段\"原则):\n * - 必需:顶层是 object + 有 `plugins` 数组\n * - 其他字段原样透传,不做类型校验(上游作者可能有私有字段)\n * - JSON 损坏 / 缺 `plugins` → 抛 `MarketplaceCorruptError`(**不 swallow**,对齐 task 验收)\n *\n * 本文件被 `directory.ts` / `github.ts` / `cache.ts` 共享。\n */\n\nimport { constants as fsConstants } from 'node:fs';\nimport { access, readFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { MarketplaceError } from './source.js';\nimport type { MarketplaceManifest } from './types.js';\n\nexport const MARKETPLACE_MANIFEST_RELATIVE_PATH =\n '.claude-plugin/marketplace.json' as const;\n\n/**\n * marketplace.json 损坏或不存在的错误。\n *\n * 细分两种:\n * - `reason='missing'`:文件不存在\n * - `reason='corrupt'`:JSON 解析失败 / 顶层不是 object / 缺 plugins 数组\n */\nexport class MarketplaceCorruptError extends MarketplaceError {\n constructor(\n public readonly filePath: string,\n public readonly reason: 'missing' | 'corrupt',\n public readonly detail: string\n ) {\n super(\n reason === 'missing'\n ? `marketplace.json not found at '${filePath}'. Is this a valid marketplace directory?`\n : `marketplace.json at '${filePath}' is corrupt: ${detail}. Please fix the file or contact the marketplace author.`\n );\n this.name = 'MarketplaceCorruptError';\n }\n}\n\n/**\n * 从 marketplace 根目录读取 `.claude-plugin/marketplace.json`。\n *\n * @param rootDir marketplace 根目录绝对路径\n * @returns 解析 + 校验后的 MarketplaceManifest\n * @throws {MarketplaceCorruptError}\n */\nexport async function readMarketplaceJson(\n rootDir: string\n): Promise<MarketplaceManifest> {\n const filePath = resolve(rootDir, MARKETPLACE_MANIFEST_RELATIVE_PATH);\n try {\n await access(filePath, fsConstants.R_OK);\n } catch {\n throw new MarketplaceCorruptError(\n filePath,\n 'missing',\n 'file does not exist or is not readable'\n );\n }\n\n const raw = await readFile(filePath, 'utf8');\n return parseMarketplaceJson(raw, filePath);\n}\n\n/**\n * 从字符串解析 + 校验 marketplace.json。\n *\n * 抽出此函数便于测试里直接传字符串,不依赖磁盘。\n */\nexport function parseMarketplaceJson(\n raw: string,\n sourcePathForError: string\n): MarketplaceManifest {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new MarketplaceCorruptError(\n sourcePathForError,\n 'corrupt',\n (err as Error).message\n );\n }\n\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new MarketplaceCorruptError(\n sourcePathForError,\n 'corrupt',\n 'root must be a JSON object'\n );\n }\n\n const obj = parsed as Record<string, unknown>;\n if (!Array.isArray(obj.plugins)) {\n throw new MarketplaceCorruptError(\n sourcePathForError,\n 'corrupt',\n \"missing 'plugins' array at root\"\n );\n }\n\n // 每个 plugin 条目至少要有 name;shape 不严格(保留作者原字段)\n for (const [idx, entry] of obj.plugins.entries()) {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {\n throw new MarketplaceCorruptError(\n sourcePathForError,\n 'corrupt',\n `plugins[${idx}] must be an object`\n );\n }\n const e = entry as Record<string, unknown>;\n if (typeof e.name !== 'string' || e.name.length === 0) {\n throw new MarketplaceCorruptError(\n sourcePathForError,\n 'corrupt',\n `plugins[${idx}].name must be a non-empty string`\n );\n }\n }\n\n return obj as unknown as MarketplaceManifest;\n}\n","/**\n * Source 字符串解析器 + 专用错误类型。\n *\n * Source of truth: `specs/cli-commands.md` §1.1 `marketplace add <source>`。\n *\n * 支持 5 种 prefix:\n * - `directory:/abs/path` ✅ Phase 1\n * - `github:owner/repo[#ref]` ✅ Phase 1\n * - `git:<url>` ⏳ Phase 1.5 → NotImplementedError\n * - `rush://host` ⏳ Phase 2 → NotImplementedError\n * - `npm:@scope/pkg` / `npm:pkg` ⏳ Phase 2 → NotImplementedError\n *\n * 解析器不做任何 IO;只做字符串层面的 shape 验证。\n * IO 发生在 `directory.ts` / `github.ts` 层面。\n */\n\nimport { isAbsolute } from 'node:path';\nimport type {\n DirectoryMarketplaceSource,\n GithubMarketplaceSource,\n GitMarketplaceSource,\n MarketplaceSource,\n MarketplaceSourceKind,\n NpmMarketplaceSource,\n RushMarketplaceSource,\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Error 类型\n// ---------------------------------------------------------------------------\n\n/** 基类 —— 便于 CLI 层统一 `instanceof MarketplaceError` catch。 */\nexport class MarketplaceError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'MarketplaceError';\n }\n}\n\n/** Source 字符串格式非法(prefix 缺失、shape 错误、空字段等)。 */\nexport class InvalidSourceError extends MarketplaceError {\n constructor(\n public readonly raw: string,\n reason: string\n ) {\n super(`Invalid marketplace source '${raw}': ${reason}`);\n this.name = 'InvalidSourceError';\n }\n}\n\n/**\n * 识别为合法 prefix 但 Phase 1 暂未实现。\n *\n * 错误消息明确引导用户\"Phase 2 会支持\",避免误以为是代码 bug。\n */\nexport class NotImplementedError extends MarketplaceError {\n constructor(\n public readonly kind: MarketplaceSourceKind,\n public readonly raw: string\n ) {\n super(\n `Source kind '${kind}:' is not implemented in Phase 1 (source='${raw}'). ` +\n `Phase 2 will support git: / rush:// / npm: — for now, please use ` +\n `'directory:<abs-path>' or 'github:<owner>/<repo>[#<ref>]'.`\n );\n this.name = 'NotImplementedError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// 解析器\n// ---------------------------------------------------------------------------\n\n/**\n * 从 raw 字符串解析出 MarketplaceSource。\n *\n * **只做字符串 shape 验证**(是否有前缀、字段是否非空、github owner/repo 形状),\n * **不**做任何 IO(不 stat 目录、不 clone)。IO 由 `directory.ts` / `github.ts`\n * 里的 fetch 函数负责。\n *\n * @throws {InvalidSourceError} 格式不合法\n * @throws {NotImplementedError} 合法但 Phase 1 未实现(git: / rush:// / npm:)\n */\nexport function parseSource(raw: string): MarketplaceSource {\n if (typeof raw !== 'string') {\n throw new InvalidSourceError(String(raw), 'source must be a string');\n }\n const trimmed = raw.trim();\n if (trimmed.length === 0) {\n throw new InvalidSourceError(raw, 'source must be non-empty');\n }\n\n if (trimmed.startsWith('directory:')) {\n return parseDirectory(trimmed);\n }\n if (trimmed.startsWith('github:')) {\n return parseGithub(trimmed);\n }\n if (trimmed.startsWith('rush://')) {\n return parseRush(trimmed);\n }\n if (trimmed.startsWith('git:')) {\n return parseGit(trimmed);\n }\n if (trimmed.startsWith('npm:')) {\n return parseNpm(trimmed);\n }\n\n throw new InvalidSourceError(\n raw,\n \"missing recognized prefix. Expected one of 'directory:' / 'github:' / 'git:' / 'rush://' / 'npm:'\"\n );\n}\n\n/**\n * 从 source 推导默认 marketplace 名(`--as` 省略时使用)。\n *\n * 规则对齐 `specs/cli-commands.md` §1.1:\n * - `directory:/abs/path` → 路径最后一段\n * - `github:owner/repo` → repo 名\n * - `git:<url>` → URL 里解析出 repo 名\n * - `rush://host` → `rush-marketplace`\n * - `npm:@scope/pkg` → pkg 名(去 @scope/)\n *\n * Phase 1 只对 `directory` / `github` 起实际作用;其他 kind 的实现虽然保留,\n * 也会在下游 `add()` 时被 `NotImplementedError` 拦住。这里仍然填完整映射,\n * 便于 CLI 在错误输出里展示\"如果实现了 name 会是啥\"。\n */\nexport function defaultNameFromSource(source: MarketplaceSource): string {\n switch (source.kind) {\n case 'directory': {\n const segments = source.path.split('/').filter((s) => s.length > 0);\n const last = segments[segments.length - 1];\n if (!last) {\n throw new InvalidSourceError(\n source.raw,\n 'cannot derive marketplace name from empty path'\n );\n }\n return last;\n }\n case 'github':\n return source.repo;\n case 'git': {\n // 从 URL 里提出最后一段的 repo 名\n const withoutQuery = source.url.split(/[?#]/)[0] ?? source.url;\n const tail =\n withoutQuery\n .replace(/\\.git$/, '')\n .split('/')\n .pop() ?? '';\n if (!tail) {\n throw new InvalidSourceError(\n source.raw,\n 'cannot derive marketplace name from git URL'\n );\n }\n return tail;\n }\n case 'rush':\n return 'rush-marketplace';\n case 'npm': {\n // @scope/pkg → pkg;pkg → pkg\n const parts = source.pkg.split('/');\n const last = parts[parts.length - 1];\n if (!last) {\n throw new InvalidSourceError(\n source.raw,\n 'cannot derive marketplace name from npm package'\n );\n }\n return last;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Per-kind 解析\n// ---------------------------------------------------------------------------\n\nfunction parseDirectory(raw: string): DirectoryMarketplaceSource {\n const path = raw.slice('directory:'.length);\n if (path.length === 0) {\n throw new InvalidSourceError(raw, 'directory: path is empty');\n }\n if (!isAbsolute(path)) {\n throw new InvalidSourceError(\n raw,\n `directory: path must be absolute (got '${path}')`\n );\n }\n return { kind: 'directory', raw, path };\n}\n\nfunction parseGithub(raw: string): GithubMarketplaceSource {\n const body = raw.slice('github:'.length);\n if (body.length === 0) {\n throw new InvalidSourceError(raw, 'github: body is empty');\n }\n\n // body shape: owner/repo[#ref]\n let pathPart = body;\n let ref: string | undefined;\n const hashIdx = body.indexOf('#');\n if (hashIdx !== -1) {\n pathPart = body.slice(0, hashIdx);\n ref = body.slice(hashIdx + 1);\n if (ref.length === 0) {\n throw new InvalidSourceError(raw, \"github: '#' present but ref is empty\");\n }\n if (!isValidGitRef(ref)) {\n throw new InvalidSourceError(raw, `github: ref '${ref}' is not valid`);\n }\n }\n\n const slashIdx = pathPart.indexOf('/');\n if (slashIdx === -1) {\n throw new InvalidSourceError(\n raw,\n \"github: body must be 'owner/repo' (missing '/')\"\n );\n }\n const owner = pathPart.slice(0, slashIdx);\n const repo = pathPart.slice(slashIdx + 1);\n\n if (!isValidGithubSegment(owner)) {\n throw new InvalidSourceError(raw, `github: owner '${owner}' is not valid`);\n }\n if (!isValidGithubSegment(repo)) {\n throw new InvalidSourceError(raw, `github: repo '${repo}' is not valid`);\n }\n // 禁止嵌套路径:owner/repo 之后不能再有 '/'\n if (repo.includes('/')) {\n throw new InvalidSourceError(\n raw,\n \"github: body must be 'owner/repo' (extra '/' found)\"\n );\n }\n\n return { kind: 'github', raw, owner, repo, ...(ref ? { ref } : {}) };\n}\n\nfunction parseGit(raw: string): GitMarketplaceSource {\n const url = raw.slice('git:'.length);\n if (url.length === 0) {\n throw new InvalidSourceError(raw, 'git: url is empty');\n }\n return { kind: 'git', raw, url };\n}\n\nfunction parseRush(raw: string): RushMarketplaceSource {\n const host = raw.slice('rush://'.length);\n if (host.length === 0) {\n throw new InvalidSourceError(raw, 'rush:// host is empty');\n }\n return { kind: 'rush', raw, host };\n}\n\nfunction parseNpm(raw: string): NpmMarketplaceSource {\n const pkg = raw.slice('npm:'.length);\n if (pkg.length === 0) {\n throw new InvalidSourceError(raw, 'npm: package is empty');\n }\n return { kind: 'npm', raw, pkg };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * GitHub owner / repo 段的合法字符集(RFC 与 GitHub 实际约定取最严的)。\n *\n * - 不允许空串\n * - 只含字母/数字/`-` / `.` / `_`\n * - 不以 `-` 开头或结尾(避免和 git 命令行 `--` flag 混淆)\n */\nfunction isValidGithubSegment(segment: string): boolean {\n if (segment.length === 0) return false;\n if (segment.startsWith('-') || segment.endsWith('-')) return false;\n return /^[A-Za-z0-9._-]+$/.test(segment);\n}\n\n/**\n * git ref(分支 / tag / sha)最小合法性校验。\n *\n * 对齐 `git check-ref-format --branch` 的**常见禁用字符**(我们不做完整 RFC 校验,\n * 以免拒绝合法名字),只拦明显危险:空格、`..`、控制字符、以 `-` 开头。\n *\n * - 不允许空串\n * - 不允许 `..`、`~`、`^`、`:`、`?`、`*`、`[`、`\\`、空格\n * - 不允许以 `-` 开头(和 git 命令行 flag 冲突)\n * - 不允许以 `.lock` 结尾(git 内部 refs 规则)\n */\nfunction isValidGitRef(ref: string): boolean {\n if (ref.length === 0) return false;\n if (ref.startsWith('-')) return false;\n if (ref.includes('..')) return false;\n if (ref.endsWith('.lock')) return false;\n // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional control-char rejection\n if (/[\\s~^:?*[\\]\\\\\\x00-\\x1f]/.test(ref)) return false;\n return true;\n}\n","/**\n * `github:` source 的 IO 实现。\n *\n * 关键策略(spec §1 + TASK 验收):\n * - 使用 `git clone --depth 1 [--branch <ref>] https://github.com/<owner>/<repo>.git <cache-dir>`\n * - **不**依赖 GitHub CLI (`gh`),避免引入额外工具\n * - 测试通过注入 `gitRunner` 函数 mock clone,**不真实打网络 / 写用户 HOME**\n *\n * Update 策略(已 clone 过之后的重新抓取):\n * - 如果 cache 目录已存在且是 git 仓库 → `git -C <cache-dir> fetch --depth 1 origin <ref>` → `git -C <cache-dir> checkout FETCH_HEAD`\n * - 如果 cache 目录存在但不是 git 仓库 → rm -rf 后重新 clone(保证状态干净)\n */\n\nimport type { SpawnOptions } from 'node:child_process';\nimport { spawn } from 'node:child_process';\nimport { constants as fsConstants } from 'node:fs';\nimport { access, rm } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { readMarketplaceJson } from './manifest.js';\nimport { MarketplaceError } from './source.js';\nimport type {\n GithubMarketplaceSource,\n MarketplaceManifest,\n ResolvedMarketplace,\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class GitCloneFailedError extends MarketplaceError {\n constructor(\n public readonly source: GithubMarketplaceSource,\n public readonly exitCode: number | null,\n public readonly stderr: string\n ) {\n super(\n `git clone failed for 'github:${source.owner}/${source.repo}${\n source.ref ? `#${source.ref}` : ''\n }' (exit=${exitCode ?? 'null'}).\\n${stderr.slice(0, 2000)}`\n );\n this.name = 'GitCloneFailedError';\n }\n}\n\nexport class GitBinaryMissingError extends MarketplaceError {\n constructor() {\n super(\n \"git executable not found on PATH. Please install git to use 'github:' sources.\"\n );\n this.name = 'GitBinaryMissingError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Git runner (注入点)\n// ---------------------------------------------------------------------------\n\n/**\n * git 调用结果(由 GitRunner 返回)。\n */\nexport interface GitRunnerResult {\n /** 进程退出码(null = killed by signal) */\n readonly exitCode: number | null;\n /** stdout 完整内容(通常用不到,保留便于 debug) */\n readonly stdout: string;\n /** stderr 完整内容(失败时用来构造错误消息) */\n readonly stderr: string;\n}\n\n/**\n * 抽象掉 `git` 子进程调用——测试里传 mock,生产走默认 `spawn` 实现。\n *\n * 约定:`cwd` 如有给就 spawn 里用;`args` 是完整 argv(不含 `git` 本身)。\n *\n * 实现必须处理 `ENOENT`(git not found)并抛 `GitBinaryMissingError`。\n */\nexport type GitRunner = (\n args: readonly string[],\n opts?: { cwd?: string }\n) => Promise<GitRunnerResult>;\n\n/**\n * 默认 git runner——spawn 真实 `git` 可执行文件。\n *\n * 测试禁止使用(会打网络 + 写 HOME),生产 CLI 用此实现。\n */\nexport const defaultGitRunner: GitRunner = async (args, opts) => {\n return await new Promise<GitRunnerResult>((resolvePromise, rejectPromise) => {\n const spawnOpts: SpawnOptions = { stdio: ['ignore', 'pipe', 'pipe'] };\n if (opts?.cwd) spawnOpts.cwd = opts.cwd;\n const proc = spawn('git', args, spawnOpts);\n let stdout = '';\n let stderr = '';\n proc.stdout?.on('data', (chunk) => {\n stdout += String(chunk);\n });\n proc.stderr?.on('data', (chunk) => {\n stderr += String(chunk);\n });\n proc.on('error', (err) => {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n rejectPromise(new GitBinaryMissingError());\n return;\n }\n rejectPromise(err);\n });\n proc.on('close', (code) => {\n resolvePromise({ exitCode: code, stdout, stderr });\n });\n });\n};\n\n// ---------------------------------------------------------------------------\n// Clone / update 逻辑\n// ---------------------------------------------------------------------------\n\nexport interface CloneOptions {\n /** 注入的 git runner(测试用 mock) */\n readonly runner?: GitRunner;\n}\n\n/**\n * 把 `github:` source 落地到指定 cache 目录,返回 ResolvedMarketplace。\n *\n * 行为:\n * - cache 目录不存在 → shallow clone 到该目录\n * - cache 目录存在但不是 git 仓库 → rm -rf 后重新 clone\n * - cache 目录存在且是 git 仓库 → fetch + checkout(update 路径)\n *\n * @throws {GitBinaryMissingError}\n * @throws {GitCloneFailedError}\n * @throws {MarketplaceCorruptError} 如果 clone 后目录里缺 `.claude-plugin/marketplace.json`\n */\nexport async function cloneGithubMarketplace(\n source: GithubMarketplaceSource,\n cacheDir: string,\n name: string,\n opts?: CloneOptions\n): Promise<ResolvedMarketplace> {\n const runner = opts?.runner ?? defaultGitRunner;\n\n const url = `https://github.com/${source.owner}/${source.repo}.git`;\n\n const existing = await detectCacheState(cacheDir);\n\n if (existing === 'missing') {\n await runClone(runner, url, cacheDir, source);\n } else if (existing === 'not-git') {\n await rm(cacheDir, { recursive: true, force: true });\n await runClone(runner, url, cacheDir, source);\n } else {\n // existing === 'git' —— update 路径\n await runUpdate(runner, cacheDir, source);\n }\n\n const manifest = await readMarketplaceJson(cacheDir);\n return {\n name,\n rootDir: resolve(cacheDir),\n source,\n manifest,\n };\n}\n\n/**\n * 仅做 update(要求 cache 目录已是 git 仓库)。\n *\n * 供 cache.ts `update()` 调用——上层已确认目录存在。\n */\nexport async function updateGithubMarketplace(\n source: GithubMarketplaceSource,\n cacheDir: string,\n name: string,\n opts?: CloneOptions\n): Promise<ResolvedMarketplace> {\n const runner = opts?.runner ?? defaultGitRunner;\n const state = await detectCacheState(cacheDir);\n if (state !== 'git') {\n // fallthrough 到 clone 逻辑,保证最终状态正确\n return cloneGithubMarketplace(source, cacheDir, name, opts);\n }\n await runUpdate(runner, cacheDir, source);\n const manifest = await readMarketplaceJson(cacheDir);\n return {\n name,\n rootDir: resolve(cacheDir),\n source,\n manifest,\n };\n}\n\n// ---------------------------------------------------------------------------\n// internals\n// ---------------------------------------------------------------------------\n\nasync function runClone(\n runner: GitRunner,\n url: string,\n cacheDir: string,\n source: GithubMarketplaceSource\n): Promise<void> {\n const args: string[] = ['clone', '--depth', '1'];\n if (source.ref) {\n args.push('--branch', source.ref);\n }\n args.push('--', url, cacheDir);\n\n const result = await runner(args);\n if (result.exitCode !== 0) {\n throw new GitCloneFailedError(source, result.exitCode, result.stderr);\n }\n}\n\nasync function runUpdate(\n runner: GitRunner,\n cacheDir: string,\n source: GithubMarketplaceSource\n): Promise<void> {\n // shallow fetch 最新 HEAD/ref\n const refArg = source.ref ?? 'HEAD';\n const fetchResult = await runner(\n ['fetch', '--depth', '1', 'origin', refArg],\n { cwd: cacheDir }\n );\n if (fetchResult.exitCode !== 0) {\n throw new GitCloneFailedError(\n source,\n fetchResult.exitCode,\n fetchResult.stderr\n );\n }\n\n // checkout 到 FETCH_HEAD 让 working tree 指向刚 fetch 的 commit\n const checkoutResult = await runner(\n ['checkout', '--quiet', '--detach', 'FETCH_HEAD'],\n { cwd: cacheDir }\n );\n if (checkoutResult.exitCode !== 0) {\n throw new GitCloneFailedError(\n source,\n checkoutResult.exitCode,\n checkoutResult.stderr\n );\n }\n}\n\nasync function detectCacheState(\n cacheDir: string\n): Promise<'missing' | 'not-git' | 'git'> {\n try {\n await access(cacheDir, fsConstants.F_OK);\n } catch {\n return 'missing';\n }\n // 判断是否 git 仓库——检查 .git(目录或文件)\n try {\n await access(resolve(cacheDir, '.git'), fsConstants.F_OK);\n return 'git';\n } catch {\n return 'not-git';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Manifest helpers re-export(方便 barrel)\n// ---------------------------------------------------------------------------\n\nexport { readMarketplaceJson };\nexport type { MarketplaceManifest };\n","/**\n * `rush-ai marketplace add <source> [--as <name>]`。\n *\n * Source of truth: `specs/cli-commands.md` §1.1\n *\n * 行为(v1 简化,详见 `./index.ts` 决策说明):\n * 1. 解析 source 字符串(`parseSource`)\n * 2. 调用 `MarketplaceCache.add(source, { as })`——落到 `~/.rush/marketplaces/<name>/`\n * - directory: 源:验证目录 + 写 meta,不 cache 拷贝\n * - github: 源:shallow clone 到 cache\n * - 其他: 抛 NotImplementedError(v1 仅 Phase 1 两家)\n * 3. **不**写 `known_marketplaces.json` / `config.toml`——延到 `plugin install`\n *\n * 退出码(对齐 spec §1.1):\n * - 0 成功(含幂等)\n * - 1 通用错误:source 无效、已存在同名不同 source、clone 失败、未实现 source\n * - 2 marketplace.json 解析失败(`MarketplaceCorruptError`)\n */\n\nimport type { Command } from 'commander';\nimport {\n defaultNameFromSource,\n InvalidSourceError,\n MarketplaceAlreadyExistsError,\n MarketplaceCache,\n MarketplaceCorruptError,\n NotImplementedError,\n parseSource,\n type ResolvedMarketplace,\n} from '../../marketplaces/index.js';\nimport { output } from '../../output/logger.js';\nimport { RushError } from '../../util/errors.js';\n\n/**\n * pure runner 的输入。测试从这里注入 home / cache 实例。\n */\nexport interface AddCommandInput {\n /** 原始 source 字符串(`directory:/abs/path` / `github:owner/repo[#ref]` ...) */\n source: string;\n /** `--as <name>`(可选) */\n as?: string;\n /** 注入自定义 home(测试用)。生产默认 `os.homedir()` */\n home?: string;\n /** 直接注入 MarketplaceCache 实例(测试 mock GitRunner 时用) */\n cache?: MarketplaceCache;\n}\n\nexport interface AddCommandResult {\n /** 最终落地的 marketplace 视图 */\n resolved: ResolvedMarketplace;\n /** 是否幂等 no-op(同 source 同名已存在) */\n idempotent: boolean;\n}\n\n/**\n * 纯函数形式的业务实现——无 console I/O,供单测 / CLI 复用。\n *\n * 失败:\n * - `RushError(code='INVALID_SOURCE', exitCode=1)` — source 格式错\n * - `RushError(code='SOURCE_NOT_IMPLEMENTED', exitCode=1)` — Phase 1 未实现的 source kind\n * - `RushError(code='MARKETPLACE_EXISTS', exitCode=1)` — 已存在同名不同 source\n * - `RushError(code='MARKETPLACE_CORRUPT', exitCode=2)` — marketplace.json 解析失败\n * - 其他错误(clone 失败、IO 错)以 RushError(exitCode=1) 透传\n */\nexport async function runAdd(\n input: AddCommandInput\n): Promise<AddCommandResult> {\n let source: ReturnType<typeof parseSource>;\n try {\n source = parseSource(input.source);\n } catch (err) {\n if (err instanceof InvalidSourceError) {\n throw new RushError(\n err.message,\n { raw: input.source },\n 'INVALID_SOURCE',\n 1\n );\n }\n throw err;\n }\n\n const cache =\n input.cache ??\n new MarketplaceCache(input.home !== undefined ? { home: input.home } : {});\n\n // 幂等 detect:add() 内部已实现\"同 source 同名 → 直接返回 existing\"\n const prior = await detectIdempotent(cache, source, input.as);\n\n try {\n const resolved = await cache.add(\n source,\n input.as !== undefined ? { as: input.as } : {}\n );\n return { resolved, idempotent: prior };\n } catch (err) {\n if (err instanceof NotImplementedError) {\n throw new RushError(\n err.message,\n { kind: err.kind },\n 'SOURCE_NOT_IMPLEMENTED',\n 1\n );\n }\n if (err instanceof MarketplaceAlreadyExistsError) {\n throw new RushError(\n err.message,\n {\n marketplaceName: err.marketplaceName,\n existingSource: err.existingSourceRaw,\n requestedSource: err.requestedSourceRaw,\n },\n 'MARKETPLACE_EXISTS',\n 1\n );\n }\n if (err instanceof MarketplaceCorruptError) {\n // spec §1.1 退出码 2\n throw new RushError(\n err.message,\n { filePath: err.filePath, reason: err.reason },\n 'MARKETPLACE_CORRUPT',\n 2\n );\n }\n if (err instanceof Error) {\n throw new RushError(err.message, {}, 'MARKETPLACE_ADD_FAILED', 1);\n }\n throw err;\n }\n}\n\n/** 判断本次 add 是否会触发幂等(仅当同名且同 source 已存在) */\nasync function detectIdempotent(\n cache: MarketplaceCache,\n source: ReturnType<typeof parseSource>,\n as: string | undefined\n): Promise<boolean> {\n const name = as ?? safeDefaultName(source);\n if (name === null) return false;\n try {\n if (!(await cache.has(name))) return false;\n const existing = await cache.get(name);\n return existing.source.raw === source.raw;\n } catch {\n // has/get 抛错(比如 meta 损坏)当作\"非幂等\"让 add 主流程处理真正的错\n return false;\n }\n}\n\n/** 不抛错的 defaultNameFromSource;失败返回 null。 */\nfunction safeDefaultName(\n source: ReturnType<typeof parseSource>\n): string | null {\n try {\n return defaultNameFromSource(source);\n } catch {\n return null;\n }\n}\n\n// -- commander wrapper --------------------------------------------------------\n\nexport function registerAddCommand(group: Command, _root: Command): void {\n group\n .command('add')\n .description(\n \"Register a marketplace source (e.g. 'github:owner/repo', 'directory:/abs/path')\"\n )\n .argument('<source>', 'marketplace source string')\n .option('--as <name>', 'override auto-derived marketplace name')\n .action(async (source: string, opts: { as?: string }) => {\n const result = await runAdd({\n source,\n ...(opts.as !== undefined ? { as: opts.as } : {}),\n });\n\n // 输出(stderr 为主;生产 stdout 保留给 JSON 等可管道数据,但本命令\n // 目前不走结构化 stdout,用 status 行提示即可)\n if (result.idempotent) {\n output.dim(\n `Marketplace '${result.resolved.name}' already registered with the same source; no change.`\n );\n } else {\n output.success(\n `Added marketplace '${result.resolved.name}' (from ${result.resolved.source.raw})`\n );\n output.dim(` → Cached at ${result.resolved.rootDir}`);\n output.dim(\n ' ⓘ IDE marketplace registration deferred to `rush-ai plugin install`.'\n );\n }\n });\n}\n","/**\n * rush-ai Registry 读-改-写实现(~/.rush/plugins/registry.json)。\n *\n * Source of truth: `specs/installer-interface.md` §3。\n *\n * 关键契约(spec §3.3):\n * - 所有写入走 write-temp-rename 原子替换\n * - 读前 `fs.stat` 拿 mtime;写前比较 mtime,冲突 → retry 一次,再变报错\n * - 文件不存在视为空 registry\n * - JSON 解析失败不尝试修复,直接抛错\n * - schemaVersion 不匹配只读不写,抛错让 CLI 层提示升级\n *\n * 本模块职责边界(spec §4 / plan §10):\n * - **只有 CLI 层**调用本模块写 registry,Installer 不写\n * - 一个 CLI 进程内可以多次 save(幂等),但并发进程间靠 mtime 检测\n *\n * 命名说明:spec §3.4 的示意 class 名为 `RushRegistry`,但 types.ts 已\n * `export interface RushRegistry` 作为数据结构。为避免同名冲突,这里的\n * class 命名为 `RushRegistryStore`(Store = 数据的 owner / 读写入口),\n * 语义上等价于 spec §3.4 的 class,方法签名与 spec 保持一致。\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { constants as fsConstants } from 'node:fs';\nimport {\n access,\n mkdir,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, resolve } from 'node:path';\nimport type {\n MigrationRecord,\n PluginRef,\n PluginRefString,\n RushRegistry as RushRegistryData,\n RushRegistryEntry,\n} from './types.js';\n\n/**\n * 默认 registry 文件路径(`~/.rush/plugins/registry.json`)。\n *\n * 测试必须通过 `{ home }` 注入临时目录,**禁止**写用户真实 `~/.rush/`。\n */\nexport const REGISTRY_RELATIVE_PATH = '.rush/plugins/registry.json' as const;\n\n/**\n * 当前 schema 版本。升级时 bump 这里 + 在 `load()` 里加迁移分支。\n */\nexport const REGISTRY_SCHEMA_VERSION = 1 as const;\n\n// ---------------------------------------------------------------------------\n// Error 类型\n// ---------------------------------------------------------------------------\n\n/** 基类 —— 便于 CLI 统一 `instanceof RushRegistryError` catch。 */\nexport class RushRegistryError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'RushRegistryError';\n }\n}\n\n/**\n * JSON 损坏 / 结构不合法时抛出。spec §3.3:不尝试修复,请用户手动处理。\n */\nexport class RushRegistryCorruptError extends RushRegistryError {\n constructor(\n public readonly filePath: string,\n public readonly cause: unknown\n ) {\n super(\n `rush-ai registry.json 解析失败(${filePath})。请备份后删除或手工修复。原因:${String(\n (cause as Error | undefined)?.message ?? cause\n )}`\n );\n this.name = 'RushRegistryCorruptError';\n }\n}\n\n/**\n * 读到旧版本 schema —— 只读不写,CLI 层应提示升级 rush-ai。\n */\nexport class RushRegistrySchemaMismatchError extends RushRegistryError {\n constructor(\n public readonly filePath: string,\n public readonly foundVersion: number,\n public readonly expectedVersion: number\n ) {\n super(\n `rush-ai registry schemaVersion=${foundVersion},当前 rush-ai 仅支持 schemaVersion=${expectedVersion}。请升级 rush-ai 或备份后迁移。`\n );\n this.name = 'RushRegistrySchemaMismatchError';\n }\n}\n\n/**\n * 并发写冲突(mtime 在读后发生了变化,即便 retry 一次也没稳定)。\n * CLI 层提示用户重试。\n */\nexport class RushRegistryConflictError extends RushRegistryError {\n constructor(public readonly filePath: string) {\n super(\n `rush-ai registry 并发写冲突:另一个进程在我们读-改-写期间修改了 ${filePath}。请重试。`\n );\n this.name = 'RushRegistryConflictError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** PluginRef ↔ key 互转。spec §1.1 约定 `name@marketplace`。 */\nexport function pluginRefToKey(ref: PluginRef): PluginRefString {\n return `${ref.name}@${ref.marketplace}` as PluginRefString;\n}\n\n/**\n * 从 `name@marketplace` 字符串解析出 PluginRef。仅内部使用(list 方法)。\n *\n * 注意:marketplace 自身不允许再含 `@`(避免歧义)。只按**第一个** `@` 劈。\n */\nfunction keyToPluginRef(key: PluginRefString): PluginRef {\n const at = key.indexOf('@');\n if (at <= 0 || at === key.length - 1) {\n throw new RushRegistryError(\n `registry 键 \"${key}\" 非法(要求 \\`name@marketplace\\`,name/marketplace 非空)`\n );\n }\n return { name: key.slice(0, at), marketplace: key.slice(at + 1) };\n}\n\n/**\n * 判断 raw JSON 是否符合我们期望的顶层 shape。不做 deep 校验(每个 entry 的\n * 字段类型由 InstallResult 生产方保证),只做 `schemaVersion` + `plugins` 壳子。\n */\nfunction assertRegistryShape(\n raw: unknown,\n filePath: string\n): asserts raw is RushRegistryData {\n if (!raw || typeof raw !== 'object') {\n throw new RushRegistryCorruptError(\n filePath,\n new Error('registry root 必须是 object')\n );\n }\n const obj = raw as Record<string, unknown>;\n if (typeof obj.schemaVersion !== 'number') {\n throw new RushRegistryCorruptError(\n filePath,\n new Error('registry.schemaVersion 必须是数字')\n );\n }\n if (\n obj.plugins !== undefined &&\n (typeof obj.plugins !== 'object' ||\n Array.isArray(obj.plugins) ||\n obj.plugins === null)\n ) {\n throw new RushRegistryCorruptError(\n filePath,\n new Error('registry.plugins 必须是 object')\n );\n }\n if (\n obj.migrations !== undefined &&\n (typeof obj.migrations !== 'object' ||\n Array.isArray(obj.migrations) ||\n obj.migrations === null)\n ) {\n throw new RushRegistryCorruptError(\n filePath,\n new Error('registry.migrations 必须是 object(如存在)')\n );\n }\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p, fsConstants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * write-temp-rename 原子替换。`rename` 在 POSIX 同一文件系统里是原子的。\n *\n * tmp 文件名用 uuid 避免并发冲突(即便同进程多次 save 也安全)。\n * 失败时 best-effort 清掉 tmp,不覆盖原始错误。\n */\nasync function atomicWrite(filePath: string, content: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n const tmp = `${filePath}.${randomUUID()}.tmp`;\n try {\n await writeFile(tmp, content, { encoding: 'utf8', flag: 'w' });\n await rename(tmp, filePath);\n } catch (err) {\n // best-effort cleanup;吞掉 cleanup 错误,保留原始错误\n await rm(tmp, { force: true }).catch(() => {});\n throw err;\n }\n}\n\n/**\n * 从给定路径读 registry 文件。抽出便于单元测试和 `reloadAndMerge` 复用。\n *\n * 不处理 HOME 解析——调用方负责传绝对路径。\n *\n * schemaVersion 不匹配时**不抛错**——load 仍返回解析后的数据,但把\n * `schemaVersion` 原样保留(本 Phase 1 仅支持 1)。save 时会在此基础上检测并\n * 拒绝写入,对齐 spec §3.3 \"读旧版但不写入\"。\n */\nasync function loadFromPath(\n filePath: string\n): Promise<{ data: RushRegistryData; mtimeMs: number | null }> {\n if (!(await pathExists(filePath))) {\n return {\n data: { schemaVersion: REGISTRY_SCHEMA_VERSION, plugins: {} },\n mtimeMs: null,\n };\n }\n\n const stats = await stat(filePath);\n const raw = await readFile(filePath, 'utf8');\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new RushRegistryCorruptError(filePath, err);\n }\n\n assertRegistryShape(parsed, filePath);\n\n // 注意:schemaVersion 不匹配**不**在此处抛错。按 spec §3.3 语义:\n // \"读旧版但不写入,退出时提示用户升级 rush-ai\"。save 里再检测。\n //\n // 这样下游 CLI 即便遇到旧 schema 也可以列出 plugins 做展示,只是写路径会报错。\n const data: RushRegistryData = {\n // 类型标注为 1,但运行时可能存储 parsed.schemaVersion(不同值)——\n // save 时会用 REGISTRY_SCHEMA_VERSION 常量比对。\n schemaVersion: parsed.schemaVersion as RushRegistryData['schemaVersion'],\n plugins: parsed.plugins ?? {},\n };\n if (parsed.migrations !== undefined) {\n data.migrations = parsed.migrations;\n }\n\n return { data, mtimeMs: stats.mtimeMs };\n}\n\n// ---------------------------------------------------------------------------\n// RushRegistryStore —— spec §3.4 的 class 实现\n// ---------------------------------------------------------------------------\n\nexport interface RushRegistryStoreOptions {\n /**\n * 注入的 HOME 目录(仅测试用)。生产代码传 `undefined`,默认走 `os.homedir()`。\n *\n * **测试铁律**:所有使用本 store 的单测都必须通过此参数注入 `os.tmpdir()` 下\n * 的临时目录,禁止写用户真实 `~/.rush/`。\n */\n home?: string;\n}\n\n/**\n * rush-ai registry(账本)的读-改-写入口。\n *\n * 使用模式:\n * ```ts\n * const store = await RushRegistryStore.load();\n * store.set(ref, entry);\n * await store.save();\n * ```\n *\n * 并发安全:`load()` 记录文件 mtime;`save()` 写入前比较 mtime,变了 → reload\n * 合并我们的 diff 重试一次;再变则抛 `RushRegistryConflictError`。\n */\nexport class RushRegistryStore {\n /**\n * Registry 文件的绝对路径(例如 `/Users/x/.rush/plugins/registry.json`)。\n */\n public readonly filePath: string;\n\n /** 内存中的数据视图。直接修改后调用 `save()` 持久化。 */\n private data: RushRegistryData;\n\n /**\n * load 时记录的文件 mtime(ms)。null 代表文件当时不存在。save 前会比对。\n */\n private loadedMtimeMs: number | null;\n\n /**\n * 本进程显式 delete 的 plugin keys(delete tombstone)。\n *\n * 并发冲突 reload+merge 时需要把这些 key 从 disk 副本里移除,否则 merge 会\n * \"复活\" B 进程留下的旧条目(违反 read-modify-write 语义)。\n *\n * set 时 tombstone 自动清除(先 delete 再 set 视为复活该条目)。\n */\n private deletedPluginKeys: Set<PluginRefString> = new Set();\n\n private constructor(\n filePath: string,\n data: RushRegistryData,\n loadedMtimeMs: number | null\n ) {\n this.filePath = filePath;\n this.data = data;\n this.loadedMtimeMs = loadedMtimeMs;\n }\n\n /**\n * 计算 registry 文件路径(根据 `home` 注入或默认 `os.homedir()`)。\n */\n static resolvePath(opts?: RushRegistryStoreOptions): string {\n const home = opts?.home ?? homedir();\n return resolve(home, REGISTRY_RELATIVE_PATH);\n }\n\n /**\n * 读取 registry 文件。\n *\n * - 文件不存在 → 返回空 registry(`{schemaVersion: 1, plugins: {}}`),`loadedMtimeMs = null`\n * - JSON 损坏 / 顶层 shape 错 → `RushRegistryCorruptError`\n * - schemaVersion 不等于 `REGISTRY_SCHEMA_VERSION` → **不抛**,原样读入内存\n * (对齐 spec §3.3 \"读旧版但不写入\")。后续调用 `save()` 时才抛\n * `RushRegistrySchemaMismatchError`。\n * - schemaVersion 为本支持版本 → 正常返回\n */\n static async load(\n opts?: RushRegistryStoreOptions\n ): Promise<RushRegistryStore> {\n const filePath = RushRegistryStore.resolvePath(opts);\n const { data, mtimeMs } = await loadFromPath(filePath);\n return new RushRegistryStore(filePath, data, mtimeMs);\n }\n\n /**\n * 原子写入当前内存状态到磁盘。\n *\n * mtime 冲突检测(spec §3.3):\n * 1. 写前 stat 文件,对比 `loadedMtimeMs`\n * 2. 不匹配 → retry 一次:reload 最新磁盘状态、把内存 diff re-apply 再 save\n * 3. 再不匹配 → `RushRegistryConflictError`\n *\n * diff re-apply 策略:将当前内存里的 plugins/migrations 条目在最新磁盘版本上 merge\n * (**内存里的条目覆盖磁盘**——本进程是唯一 writer 的场景下这是正确的;并发\n * writer 场景 coordinator 明确 spec §4 \"只有 CLI 层写\" 不允许,靠异常兜底)。\n */\n async save(): Promise<void> {\n // spec §3.3:schemaVersion 不匹配 → 读旧版但不写入。\n // load 阶段已经允许把旧 schema 读进内存,save 时在每个写盘点前都要检查一次\n // (reloadAndMerge 可能把 schemaVersion 从 disk 替换进来,必须重新把关)。\n this.assertWriteableSchema();\n await this.saveWithRetry(0);\n }\n\n /**\n * 断言当前内存 schemaVersion 是本 CLI 能写入的版本。\n *\n * 任何写盘点前都应调用——包括 reloadAndMerge 之后(避免\"A load 时是 v1,B 抢\n * 写 v999,A 重试时用 B 的 v999 写盘\"的路径)。\n */\n private assertWriteableSchema(): void {\n if (this.data.schemaVersion !== REGISTRY_SCHEMA_VERSION) {\n throw new RushRegistrySchemaMismatchError(\n this.filePath,\n this.data.schemaVersion as unknown as number,\n REGISTRY_SCHEMA_VERSION\n );\n }\n }\n\n private async saveWithRetry(attempt: number): Promise<void> {\n // reloadAndMerge 可能已把磁盘上更高 schemaVersion 塞进 this.data,这里再把关。\n this.assertWriteableSchema();\n\n // 首次写入(文件本不存在):只检查现在是否仍不存在;存在了意味着别的进程抢写了\n if (this.loadedMtimeMs === null) {\n if (await pathExists(this.filePath)) {\n if (attempt === 0) {\n await this.reloadAndMerge();\n return this.saveWithRetry(1);\n }\n throw new RushRegistryConflictError(this.filePath);\n }\n // 正常首次写\n await atomicWrite(this.filePath, this.serialize());\n // 更新 loadedMtime 方便同一进程里继续操作\n const stats = await stat(this.filePath);\n this.loadedMtimeMs = stats.mtimeMs;\n // 写入成功 → 清空 tombstone(本批次 delete 已持久化到磁盘)\n this.deletedPluginKeys.clear();\n return;\n }\n\n // 非首次:比对 mtime\n const stats = await stat(this.filePath).catch(() => null);\n if (stats === null) {\n // 文件消失(被手工删),当作冲突(需要 reload)\n if (attempt === 0) {\n await this.reloadAndMerge();\n return this.saveWithRetry(1);\n }\n throw new RushRegistryConflictError(this.filePath);\n }\n\n if (stats.mtimeMs !== this.loadedMtimeMs) {\n if (attempt === 0) {\n await this.reloadAndMerge();\n return this.saveWithRetry(1);\n }\n throw new RushRegistryConflictError(this.filePath);\n }\n\n await atomicWrite(this.filePath, this.serialize());\n const afterStats = await stat(this.filePath);\n this.loadedMtimeMs = afterStats.mtimeMs;\n // 写入成功 → 清空 tombstone\n this.deletedPluginKeys.clear();\n }\n\n /**\n * 磁盘被别人改了 → 把别人的改动 load 进来,再把本进程的内存改动 merge 上去。\n *\n * merge 规则:\n * - 磁盘条目作为 base\n * - 本进程的 delete tombstone(`deletedPluginKeys` / `deletedMigrationKeys`)\n * 从 base 里移除对应 key(避免磁盘上 B 进程的旧条目被复活)\n * - 本进程内存里的 plugins/migrations 条目覆盖同 key(ours wins)\n *\n * 注意:直接读 `this.filePath`,不走 `load()` 的 HOME resolution,避免二次路径计算。\n */\n private async reloadAndMerge(): Promise<void> {\n const ourPlugins = this.data.plugins;\n const ourMigrations = this.data.migrations;\n const ourDeletedPlugins = this.deletedPluginKeys;\n\n const disk = await loadFromPath(this.filePath);\n\n // 1. 从 disk 副本移除我们主动 delete 的 key(避免复活)\n const mergedPlugins: Record<string, RushRegistryEntry> = {};\n for (const [key, entry] of Object.entries(disk.data.plugins)) {\n if (!ourDeletedPlugins.has(key as PluginRefString)) {\n mergedPlugins[key] = entry;\n }\n }\n // 2. 叠加 ours(同 key 覆盖)\n for (const [key, entry] of Object.entries(ourPlugins)) {\n mergedPlugins[key] = entry;\n }\n\n // migrations append-only 语义(本 spec 范围内没有 deleteMigration API),\n // 直接 disk + ours 合并;ours 覆盖同 key。\n let mergedMigrations: RushRegistryData['migrations'] | undefined;\n const hasAnyMigration =\n (ourMigrations && Object.keys(ourMigrations).length > 0) ||\n (disk.data.migrations && Object.keys(disk.data.migrations).length > 0);\n if (hasAnyMigration) {\n mergedMigrations = {\n ...(disk.data.migrations ?? {}),\n ...(ourMigrations ?? {}),\n };\n }\n\n this.data = {\n schemaVersion: disk.data.schemaVersion,\n plugins: mergedPlugins,\n ...(mergedMigrations ? { migrations: mergedMigrations } : {}),\n };\n this.loadedMtimeMs = disk.mtimeMs;\n // tombstone 已被 apply,save 成功后由 saveWithRetry 末尾清空\n }\n\n /** 序列化成 JSON 字符串(2-space 缩进,便于人工审查)。 */\n private serialize(): string {\n // 稳定输出:plugins / migrations key 按字典序\n const plugins = Object.fromEntries(\n Object.entries(this.data.plugins).sort(([a], [b]) =>\n a < b ? -1 : a > b ? 1 : 0\n )\n );\n const out: RushRegistryData = {\n schemaVersion: this.data.schemaVersion,\n plugins,\n };\n if (this.data.migrations) {\n out.migrations = Object.fromEntries(\n Object.entries(this.data.migrations).sort(([a], [b]) =>\n a < b ? -1 : a > b ? 1 : 0\n )\n );\n }\n return `${JSON.stringify(out, null, 2)}\\n`;\n }\n\n // -------------------------------------------------------------------------\n // spec §3.4 的 API\n // -------------------------------------------------------------------------\n\n /** 获取某插件的 registry 条目。未安装返回 undefined。 */\n get(ref: PluginRef): RushRegistryEntry | undefined {\n return this.data.plugins[pluginRefToKey(ref)];\n }\n\n /**\n * 设置某插件的 registry 条目(覆盖或新增)。\n *\n * 若该 key 曾被 `delete()` 打过 tombstone,本次 set 视为\"复活\"——清除 tombstone,\n * 以免 reload+merge 时把 disk 上同 key 条目和本次 set 都清掉。\n */\n set(ref: PluginRef, entry: RushRegistryEntry): void {\n const key = pluginRefToKey(ref);\n this.data.plugins[key] = entry;\n this.deletedPluginKeys.delete(key);\n }\n\n /**\n * 删除某插件的 registry 条目(不存在是 no-op)。\n *\n * 同时把 key 加入 `deletedPluginKeys` tombstone 集合——保证并发 reload+merge\n * 时不会被 disk 上其它进程留下的同 key 旧条目\"复活\"。\n */\n delete(ref: PluginRef): void {\n const key = pluginRefToKey(ref);\n delete this.data.plugins[key];\n this.deletedPluginKeys.add(key);\n }\n\n /** 列出所有已记录插件,顺序按 key 字典序。 */\n list(): Array<[PluginRef, RushRegistryEntry]> {\n return Object.entries(this.data.plugins)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([key, entry]) => [keyToPluginRef(key as PluginRefString), entry]);\n }\n\n /**\n * 读取迁移记录(M4 task-12 使用)。spec §3.2 允许 migrations 缺省。\n */\n getMigration(key: string): MigrationRecord | undefined {\n return this.data.migrations?.[key];\n }\n\n /**\n * 写迁移记录。若 `migrations` 字段尚未初始化则自动创建。\n */\n setMigration(key: string, record: MigrationRecord): void {\n if (!this.data.migrations) {\n this.data.migrations = {};\n }\n this.data.migrations[key] = record;\n }\n\n /**\n * 当前内存快照(deep-clone,便于测试断言或 debug dump)。\n * 修改返回值不影响内部状态。\n */\n snapshot(): RushRegistryData {\n return structuredClone(this.data);\n }\n}\n","/**\n * `rush-ai marketplace list`。\n *\n * Source of truth: `specs/cli-commands.md` §1.3\n *\n * 输出(人看模式):\n * ```\n * NAME SOURCE INSTALLED PLUGINS\n * rush-marketplace github:kanyun/rush-plugins 3 (rush, yuanli-infra, octo)\n * official directory:/abs/path (empty)\n * ```\n *\n * 数据源:\n * - NAME / SOURCE:`MarketplaceCache.list()`\n * - INSTALLED PLUGINS:`RushRegistryStore` 里按 `ref.marketplace === name` 聚合;\n * registry 还没记录时显示 `(empty)`\n *\n * JSON 模式(`--json`):\n * ```json\n * { \"marketplaces\": [{ \"name\": \"...\", \"source\": \"...\", \"installedPlugins\": [\"...\"] }] }\n * ```\n */\n\nimport type { Command } from 'commander';\nimport { RushRegistryStore } from '../../installers/registry.js';\nimport { MarketplaceCache } from '../../marketplaces/index.js';\nimport { formatOutput, resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\n\nexport interface ListCommandInput {\n home?: string;\n cache?: MarketplaceCache;\n registry?: RushRegistryStore;\n}\n\nexport interface ListEntry {\n name: string;\n /** raw source 字符串(如 `github:owner/repo`) */\n source: string;\n /** 已装插件名列表(仅 name,不含 `@mkt` 后缀——mkt 已经在外层 NAME 列) */\n installedPlugins: string[];\n}\n\nexport interface ListCommandResult {\n marketplaces: ListEntry[];\n}\n\n/**\n * 纯函数实现。空 cache + 空 registry → 返回 `{ marketplaces: [] }`(不报错)。\n */\nexport async function runList(\n input: ListCommandInput = {}\n): Promise<ListCommandResult> {\n const homeOpts = input.home !== undefined ? { home: input.home } : {};\n const cache = input.cache ?? new MarketplaceCache(homeOpts);\n const registry = input.registry ?? (await RushRegistryStore.load(homeOpts));\n\n const cached = await cache.list();\n\n // 聚合 registry 按 marketplace 分组\n const byMkt = new Map<string, string[]>();\n for (const [ref] of registry.list()) {\n const arr = byMkt.get(ref.marketplace) ?? [];\n arr.push(ref.name);\n byMkt.set(ref.marketplace, arr);\n }\n\n const marketplaces: ListEntry[] = cached.map((entry) => ({\n name: entry.name,\n source: entry.source.raw,\n installedPlugins: byMkt.get(entry.name) ?? [],\n }));\n\n return { marketplaces };\n}\n\n// -- commander wrapper --------------------------------------------------------\n\nexport function registerListCommand(group: Command, root: Command): void {\n group\n .command('list')\n .alias('ls')\n .description(\n 'List locally registered marketplaces and their installed plugins'\n )\n .action(async () => {\n const format = resolveFormat(root.opts());\n const result = await runList();\n\n if (format === 'json') {\n output.log(JSON.stringify(result, null, 2));\n return;\n }\n\n if (result.marketplaces.length === 0) {\n output.log('(no marketplaces registered)');\n output.dim(\n \" Add one with 'rush-ai marketplace add <source>' (e.g. github:owner/repo)\"\n );\n return;\n }\n\n const rows = result.marketplaces.map((m) => ({\n NAME: m.name,\n SOURCE: m.source,\n 'INSTALLED PLUGINS':\n m.installedPlugins.length === 0\n ? '(empty)'\n : `${m.installedPlugins.length} (${m.installedPlugins.join(', ')})`,\n }));\n output.log(formatOutput(rows, format));\n });\n}\n","/**\n * `rush-ai marketplace remove <name>`。\n *\n * Source of truth: `specs/cli-commands.md` §1.2\n *\n * 行为(v1 简化,见 `./index.ts` 决策说明):\n * 1. 检查 rush-ai registry(`~/.rush/plugins/registry.json`)里是否仍有插件装自该 marketplace\n * - 有 → **拒绝删除**,提示先 `plugin uninstall`\n * - 无 → 进入步骤 2\n * 2. 调用 `MarketplaceCache.remove(name)`——从 `~/.rush/marketplaces/<name>/` 清掉 cache\n * - directory: source:只删 meta 目录,不触碰用户 path\n * - github: source:删整个 cache 目录\n * - 不存在:no-op(静默成功;CLI 层单独提示)\n * 3. **不**清 `known_marketplaces.json` / `config.toml`——由 `plugin uninstall` 时各 Installer\n * 自行维护(v1 简化)。\n *\n * 退出码:\n * - 0 成功(含 no-op)\n * - 1 通用错误:仍有插件装自该 marketplace、IO 错\n */\n\nimport type { Command } from 'commander';\nimport {\n pluginRefToKey,\n RushRegistryStore,\n} from '../../installers/registry.js';\nimport { MarketplaceCache } from '../../marketplaces/index.js';\nimport { output } from '../../output/logger.js';\nimport { RushError } from '../../util/errors.js';\n\nexport interface RemoveCommandInput {\n /** marketplace 名 */\n name: string;\n /** 注入自定义 home(测试用) */\n home?: string;\n /** 直接注入 cache 实例 */\n cache?: MarketplaceCache;\n /** 直接注入 registry store(测试可 pre-seed 插件) */\n registry?: RushRegistryStore;\n /**\n * `--force`:即使 registry 里还有插件装自该 marketplace,也强删(不推荐;\n * 仅供用户应急恢复损坏状态)。对齐 spec 把失败做成主路径 + 明确 flag 绕过。\n */\n force?: boolean;\n}\n\nexport interface RemoveCommandResult {\n /** 本次是否真的有东西可删(cache 目录本来就不在 → noOp=true) */\n noOp: boolean;\n /** 若强删且 registry 里有残留插件,列出来给用户看(CLI 可展示警告) */\n orphanedPlugins: string[];\n}\n\n/**\n * 纯函数实现。\n *\n * 失败:\n * - `RushError(code='MARKETPLACE_IN_USE', exitCode=1)` — 有插件装自该 marketplace,拒绝删\n * - 其他 IO 错:以 RushError(exitCode=1) 透传\n */\nexport async function runRemove(\n input: RemoveCommandInput\n): Promise<RemoveCommandResult> {\n const homeOpts = input.home !== undefined ? { home: input.home } : {};\n const cache = input.cache ?? new MarketplaceCache(homeOpts);\n const registry = input.registry ?? (await RushRegistryStore.load(homeOpts));\n\n // 找出所有装自此 marketplace 的插件\n const dependents = registry\n .list()\n .filter(([ref]) => ref.marketplace === input.name);\n const dependentKeys = dependents.map(([ref]) => pluginRefToKey(ref));\n\n if (dependents.length > 0 && !input.force) {\n throw new RushError(\n `Cannot remove marketplace '${input.name}': ${\n dependents.length\n } installed plugin(s) still depend on it (${dependentKeys.join(\n ', '\n )}). Run 'rush-ai plugin uninstall <name>@${input.name}' for each before removing the marketplace, or pass --force to ignore.`,\n {\n marketplaceName: input.name,\n dependents: dependentKeys,\n },\n 'MARKETPLACE_IN_USE',\n 1\n );\n }\n\n const exists = await cache.has(input.name).catch(() => false);\n await cache.remove(input.name);\n\n return {\n noOp: !exists,\n orphanedPlugins: dependentKeys,\n };\n}\n\n// -- commander wrapper --------------------------------------------------------\n\nexport function registerRemoveCommand(group: Command, _root: Command): void {\n group\n .command('remove')\n .description('Remove a marketplace from the local cache')\n .argument('<name>', 'marketplace name')\n .option(\n '--force',\n 'remove even when installed plugins still depend on this marketplace'\n )\n .action(async (name: string, opts: { force?: boolean }) => {\n const result = await runRemove({\n name,\n ...(opts.force ? { force: true } : {}),\n });\n\n if (result.noOp) {\n output.warn(\n `Marketplace '${name}' was not in the local cache; nothing to remove.`\n );\n } else {\n output.success(`Removed marketplace '${name}' from local cache.`);\n }\n\n // Orphan 警告在 noOp / 非 noOp 两种路径下都要展示——用户即使\"什么都没删\",\n // 只要 registry 里仍有 stale 插件条目装自此 mkt,也需要被明确引导去清理。\n if (result.orphanedPlugins.length > 0) {\n output.warn(\n `${result.orphanedPlugins.length} plugin(s) orphaned in registry (removed with --force): ${result.orphanedPlugins.join(', ')}`\n );\n output.dim(\n \" Run 'rush-ai plugin uninstall <name>@<mkt>' for each orphan to clean registry.\"\n );\n }\n if (!result.noOp) {\n output.dim(\n ' ⓘ IDE marketplace deregistration deferred to `rush-ai plugin uninstall`.'\n );\n }\n });\n}\n","/**\n * `rush-ai marketplace update [<name>]`。\n *\n * Source of truth: `specs/cli-commands.md` §1.4\n *\n * 行为:\n * - 省略 `<name>` → 更新 cache 里所有 marketplace\n * - 带 `<name>` → 只更这一个\n * - directory: source:no-op(源直接引用)\n * - github: source:`git pull` / re-clone(由 `MarketplaceCache.update` 实现)\n * - 未找到的 name:抛 `RushError(code='MARKETPLACE_NOT_FOUND', exitCode=1)`\n *\n * **不**自动重装插件(spec §1.4 语义,重装走 `plugin update`)。\n */\n\nimport type { Command } from 'commander';\nimport {\n MarketplaceCache,\n MarketplaceNotFoundError,\n NotImplementedError,\n} from '../../marketplaces/index.js';\nimport { output } from '../../output/logger.js';\nimport { RushError } from '../../util/errors.js';\n\nexport interface UpdateCommandInput {\n /** 指定 marketplace 名;undefined → 更新全部 */\n name?: string;\n home?: string;\n cache?: MarketplaceCache;\n /**\n * `--dry-run`:不写磁盘,只返回\"会 update 哪些 marketplace\"。\n * spec §3.5 要求 `marketplace update` 支持该 flag。\n */\n dryRun?: boolean;\n}\n\nexport interface UpdateEntry {\n name: string;\n /** 被跳过(directory: source 或异常可接受跳过) */\n skipped: boolean;\n /** 本条是 dry-run 模式下的计划(不代表已执行) */\n dryRun?: boolean;\n /** 失败原因(如 git pull 失败) */\n error?: string;\n}\n\nexport interface UpdateCommandResult {\n results: UpdateEntry[];\n}\n\n/**\n * 纯函数实现。\n *\n * - 单 name 找不到 → `RushError(code='MARKETPLACE_NOT_FOUND', exitCode=1)`\n * - 全量 update 时某个失败 → 不中断,继续更下一个;结果在 `results[i].error` 里\n * - directory: source → skipped=true\n * - 未实现 source (git:/rush://npm:) → skipped=true + error 说明\n */\nexport async function runUpdate(\n input: UpdateCommandInput = {}\n): Promise<UpdateCommandResult> {\n const homeOpts = input.home !== undefined ? { home: input.home } : {};\n const cache = input.cache ?? new MarketplaceCache(homeOpts);\n\n const targets: string[] = [];\n if (input.name) {\n if (!(await cache.has(input.name))) {\n throw new RushError(\n `Marketplace '${input.name}' is not in the local cache. Run 'rush-ai marketplace list' to see registered marketplaces.`,\n { marketplaceName: input.name },\n 'MARKETPLACE_NOT_FOUND',\n 1\n );\n }\n targets.push(input.name);\n } else {\n const all = await cache.list();\n for (const entry of all) targets.push(entry.name);\n }\n\n const results: UpdateEntry[] = [];\n for (const name of targets) {\n try {\n // 先看 source kind 决定要不要 update(避免 directory 的 no-op 被当作\"真更新\")\n const existing = await cache.get(name);\n if (existing.source.kind === 'directory') {\n results.push({\n name,\n skipped: true,\n ...(input.dryRun ? { dryRun: true } : {}),\n });\n continue;\n }\n if (input.dryRun) {\n // dry-run:不写磁盘,只标记\"会 update\"。退出码仍按真跑一致(无错即 0)。\n results.push({ name, skipped: false, dryRun: true });\n continue;\n }\n await cache.update(name);\n results.push({ name, skipped: false });\n } catch (err) {\n if (err instanceof MarketplaceNotFoundError) {\n // 理论上 has() 已挡;保留以防竞态\n results.push({\n name,\n skipped: false,\n error: `not found (was it removed concurrently?)`,\n });\n continue;\n }\n if (err instanceof NotImplementedError) {\n results.push({\n name,\n skipped: true,\n error: err.message,\n });\n continue;\n }\n const message = err instanceof Error ? err.message : String(err);\n results.push({ name, skipped: false, error: message });\n }\n }\n\n return { results };\n}\n\n// -- commander wrapper --------------------------------------------------------\n\nexport function registerUpdateCommand(group: Command, _root: Command): void {\n group\n .command('update')\n .description(\n 'Refresh local marketplace cache (git pull for github: sources; directory: is a no-op)'\n )\n .argument('[name]', 'marketplace name; omit to update all')\n .option(\n '--dry-run',\n \"don't touch disk; print the marketplaces that would be updated\"\n )\n .action(async (name: string | undefined, opts: { dryRun?: boolean }) => {\n const result = await runUpdate({\n ...(name !== undefined ? { name } : {}),\n ...(opts.dryRun ? { dryRun: true } : {}),\n });\n\n if (result.results.length === 0) {\n output.warn('No marketplaces to update.');\n output.dim(\" Add one with 'rush-ai marketplace add <source>' first.\");\n return;\n }\n\n let failed = 0;\n for (const entry of result.results) {\n if (entry.skipped) {\n output.dim(\n ` ↷ ${entry.name} skipped${entry.error ? ` (${entry.error})` : ' (no-op)'}`\n );\n } else if (entry.error) {\n output.error(` ✗ ${entry.name}: ${entry.error}`);\n failed += 1;\n } else if (entry.dryRun) {\n output.dim(` → would update ${entry.name}`);\n } else {\n output.success(`Updated ${entry.name}`);\n }\n }\n\n if (opts.dryRun) {\n output.dim(' (dry-run: nothing was written to disk)');\n }\n\n if (failed > 0) {\n // 使 exit code 非 0(通过抛 RushError 让 index.ts 统一处理)\n throw new RushError(\n `${failed} marketplace(s) failed to update; see output above for details.`,\n { failed },\n 'MARKETPLACE_UPDATE_PARTIAL',\n 1\n );\n }\n });\n}\n","/**\n * `rush-ai marketplace` 命令组入口(task-9)。\n *\n * Source of truth: `specs/cli-commands.md` §1\n *\n * 子命令:\n * - `add <source> [--as <name>]` 见 add.ts\n * - `remove <name>` 见 remove.ts\n * - `list` 见 list.ts\n * - `update [<name>]` 见 update.ts\n *\n * 设计原则:\n * - 命令 handler 只做\"参数解析 + 调用 runXxx(ctx) + 格式化输出\",业务逻辑全部在\n * pure runner 函数里实现,方便单测直接调用(不用 parseAsync)\n * - 所有磁盘操作通过 `home` 参数注入(生产默认 `os.homedir()`,测试强制传 tmp dir)\n * - **v1 简化决策**(见 progress/agent-cli-marketplace.md):marketplace add/remove/update\n * 阶段**只写 rush-ai 自己的 cache**(`~/.rush/marketplaces/`)。IDE 侧的 marketplace\n * 注册(Claude Code 的 `known_marketplaces.json`、Codex 的 `config.toml` 下\n * `[marketplaces.*]`)延到 `plugin install` 命令(task-10)中由各 Installer 统一写。\n * 原因:Installer 尚未在 task-6/7/8 实现,此处若写 IDE 配置会提前耦合未落地的接口;\n * 同时 spec §1.1 的 \"写入 known_marketplaces.json / config.toml\" 步骤与 Installer\n * 职责高度重叠,延到 install 时机处理能自然复用。\n */\n\nimport type { Command } from 'commander';\nimport { registerAddCommand } from './add.js';\nimport { registerListCommand } from './list.js';\nimport { registerRemoveCommand } from './remove.js';\nimport { registerUpdateCommand } from './update.js';\n\nexport function registerMarketplaceCommand(program: Command): void {\n const marketplace = program\n .command('marketplace')\n .description(\n 'Manage plugin marketplaces (local cache under ~/.rush/marketplaces/)'\n );\n\n registerAddCommand(marketplace, program);\n registerRemoveCommand(marketplace, program);\n registerListCommand(marketplace, program);\n registerUpdateCommand(marketplace, program);\n}\n\nexport type { AddCommandInput, AddCommandResult } from './add.js';\n// -- Re-export pure runners for integration / advanced consumers --------------\nexport { runAdd } from './add.js';\nexport type { ListCommandInput, ListCommandResult, ListEntry } from './list.js';\nexport { runList } from './list.js';\nexport type { RemoveCommandInput, RemoveCommandResult } from './remove.js';\nexport { runRemove } from './remove.js';\nexport type {\n UpdateCommandInput,\n UpdateCommandResult,\n UpdateEntry,\n} from './update.js';\nexport { runUpdate } from './update.js';\n","import type { Command } from 'commander';\nimport { formatOutput, resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport { createClient } from '../../util/client.js';\nimport { ApiError } from '../../util/errors.js';\n\n/** MCP server as returned by GET /api/mcp-registry */\nexport interface McpServer {\n id: string;\n name: string;\n displayName: string;\n description: string;\n transportType: string;\n category: string;\n tags: string[];\n author: string;\n tools: Array<{ name: string; description: string }> | null;\n validationStatus: {\n status: string;\n errorCode?: string;\n errorMessage?: string;\n lastCheckedAt?: string;\n } | null;\n visibility: string;\n starCount: number;\n isBuiltin: boolean;\n}\n\n/** GET /api/mcp-registry response */\ninterface McpListApiResponse {\n success: boolean;\n data: McpServer[];\n meta?: {\n pagination?: {\n currentPage: number;\n totalPages: number;\n totalItems: number;\n itemsPerPage: number;\n hasNext: boolean;\n hasPrevious: boolean;\n };\n };\n}\n\n/** GET /api/mcp-registry/[id] response */\ninterface McpDetailApiResponse {\n success: boolean;\n data: McpServer;\n}\n\nfunction formatValidationStatus(vs: McpServer['validationStatus']): string {\n if (!vs) return '-';\n switch (vs.status) {\n case 'verified':\n return '✓ verified';\n case 'pending':\n return '… pending';\n case 'credential_required':\n return '⚠ credential required';\n case 'failed':\n return `✗ ${vs.errorCode || 'failed'}`;\n default:\n return vs.status;\n }\n}\n\nfunction truncate(str: string, max: number): string {\n if (str.length <= max) return str;\n return `${str.slice(0, max - 3)}...`;\n}\n\nexport function registerMcpCommand(program: Command): void {\n const mcp = program\n .command('mcp')\n .description('MCP server and platform MCP discovery');\n\n mcp\n .command('serve')\n .description('Start the MCP stdio server')\n .action(async () => {\n const { startMcpServer } = await import('./server.js');\n await startMcpServer();\n });\n\n mcp\n .command('list')\n .alias('ls')\n .description('List available MCP servers on the platform')\n .option('-c, --category <category>', 'Filter by category')\n .option('-t, --tag <tag>', 'Filter by tag')\n .option('-s, --search <query>', 'Search by name or description')\n .option('--transport <type>', 'Filter by transport type (stdio|sse|http)')\n .option(\n '-l, --limit <number>',\n 'Max results per page (default: 50, max: 100)',\n '50'\n )\n .option('-p, --page <number>', 'Page number (default: 1)', '1')\n .action(async (opts) => {\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n const params = new URLSearchParams();\n if (opts.category) params.set('category', opts.category);\n if (opts.tag) params.set('tag', opts.tag);\n if (opts.search) params.set('search', opts.search);\n if (opts.transport) params.set('transport', opts.transport);\n const parsedLimit = parseInt(opts.limit, 10);\n params.set(\n 'limit',\n String(\n Math.min(\n Math.max(Number.isFinite(parsedLimit) ? parsedLimit : 50, 1),\n 100\n )\n )\n );\n const parsedPage = parseInt(opts.page, 10);\n params.set(\n 'page',\n String(Math.max(Number.isFinite(parsedPage) ? parsedPage : 1, 1))\n );\n\n const { data } = await client.get<McpListApiResponse>(\n `/api/mcp-registry?${params.toString()}`\n );\n\n const servers = data.data;\n const pagination = data.meta?.pagination;\n\n if (format === 'json') {\n output.log(JSON.stringify(data, null, 2));\n return;\n }\n\n if (servers.length === 0) {\n output.info('No MCP servers found.');\n return;\n }\n\n const totalItems = pagination?.totalItems ?? servers.length;\n output.log(output.bold(`MCP Servers (${totalItems} total):`));\n output.newline();\n\n const rows = servers.map((m) => ({\n ID: m.id,\n 'Display Name': truncate(m.displayName, 30),\n Category: m.category || '-',\n Transport: m.transportType || '-',\n Tools: String(m.tools?.length ?? 0),\n Status: formatValidationStatus(m.validationStatus),\n }));\n\n output.log(\n formatOutput(rows, format, {\n columns: { 'Display Name': { maxWidth: 30 } },\n })\n );\n\n // Show pagination hint if there are more pages\n if (pagination && pagination.hasNext) {\n output.newline();\n output.dim(\n `Page ${pagination.currentPage} of ${pagination.totalPages}. Use --page ${pagination.currentPage + 1} to see more.`\n );\n }\n });\n\n mcp\n .command('list-tools <server-id>')\n .description('List tools provided by an MCP server')\n .action(async (serverId: string) => {\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n let data: McpDetailApiResponse;\n try {\n ({ data } = await client.get<McpDetailApiResponse>(\n `/api/mcp-registry/${encodeURIComponent(serverId)}`\n ));\n } catch (err) {\n if (err instanceof ApiError && err.status === 404) {\n output.error(`MCP server '${serverId}' not found.`);\n process.exitCode = 1;\n return;\n }\n throw err;\n }\n\n const server = data.data;\n\n if (format === 'json') {\n output.log(JSON.stringify(server, null, 2));\n return;\n }\n\n // Server summary header\n output.log(output.bold(`${server.displayName} (${server.id})`));\n output.log(` Category: ${server.category || '-'}`);\n output.log(` Transport: ${server.transportType || '-'}`);\n output.log(` Author: ${server.author || '-'}`);\n output.log(\n ` Status: ${formatValidationStatus(server.validationStatus)}`\n );\n output.newline();\n\n const tools = server.tools ?? [];\n if (tools.length === 0) {\n output.info('No tools defined for this MCP server.');\n return;\n }\n\n output.log(output.bold(`Tools (${tools.length}):`));\n output.newline();\n\n const rows = tools.map((t) => ({\n 'Tool Name': t.name || '-',\n Description: truncate(t.description || '', 60),\n }));\n\n output.log(\n formatOutput(rows, format, {\n columns: { Description: { maxWidth: 60 } },\n })\n );\n });\n\n mcp\n .command('install <mcp-id>')\n .description(\n 'Install an MCP server from Rush registry to Claude Desktop / Claude Code'\n )\n .option('-y, --yes', 'Skip confirmation prompts (use defaults)')\n .option(\n '--target <targets>',\n 'Comma-separated targets: claude-desktop,claude-code (default: both)'\n )\n .option('--allow-unverified', 'Allow installing unverified MCP servers')\n .action(async (mcpId: string, opts) => {\n const format = resolveFormat(program.opts());\n const { runMcpInstall, printMcpInstallSummary } = await import(\n './install.js'\n );\n\n try {\n const result = await runMcpInstall({\n mcpId,\n yes: opts.yes,\n targetRaw: opts.target,\n allowUnverified: opts.allowUnverified,\n });\n\n const anyOk = result.targets.some((t) => t.status === 'ok');\n const anyError = result.targets.some((t) => t.status === 'error');\n if (anyError || !anyOk) process.exitCode = 1;\n\n if (format === 'json') {\n output.log(JSON.stringify(result, null, 2));\n return;\n }\n\n printMcpInstallSummary(result);\n } catch (err) {\n if (format === 'json') {\n output.log(\n JSON.stringify(\n {\n error: err instanceof Error ? err.message : String(err),\n },\n null,\n 2\n )\n );\n } else {\n output.error(err instanceof Error ? err.message : String(err));\n }\n process.exitCode = 1;\n }\n });\n\n mcp\n .command('uninstall <mcp-id>')\n .description(\n 'Remove an MCP server from Claude Desktop / Claude Code config'\n )\n .option(\n '--target <targets>',\n 'Comma-separated targets: claude-desktop,claude-code (default: both)'\n )\n .action(async (mcpId: string, opts) => {\n const format = resolveFormat(program.opts());\n const { runMcpUninstall, printMcpUninstallSummary } = await import(\n './install.js'\n );\n\n try {\n const result = await runMcpUninstall({\n mcpId,\n targetRaw: opts.target,\n });\n\n const anyError = result.targets.some((t) => t.error);\n if (anyError) process.exitCode = 1;\n\n if (format === 'json') {\n output.log(JSON.stringify(result, null, 2));\n return;\n }\n\n printMcpUninstallSummary(result);\n } catch (err) {\n if (format === 'json') {\n output.log(\n JSON.stringify(\n {\n error: err instanceof Error ? err.message : String(err),\n },\n null,\n 2\n )\n );\n } else {\n output.error(err instanceof Error ? err.message : String(err));\n }\n process.exitCode = 1;\n }\n });\n}\n","/**\n * ClaudeCodeInstaller — 原生 plugin 写入器(task-6 产物)。\n *\n * Source of truth:\n * - `specs/plugin-schemas.md` §1 Claude Code 原生 plugin 格式\n * - `specs/installer-interface.md` §1-§4 Installer 接口 + artifacts 契约\n *\n * 职责:\n * - `detect()`:检查 `<home>/.claude/` 目录是否存在\n * - `install()`:把 `ResolvedPlugin` 写入 Claude Code 原生 plugin 结构\n * - cache 目录:`<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/`\n * - plugin.json:内嵌 mcpServers(对 rush 自己的插件规范化 command 绝对路径)\n * - known_marketplaces.json:V1 marketplace 注册表\n * - installed_plugins.json:V2 多 scope 数组\n * - settings.json.enabledPlugins:布尔 true\n * - `uninstall()`:反向清理(cache 目录**保留**,和 Claude Code 原生行为一致)\n * - `isInstalled()` / `list()`:查 installed_plugins.json\n *\n * 契约不变式:\n * - 所有 JSON 写入走 atomic `writeJsonFile`(write .tmp → rename)\n * - 失败**完整回滚**:删除本次写入的 cache 目录 + 恢复 3 个 JSON 到 install 前状态\n * - `install.artifacts.files`:本次写入/更新的绝对路径清单(含 cache dir + 3 个 JSON)\n * - `install.artifacts.mcpKeys`:plugin.json 里声明的 MCP server key 清单\n * - 幂等:同版本同 scope 重复 install → no-op(artifacts 空数组,status=ok)\n * - `force: true` 才覆盖;`dryRun: true` 不写磁盘\n */\n\nimport { randomUUID } from 'node:crypto';\nimport {\n copyFile,\n lstat,\n mkdir,\n readdir,\n readFile,\n readlink,\n rename,\n rm,\n stat,\n symlink,\n unlink,\n} from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join, resolve } from 'node:path';\nimport type {\n InstalledPlugin,\n InstallOptions,\n InstallResult,\n PluginCapability,\n PluginInstaller,\n PluginRef,\n ResolvedPlugin,\n UninstallOptions,\n UninstallResult,\n} from '../types.js';\nimport {\n AtomicJsonConflictError,\n pathExists,\n readJsonFile,\n writeJsonFile,\n} from './atomic-json.js';\nimport { defaultRushBinaryResolver, normalizeClaudeMcpServers } from './mcp.js';\nimport { CAPABILITY_DIRS, ClaudeCodePaths, pluginKey } from './paths.js';\n\n// ---------------------------------------------------------------------------\n// JSON shape(Claude Code 原生格式)\n// ---------------------------------------------------------------------------\n\n/** `known_marketplaces.json` shape(spec §1.2) */\ninterface KnownMarketplacesFile {\n [name: string]: KnownMarketplaceEntry;\n}\n\ninterface KnownMarketplaceEntry {\n source: {\n source: 'github' | 'git' | 'directory';\n repo?: string;\n url?: string;\n path?: string;\n };\n installLocation: string;\n lastUpdated: string;\n autoUpdate: boolean;\n}\n\n/** `installed_plugins.json` V2 shape(spec §1.3) */\ninterface InstalledPluginsFile {\n version: 2;\n plugins: Record<string, InstalledPluginScopeEntry[]>;\n}\n\ninterface InstalledPluginScopeEntry {\n scope: 'user' | 'project';\n installPath: string;\n version: string;\n installedAt: string;\n lastUpdated: string;\n gitCommitSha?: string;\n}\n\n/** `settings.json` 我们关心的字段子集。保留 `[key: string]: unknown` 透传未知字段 */\ninterface SettingsFile {\n enabledPlugins?: Record<string, boolean>;\n [key: string]: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/**\n * marketplace 来源描述——用于写入 `known_marketplaces.json.source` 字段\n * + 让 Installer 知道去哪里拿完整 marketplace repo 来布置 `plugins/marketplaces/<mkt>/`。\n *\n * Claude Code 期望 `installLocation` 指向**完整 marketplace repo 目录**(含\n * `.claude-plugin/marketplace.json`)。rush-ai 本地 cache(`~/.rush/marketplaces/<mkt>/`)\n * 就是该 repo;Installer 把它 symlink 到 `~/.claude/plugins/marketplaces/<mkt>/`\n * (symlink 失败时 fallback 到递归 copy)。\n *\n * 为什么 rush-ai 不直接把 Claude Code 期望的目录作为自己的 cache:保留独立\n * cache(`~/.rush/marketplaces/`)方便 Cursor/Codex 共享同一份 repo 源。\n */\nexport interface ClaudeCodeMarketplaceSource {\n /**\n * Marketplace 完整 repo 的绝对路径。内含 `.claude-plugin/marketplace.json`。\n *\n * 生产场景 = `~/.rush/marketplaces/<mkt>/`(`MarketplaceCache.pathFor(name)`);\n * 测试场景 = tmp 目录下构造的 fake repo。\n */\n rootDir: string;\n /**\n * 原始 source 类型——对应 `known_marketplaces.json.source` 字段。\n *\n * - `directory`:用户给的本地目录;`installLocation` 与 `path` 一致\n * - `github`:`owner/repo[#ref]`;`installLocation` 为 plugins/marketplaces/<mkt>\n * - `git`:完整 URL;同上\n */\n descriptor:\n | { kind: 'directory'; path: string }\n | { kind: 'github'; repo: string; ref?: string }\n | { kind: 'git'; url: string };\n}\n\nexport interface ClaudeCodeInstallerOptions {\n /**\n * 注入 HOME 目录(**测试必填**,生产代码省略默认 `os.homedir()`)。\n *\n * 测试铁律:所有单测必须用 `os.tmpdir()` 下的临时目录,**禁止**写用户真实 `~/.claude/`。\n */\n home?: string;\n /**\n * 覆盖 `which rush-ai` 的解析逻辑(测试注入用)。\n * 默认实现:`defaultRushBinaryResolver`(仅 `which rush-ai`,失败返回 undefined)。\n */\n rushBinaryResolver?: () => string | undefined;\n /**\n * 覆盖\"当前时间\"的来源(测试里注入固定时间)。\n * 默认 `() => new Date().toISOString()`。\n */\n now?: () => string;\n /**\n * 本次 install 的 marketplace 源(bug #4 修复)。\n *\n * **生产路径必须传**——CLI `defaultInstallerFactory` 从 `ResolvedMarketplace` 构造;\n * 不传时 Installer 退化到\"只写 installLocation 指向 plugins/marketplaces/<mkt>/,\n * 不布置 marketplace 目录\"的兼容模式(旧单测路径)。\n *\n * 退化模式下 installLocation 指向的目录不存在 → Claude Code 仍会报\n * `Plugin not found in marketplace`,所以生产禁用。\n */\n marketplaceSource?: ClaudeCodeMarketplaceSource;\n /**\n * 覆盖 symlink 创建行为(测试注入用;生产走 `fs.symlink`)。\n *\n * 返回 `{ kind: 'ok' }` 表示 symlink 成功;`{ kind: 'fallback', reason }` 让\n * Installer 退化到 copy;抛错则 install 整体 failed。\n */\n symlinkRunner?: (\n target: string,\n linkPath: string\n ) => Promise<{ kind: 'ok' } | { kind: 'fallback'; reason: string }>;\n}\n\n// ---------------------------------------------------------------------------\n// Installer 主体\n// ---------------------------------------------------------------------------\n\nexport class ClaudeCodeInstaller implements PluginInstaller {\n readonly target = 'claude-code' as const;\n readonly paths: ClaudeCodePaths;\n private readonly rushBinaryResolver: () => string | undefined;\n private readonly now: () => string;\n private readonly marketplaceSource?: ClaudeCodeMarketplaceSource;\n private readonly symlinkRunner: (\n target: string,\n linkPath: string\n ) => Promise<{ kind: 'ok' } | { kind: 'fallback'; reason: string }>;\n\n constructor(opts: ClaudeCodeInstallerOptions = {}) {\n const home = opts.home ?? homedir();\n this.paths = new ClaudeCodePaths(home);\n this.rushBinaryResolver =\n opts.rushBinaryResolver ?? defaultRushBinaryResolver;\n this.now = opts.now ?? (() => new Date().toISOString());\n if (opts.marketplaceSource) {\n this.marketplaceSource = opts.marketplaceSource;\n }\n this.symlinkRunner = opts.symlinkRunner ?? defaultSymlinkRunner;\n }\n\n // -------------------------------------------------------------------------\n // detect / isInstalled / list\n // -------------------------------------------------------------------------\n\n async detect(): Promise<boolean> {\n return dirExists(this.paths.claudeDir);\n }\n\n async isInstalled(ref: PluginRef): Promise<boolean> {\n if (!(await pathExists(this.paths.installedPluginsJson))) {\n return false;\n }\n const { data } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const entries = data.plugins?.[pluginKey(ref)];\n return Array.isArray(entries) && entries.length > 0;\n }\n\n async list(): Promise<InstalledPlugin[]> {\n if (!(await pathExists(this.paths.installedPluginsJson))) {\n return [];\n }\n const { data } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const out: InstalledPlugin[] = [];\n for (const [key, scopeEntries] of Object.entries(data.plugins ?? {})) {\n if (!Array.isArray(scopeEntries) || scopeEntries.length === 0) continue;\n const ref = parsePluginKey(key);\n if (!ref) continue;\n // 一个 plugin 可能多 scope,取第一个(user 优先)作为展示版本\n const pick =\n scopeEntries.find((e) => e.scope === 'user') ?? scopeEntries[0];\n if (!pick) continue;\n out.push({\n ref,\n version: pick.version,\n installedAt: pick.installedAt,\n targets: [this.target],\n });\n }\n return out;\n }\n\n // -------------------------------------------------------------------------\n // install\n // -------------------------------------------------------------------------\n\n async install(\n plugin: ResolvedPlugin,\n opts: InstallOptions = {}\n ): Promise<InstallResult> {\n // spec §2.1:install() 首行必须 detect()=false 短路\n if (!(await this.detect())) {\n return {\n target: this.target,\n status: 'skipped',\n included: [],\n skipped: [],\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n\n const { ref, version } = plugin;\n const pluginVersionDir = this.paths.pluginVersionDir(ref, version);\n const key = pluginKey(ref);\n\n // 幂等检查:同版本同 scope 已装 + !force → no-op\n // 注:isAlreadyInstalledAtVersion 读损坏 JSON 会抛,统一用 try/catch 转 failed\n if (!opts.force) {\n let already = false;\n try {\n already = await this.isAlreadyInstalledAtVersion(ref, version);\n } catch (err) {\n return this.buildFailedResult(err);\n }\n if (already) {\n return {\n target: this.target,\n status: 'ok',\n included: this.computeIncluded(plugin),\n skipped: this.computeSkipped(plugin),\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n }\n\n // dry-run:计算预期 artifacts 不写磁盘\n if (opts.dryRun) {\n try {\n return await this.computeDryRun(plugin);\n } catch (err) {\n return this.buildFailedResult(err);\n }\n }\n\n // ---- 真实写入(带回滚) ----\n\n // Rollback 状态:capture 所有 JSON 的 pre-install 快照 + cache dir 是否新建\n // + 若 pluginVersionDir 预先存在(force 重装场景),先整体 rename 到 backup\n // 路径,失败时原样复位——避免\"部分覆盖\"状态\n //\n // captureRollback 本身放 try/catch 内——若 JSON 损坏等预期错误抛出,按\n // failed 返回;captureRollback 内部把 rename 放在\"所有可能抛错操作之后\",\n // 因此 rename 发生后就不会再抛错\n let rollback: RollbackSnapshot;\n try {\n rollback = await this.captureRollback(ref, version);\n } catch (err) {\n return this.buildFailedResult(err);\n }\n\n try {\n // 1. 复制 capability 目录 + 生成 plugin.json\n const writtenFiles: string[] = [];\n await mkdir(pluginVersionDir, { recursive: true });\n writtenFiles.push(pluginVersionDir);\n\n // 复制每个 capability 子目录(源目录若不存在则跳过)\n // copyDirTracked 返回本次写入的文件绝对路径清单,累加到 writtenFiles\n for (const cap of CAPABILITY_DIRS) {\n const srcDir = resolve(plugin.sourceDir, cap);\n if (!(await dirExists(srcDir))) continue;\n const dstDir = this.paths.capabilityDir(ref, version, cap);\n await mkdir(dstDir, { recursive: true });\n writtenFiles.push(dstDir);\n const copied = await copyDirTracked(srcDir, dstDir);\n writtenFiles.push(...copied);\n }\n\n // 生成 plugin.json(内嵌 mcpServers)\n const manifestPath = this.paths.pluginManifestPath(ref, version);\n const normalizedMcp = normalizeClaudeMcpServers(\n ref,\n plugin.manifest,\n this.rushBinaryResolver\n );\n const pluginJsonContent = buildPluginJson(plugin, normalizedMcp);\n await writeJsonFile(manifestPath, pluginJsonContent);\n writtenFiles.push(manifestPath);\n\n const mcpKeys = normalizedMcp ? Object.keys(normalizedMcp) : [];\n\n // 1.5. 布置 `~/.claude/plugins/marketplaces/<mkt>/`(bug #4 修复):\n // Claude Code 期望 `known_marketplaces.json.installLocation` 指向\n // 完整 marketplace repo 目录。我们从 `marketplaceSource.rootDir`\n // symlink 到 `plugins/marketplaces/<mkt>/`;symlink 失败 fallback copy。\n //\n // 幂等:若目标已是指向相同 rootDir 的 symlink → no-op;若是不同\n // rootDir 的 symlink / 目录 → 冲突不覆盖(用户自装的 marketplace 不碰)。\n const marketplaceDirResult = await this.ensureMarketplaceDir(\n ref.marketplace\n );\n if (marketplaceDirResult.written) {\n writtenFiles.push(marketplaceDirResult.path);\n }\n\n // 2. 更新 known_marketplaces.json(首次见该 marketplace 时写入)\n const marketplaceEntryWritten = await this.upsertKnownMarketplace(ref);\n if (marketplaceEntryWritten) {\n writtenFiles.push(this.paths.knownMarketplacesJson);\n }\n\n // 3. 更新 installed_plugins.json(添加或覆盖 user scope 条目)\n const installedPluginsChanged = await this.upsertInstalledPlugin(\n ref,\n version,\n pluginVersionDir\n );\n if (installedPluginsChanged) {\n writtenFiles.push(this.paths.installedPluginsJson);\n }\n\n // 4. 更新 settings.json.enabledPlugins[key] = true(只在真实写盘时 push)\n const settingsChanged = await this.enablePlugin(key);\n if (settingsChanged) {\n writtenFiles.push(this.paths.settingsJson);\n }\n\n // 成功路径:清理 backup(如有)\n await this.finalizeRollback(rollback);\n\n return {\n target: this.target,\n status: 'ok',\n included: this.computeIncluded(plugin),\n skipped: this.computeSkipped(plugin),\n artifacts: { files: writtenFiles, mcpKeys },\n };\n } catch (err) {\n // Rollback:恢复所有 JSON + 删本次新建的 cache dir + 复位 backup 目录\n await this.applyRollback(rollback).catch(() => {\n // rollback 失败——吞掉错误,原始 error 更重要\n });\n return this.buildFailedResult(err);\n }\n }\n\n /**\n * 构造 failed 结果(统一 install 内任何可预期错误的 error-return 模式)。\n */\n private buildFailedResult(err: unknown): InstallResult {\n const message = err instanceof Error ? err.message : String(err);\n return {\n target: this.target,\n status: 'failed',\n included: [],\n skipped: [],\n artifacts: { files: [], mcpKeys: [] },\n errors: [message],\n };\n }\n\n // -------------------------------------------------------------------------\n // uninstall\n // -------------------------------------------------------------------------\n\n async uninstall(\n ref: PluginRef,\n opts: UninstallOptions = {}\n ): Promise<UninstallResult> {\n // detect=false 时仍可能有历史装过的残留(比如 Claude Code 被删但 settings.json 还在)——\n // 但 `detect()=false` 意味着 `<home>/.claude/` 目录都没了,uninstall 实质 no-op\n if (!(await this.detect())) {\n return {\n target: this.target,\n status: 'ok',\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n\n const key = pluginKey(ref);\n const touchedFiles: string[] = [];\n\n // 1. 预读 installed_plugins 中本插件的条目(找 mcpKeys 用)\n const installedMcpKeys = await this.collectMcpKeysFromInstalled(ref);\n\n if (opts.dryRun) {\n // 仅返回\"本次是否会改动\"预期 artifacts——和真实 uninstall 的 touchedFiles\n // 语义一致(避免插件未安装但全局 JSON 存在时的虚报)\n const files: string[] = [];\n if (await this.installedPluginsHasEntry(ref)) {\n files.push(this.paths.installedPluginsJson);\n }\n if (await this.settingsHasEnabledKey(key)) {\n files.push(this.paths.settingsJson);\n }\n return {\n target: this.target,\n status: 'ok',\n artifacts: { files, mcpKeys: installedMcpKeys },\n };\n }\n\n // 2. 从 installed_plugins.json 删除本插件 user scope 条目(可能整个 key 没了)\n const changedInstalled = await this.removeInstalledPlugin(ref);\n if (changedInstalled) {\n touchedFiles.push(this.paths.installedPluginsJson);\n }\n\n // 3. 从 settings.json.enabledPlugins 删除 key\n const changedSettings = await this.disablePlugin(key);\n if (changedSettings) {\n touchedFiles.push(this.paths.settingsJson);\n }\n\n // 4. cache 目录**保留**(和 Claude Code 原生行为一致)\n\n // 5. 若本 plugin 是其 marketplace 下最后一个 → 清理\n // `plugins/marketplaces/<mkt>/` + `known_marketplaces.json[<mkt>]`\n // (bug #4 修复:避免 marketplace 目录越装越多)。\n //\n // 注意两点:\n // - 顺序关键:必须在 `removeInstalledPlugin` **之后**判断,因为靠\n // installed_plugins.json 做\"同 marketplace 下还有别的 plugin 吗\"的引用计数\n // - **只有真正移除了 installed_plugins 条目才触发清理**(`changedInstalled=true`)——\n // 否则\"卸载未装 plugin\"会意外清掉他人还在用的 marketplace(Sparring SHOULD-FIX)\n if (changedInstalled) {\n const marketplaceCleanup =\n await this.cleanupMarketplaceIfUnreferenced(ref);\n touchedFiles.push(...marketplaceCleanup.files);\n }\n\n return {\n target: this.target,\n status: 'ok',\n artifacts: { files: touchedFiles, mcpKeys: installedMcpKeys },\n };\n }\n\n /**\n * 若当前 plugin 是其 marketplace 下最后一个(已从 installed_plugins.json 移除后\n * 没有其他 plugin),则:\n * 1. 清理 `~/.claude/plugins/marketplaces/<mkt>/`(unlink symlink 或 rm -rf dir)\n * 2. 删 `known_marketplaces.json[<mkt>]`\n *\n * 其他 plugin 仍挂在该 marketplace → no-op(保留共享 marketplace repo)。\n *\n * **安全策略**:\n * - 只清理 rush-ai 自己装的 marketplace 目录。判定:symlink 一律删(rush-ai\n * 装的是 symlink);非 symlink 目录保留(可能是用户/Claude Code 原生命令装的)\n * - 若 `installed_plugins.json` 根本不存在(极端场景)→ 视为\"无其他引用\"仍执行清理\n */\n private async cleanupMarketplaceIfUnreferenced(\n ref: PluginRef\n ): Promise<{ files: string[] }> {\n const files: string[] = [];\n if (await this.marketplaceStillReferenced(ref.marketplace)) {\n return { files };\n }\n const mktDir = this.paths.marketplaceInstallDir(ref.marketplace);\n const kind = await inodeKind(mktDir);\n if (kind === 'symlink') {\n await unlink(mktDir);\n files.push(mktDir);\n } else if (kind === 'dir') {\n // 仅当目录里有 `.rush-marketplace-source.json`(rush-ai cache 元数据)时认定\n // 是我们 copy 来的兜底产物,可安全 rm;否则保留\n if (await pathExists(join(mktDir, '.rush-marketplace-source.json'))) {\n await rm(mktDir, { recursive: true, force: true });\n files.push(mktDir);\n }\n }\n // known_marketplaces.json[<mkt>] 清理\n const knownChanged = await this.removeKnownMarketplaceEntry(\n ref.marketplace\n );\n if (knownChanged) {\n files.push(this.paths.knownMarketplacesJson);\n }\n return { files };\n }\n\n /**\n * installed_plugins.json 里是否还有其他 plugin 挂在给定 marketplace 上。\n *\n * 调用方必须**先**从 installed_plugins.json 移除本次要卸载的 plugin——否则\n * 会把自己算作\"还有引用\"。\n */\n private async marketplaceStillReferenced(\n marketplace: string\n ): Promise<boolean> {\n if (!(await pathExists(this.paths.installedPluginsJson))) {\n return false;\n }\n const { data } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const suffix = `@${marketplace}`;\n for (const [key, entries] of Object.entries(data.plugins ?? {})) {\n if (!Array.isArray(entries) || entries.length === 0) continue;\n if (key.endsWith(suffix)) return true;\n }\n return false;\n }\n\n private async removeKnownMarketplaceEntry(\n marketplace: string\n ): Promise<boolean> {\n if (!(await pathExists(this.paths.knownMarketplacesJson))) {\n return false;\n }\n return retryOnConflict(async () => {\n const { data, mtimeMs } = await readJsonFile<KnownMarketplacesFile>(\n this.paths.knownMarketplacesJson,\n () => ({})\n );\n if (!Object.hasOwn(data, marketplace)) {\n return false;\n }\n const next: KnownMarketplacesFile = { ...data };\n delete next[marketplace];\n await writeAtomicWithMtimeCheck(\n this.paths.knownMarketplacesJson,\n next,\n mtimeMs\n );\n return true;\n });\n }\n\n /**\n * 确保 `~/.claude/plugins/marketplaces/<mkt>/` 存在并指向当前 marketplaceSource.rootDir。\n *\n * 行为:\n * 1. 没传 `marketplaceSource`(兼容模式)→ no-op,返回 `{ written: false }`\n * 2. 目标已是 symlink 且 target === rootDir → 幂等 no-op\n * 3. 目标不存在 → `symlinkRunner(rootDir, mktDir)`;失败 fallback 到 copyRecursive\n * 4. 目标是 symlink 但指向不同 target → 抛错(潜在冲突,不覆盖)\n * 5. 目标是目录 → 若含 rush-ai cache marker 且内容等价 → 幂等;否则抛错(不覆盖用户目录)\n */\n private async ensureMarketplaceDir(\n marketplaceName: string\n ): Promise<{ written: boolean; path: string }> {\n const src = this.marketplaceSource;\n if (!src) {\n // 兼容模式:旧测试 / 旧 factory 路径,不布置 marketplace 目录\n return { written: false, path: '' };\n }\n const mktDir = this.paths.marketplaceInstallDir(marketplaceName);\n await mkdir(this.paths.marketplacesRootDir, { recursive: true });\n const kind = await inodeKind(mktDir);\n if (kind === 'symlink') {\n const target = await readlink(mktDir).catch(() => null);\n // 相对 symlink 应相对 linkPath **所在目录** 解析,而非 process cwd\n // (`readlink` 返回原始字符串;resolve(target) 会按 cwd 展开)\n const resolvedTarget =\n target !== null ? resolve(dirname(mktDir), target) : null;\n if (resolvedTarget && resolvedTarget === resolve(src.rootDir)) {\n // 幂等:已是同靶 symlink\n return { written: false, path: mktDir };\n }\n throw new Error(\n `Marketplace dir '${mktDir}' already exists as a symlink to '${target}', ` +\n `refusing to replace with symlink to '${src.rootDir}'. ` +\n `Remove it manually if you intend to switch marketplace sources.`\n );\n }\n if (kind === 'dir') {\n // 可能是上一次 fallback copy 产物(用 `.rush-marketplace-source.json` marker 判定)\n const metaPath = join(mktDir, '.rush-marketplace-source.json');\n if (await pathExists(metaPath)) {\n // 校验 source 是否和当前 marketplaceSource 匹配——避免 source 切换后\n // 静默复用旧目录(Sparring round 1 MUST-FIX)\n const metaSourceRaw = await readMarketplaceCacheSource(metaPath);\n const currentSourceRaw = descriptorToSourceRaw(src.descriptor);\n if (metaSourceRaw !== null && metaSourceRaw === currentSourceRaw) {\n return { written: false, path: mktDir };\n }\n throw new Error(\n `Marketplace dir '${mktDir}' was previously installed from '${\n metaSourceRaw ?? 'unknown source'\n }', ` +\n `but current marketplaceSource is '${currentSourceRaw}'. ` +\n `Refusing to overwrite silently—remove it manually or run 'rush-ai marketplace remove <name>'.`\n );\n }\n throw new Error(\n `Marketplace dir '${mktDir}' already exists (non-symlink). ` +\n `rush-ai will not overwrite it—remove it manually if it is stale.`\n );\n }\n if (kind === 'file' || kind === 'other') {\n throw new Error(\n `Marketplace path '${mktDir}' exists but is not a directory/symlink.`\n );\n }\n // kind === 'missing':尝试 symlink,失败 fallback copy\n const linkRes = await this.symlinkRunner(src.rootDir, mktDir);\n if (linkRes.kind === 'ok') {\n return { written: true, path: mktDir };\n }\n // fallback:递归 copy\n await copyDirTracked(src.rootDir, mktDir).catch(async (err) => {\n // copy 也失败 → 清除残留 + 抛原错\n await rm(mktDir, { recursive: true, force: true }).catch(() => {});\n throw err;\n });\n return { written: true, path: mktDir };\n }\n\n // -------------------------------------------------------------------------\n // 内部:能力分类 / dry-run / 幂等\n // -------------------------------------------------------------------------\n\n /**\n * Claude Code 支持的 capability 过滤。\n * `commands` / `skills` / `rules` / `hooks` / `mcp` 全部支持——没有 skipped。\n */\n private computeIncluded(plugin: ResolvedPlugin): PluginCapability[] {\n return [...plugin.capabilities];\n }\n\n private computeSkipped(_plugin: ResolvedPlugin): PluginCapability[] {\n // Claude Code 支持所有 capability;v1 里此值永远为空\n return [];\n }\n\n private async isAlreadyInstalledAtVersion(\n ref: PluginRef,\n version: string\n ): Promise<boolean> {\n if (!(await pathExists(this.paths.installedPluginsJson))) {\n return false;\n }\n const { data } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const entries = data.plugins?.[pluginKey(ref)];\n if (!Array.isArray(entries)) return false;\n return entries.some((e) => e.scope === 'user' && e.version === version);\n }\n\n private async computeDryRun(plugin: ResolvedPlugin): Promise<InstallResult> {\n const { ref, version } = plugin;\n const pluginVersionDir = this.paths.pluginVersionDir(ref, version);\n const dryFiles: string[] = [pluginVersionDir];\n\n // 枚举每个 capability 源目录下的所有文件(用 dry-run 专用 tracker,不写磁盘)\n for (const cap of CAPABILITY_DIRS) {\n const srcDir = resolve(plugin.sourceDir, cap);\n if (!(await dirExists(srcDir))) continue;\n const dstDir = this.paths.capabilityDir(ref, version, cap);\n dryFiles.push(dstDir);\n const plannedFiles = await listDirFilesRecursive(srcDir, dstDir);\n dryFiles.push(...plannedFiles);\n }\n\n dryFiles.push(this.paths.pluginManifestPath(ref, version));\n\n // marketplace 安装目录(bug #4)——只有在 marketplaceSource 注入 + 目标不存在时\n // 才会真实写入\n if (this.marketplaceSource) {\n const mktInstallDir = this.paths.marketplaceInstallDir(ref.marketplace);\n if ((await inodeKind(mktInstallDir)) === 'missing') {\n dryFiles.push(mktInstallDir);\n }\n }\n\n // 只把真实会变化的 JSON 列进来——避免 dry-run 虚报\n if (!(await this.isKnownMarketplaceRegistered(ref.marketplace))) {\n dryFiles.push(this.paths.knownMarketplacesJson);\n }\n if (\n await this.wouldInstalledPluginsChange(ref, version, pluginVersionDir)\n ) {\n dryFiles.push(this.paths.installedPluginsJson);\n }\n if (await this.wouldSettingsChange(pluginKey(ref))) {\n dryFiles.push(this.paths.settingsJson);\n }\n\n const normalizedMcp = normalizeClaudeMcpServers(\n ref,\n plugin.manifest,\n this.rushBinaryResolver\n );\n const mcpKeys = normalizedMcp ? Object.keys(normalizedMcp) : [];\n return {\n target: this.target,\n status: 'ok',\n included: this.computeIncluded(plugin),\n skipped: this.computeSkipped(plugin),\n artifacts: { files: dryFiles, mcpKeys },\n };\n }\n\n private async isKnownMarketplaceRegistered(\n marketplace: string\n ): Promise<boolean> {\n if (!(await pathExists(this.paths.knownMarketplacesJson))) {\n return false;\n }\n const { data } = await readJsonFile<KnownMarketplacesFile>(\n this.paths.knownMarketplacesJson,\n () => ({})\n );\n return Object.hasOwn(data, marketplace);\n }\n\n private async wouldInstalledPluginsChange(\n ref: PluginRef,\n version: string,\n installPath: string\n ): Promise<boolean> {\n if (!(await pathExists(this.paths.installedPluginsJson))) {\n return true; // 不存在时肯定需要新建\n }\n const { data } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const key = pluginKey(ref);\n const entries = data.plugins?.[key] ?? [];\n const userEntry = entries.find((e) => e.scope === 'user');\n if (!userEntry) return true;\n return (\n userEntry.version !== version || userEntry.installPath !== installPath\n );\n }\n\n private async wouldSettingsChange(key: string): Promise<boolean> {\n if (!(await pathExists(this.paths.settingsJson))) {\n return true;\n }\n const { data } = await readJsonFile<SettingsFile>(\n this.paths.settingsJson,\n () => ({})\n );\n const enabled = data.enabledPlugins;\n if (!enabled || typeof enabled !== 'object' || Array.isArray(enabled)) {\n return true;\n }\n return enabled[key] !== true;\n }\n\n // -------------------------------------------------------------------------\n // 内部:rollback snapshot / apply\n // -------------------------------------------------------------------------\n\n /**\n * 预读快照 + 若 `pluginVersionDir` 已存在(force 重装或异常场景),把整个目录\n * 整体 rename 到 backup 路径——**成功时** 我们会在 `finalizeRollback()` 里把\n * backup 删掉;**失败时** 在 `applyRollback()` 里把 backup rename 回去恢复。\n *\n * 为什么先 rename 整个目录而不是复制后覆盖:\n * - rename 在 POSIX 同文件系统内**原子**;不会出现 \"部分覆盖\" 状态\n * - 复制 + 覆盖成本 O(n),rename 是 O(1)\n * - 回滚语义更简单:单一 rename 即可恢复\n *\n * **时序保证(round-2 修订)**:**先读** 3 个 JSON 快照,**再做** version dir 的\n * rename。这样 JSON 损坏(`AtomicJsonCorruptError`)抛出时,原 pluginVersionDir\n * 不会被 rename 走——install() 的 try/catch 之外保持磁盘状态不变,上层捕获\n * 异常时磁盘没有\"已 rename 但无法复位\"的悬挂状态。\n *\n * **路径类型记录(round-3 修订)**:对 marketplace / plugin / version cache 目录\n * 记录其\"预存在 inode 类型\"(dir / file / symlink / 不存在)。回滚时只清理\"本次\n * 明确新建\"的路径——若路径预先是文件或其他非目录类型(用户/测试里特殊场景),\n * 绝**不删除** 它(避免数据破坏)。\n *\n * rename 本身是 install 副作用的一部分——它发生后,install() 里的 try/catch\n * 会通过 `applyRollback()` 复位。\n */\n private async captureRollback(\n ref: PluginRef,\n version: string\n ): Promise<RollbackSnapshot> {\n const pluginVersionDir = this.paths.pluginVersionDir(ref, version);\n const preexistedVersion = await inodeKind(pluginVersionDir);\n const preexistedMarketplace = await inodeKind(\n this.paths.marketplaceCacheDir(ref.marketplace)\n );\n const preexistedPlugin = await inodeKind(this.paths.pluginCacheDir(ref));\n const preexistedMarketplaceInstall = await inodeKind(\n this.paths.marketplaceInstallDir(ref.marketplace)\n );\n\n // 1. 先读 JSON 快照(任何损坏/读错在此阶段抛出,不会破坏磁盘状态)\n const known = await this.readRawJsonForRollback(\n this.paths.knownMarketplacesJson\n );\n const installed = await this.readRawJsonForRollback(\n this.paths.installedPluginsJson\n );\n const settings = await this.readRawJsonForRollback(this.paths.settingsJson);\n\n // 2. 若 version dir 预存在(且是目录)→ 整体 rename 到 backup 路径。\n // 如果 preexistedVersion 是非 'dir' 类型(file/symlink 等特殊情况),\n // 让后续的 mkdir(pluginVersionDir) 自己抛错——回滚时绝不触碰它\n let backupVersionDir: string | null = null;\n if (preexistedVersion === 'dir') {\n backupVersionDir = `${pluginVersionDir}.rollback-${randomUUID()}`;\n await rename(pluginVersionDir, backupVersionDir);\n }\n\n return {\n ref,\n version,\n pluginVersionDir,\n preexistedVersion,\n preexistedPlugin,\n preexistedMarketplace,\n preexistedMarketplaceInstall,\n backupVersionDir,\n known,\n installed,\n settings,\n };\n }\n\n /**\n * 成功路径:清理 backup version dir(本次 install 已完全覆盖它)。\n */\n private async finalizeRollback(snap: RollbackSnapshot): Promise<void> {\n if (snap.backupVersionDir) {\n await rm(snap.backupVersionDir, { recursive: true, force: true });\n }\n }\n\n /**\n * 只清理**本次 install 明确新建**的路径——绝不删除预先存在的任意 inode(文件、\n * symlink、非预期目录)。\n *\n * 判定规则:对 marketplace / plugin / version dir,若 `preexistedX === 'missing'`\n * 才视为\"本次新建\",可以 `rm -rf`。其他任何 inode 类型(`dir` / `file` / `symlink`\n * / `other`)都保持不动,除了 `preexistedVersion === 'dir'` 的场景(走 backup rename 复位)。\n */\n private async applyRollback(snap: RollbackSnapshot): Promise<void> {\n // 1a. version dir 预先是目录(force 重装)→ 删本次写入的内容 + backup rename 回位\n if (snap.preexistedVersion === 'dir' && snap.backupVersionDir) {\n await rm(snap.pluginVersionDir, { recursive: true, force: true });\n await rename(snap.backupVersionDir, snap.pluginVersionDir);\n }\n // 1b. version dir 预先不存在 → 本次新建的,可以删\n else if (snap.preexistedVersion === 'missing') {\n await rm(snap.pluginVersionDir, { recursive: true, force: true });\n }\n // 1c. 预先是文件/symlink/其他 → 不动(install() 写入阶段会自己因 ENOTDIR 抛错)\n\n // 2. 按目录层级回滚——只清理 preexistedX==='missing' 的路径\n if (snap.preexistedPlugin === 'missing') {\n await rm(this.paths.pluginCacheDir(snap.ref), {\n recursive: true,\n force: true,\n });\n }\n if (snap.preexistedMarketplace === 'missing') {\n await rm(this.paths.marketplaceCacheDir(snap.ref.marketplace), {\n recursive: true,\n force: true,\n });\n }\n\n // 2b. 清理本次 ensureMarketplaceDir 新建的 plugins/marketplaces/<mkt>/\n // 若预先已存在(preexisted !== 'missing')→ 用户/上次 install 的产物,不动\n if (snap.preexistedMarketplaceInstall === 'missing') {\n const mktInstallDir = this.paths.marketplaceInstallDir(\n snap.ref.marketplace\n );\n const currentKind = await inodeKind(mktInstallDir);\n if (currentKind === 'symlink') {\n await unlink(mktInstallDir).catch(() => {});\n } else if (currentKind !== 'missing') {\n await rm(mktInstallDir, { recursive: true, force: true }).catch(\n () => {}\n );\n }\n }\n\n // 3. 恢复三个 JSON\n await this.restoreRawJson(this.paths.knownMarketplacesJson, snap.known);\n await this.restoreRawJson(this.paths.installedPluginsJson, snap.installed);\n await this.restoreRawJson(this.paths.settingsJson, snap.settings);\n }\n\n private async readRawJsonForRollback(\n path: string\n ): Promise<JsonRollbackState> {\n if (!(await pathExists(path))) {\n return { existed: false };\n }\n // 直接用 readJsonFile 取 parsed obj(不关心 mtime)\n const { data } = await readJsonFile<Record<string, unknown>>(\n path,\n () => ({})\n );\n return { existed: true, data };\n }\n\n private async restoreRawJson(\n path: string,\n state: JsonRollbackState\n ): Promise<void> {\n if (!state.existed) {\n // 本来不存在 → 如果本次写过就删掉\n if (await pathExists(path)) {\n await rm(path, { force: true });\n }\n return;\n }\n // 本来存在 → 恢复原内容\n if (state.data !== undefined) {\n await writeJsonFile(path, state.data);\n }\n }\n\n // -------------------------------------------------------------------------\n // 内部:3 个 JSON 的 CRUD 封装\n // -------------------------------------------------------------------------\n\n /**\n * 返回 `true` 表示本次确实写入了一条新 marketplace 条目(或覆盖了已有条目的\n * lastUpdated),`false` 表示 marketplace 已存在且无变化(不写文件)。\n *\n * 策略:如果 marketplace 已存在于 known_marketplaces → 保持不动(避免抹掉用户\n * 的 autoUpdate / source 等字段)。只在首次见到该 marketplace 时写入。\n */\n private async upsertKnownMarketplace(ref: PluginRef): Promise<boolean> {\n return retryOnConflict(async () => {\n const { data, mtimeMs } = await readJsonFile<KnownMarketplacesFile>(\n this.paths.knownMarketplacesJson,\n () => ({})\n );\n if (Object.hasOwn(data, ref.marketplace)) {\n return false;\n }\n const next: KnownMarketplacesFile = {\n ...data,\n [ref.marketplace]: this.buildKnownMarketplaceEntry(ref.marketplace),\n };\n await writeAtomicWithMtimeCheck(\n this.paths.knownMarketplacesJson,\n next,\n mtimeMs\n );\n return true;\n });\n }\n\n /**\n * 构造 `known_marketplaces.json[<mkt>]` 条目。\n *\n * `installLocation` 必须指向 `plugins/marketplaces/<mkt>/`(不是 `cache/`——\n * bug #4 修复)。`source` 字段按 marketplaceSource.descriptor 映射:\n * - directory → `{ source: 'directory', path: <descriptor.path> }`\n * - github → `{ source: 'github', repo: '<owner>/<repo>' }`\n * - git → `{ source: 'git', url: '<url>' }`\n *\n * 兼容模式(`marketplaceSource` 未注入):直接用 `installLocation`(即\n * `plugins/marketplaces/<mkt>/`)充当 directory path——老调用方本来就没提供\n * source 来源,此 fallback 保持 known_marketplaces schema 合法性,`installLocation`\n * 字段仍指向新路径。\n */\n private buildKnownMarketplaceEntry(\n marketplace: string\n ): KnownMarketplaceEntry {\n const installLocation = this.paths.marketplaceInstallDir(marketplace);\n const src = this.marketplaceSource;\n const source: KnownMarketplaceEntry['source'] = (() => {\n if (!src) {\n // 兼容模式:旧测试路径——fallback 用 plugins/marketplaces/<mkt> 作 path\n return { source: 'directory', path: installLocation };\n }\n const d = src.descriptor;\n switch (d.kind) {\n case 'directory':\n return { source: 'directory', path: d.path };\n case 'github':\n return { source: 'github', repo: d.repo };\n case 'git':\n return { source: 'git', url: d.url };\n }\n })();\n return {\n source,\n installLocation,\n lastUpdated: this.now(),\n autoUpdate: false,\n };\n }\n\n /**\n * 返回 `true` 表示本次真实写入了 installed_plugins.json;\n * 返回 `false` 表示内容未变化(幂等 no-op,不写盘)。\n *\n * 幂等判定:同 key 下已有 user scope 条目 + version/installPath 完全一致 →\n * 不写盘(但 lastUpdated 仍可以省略,不算\"内容变化\")。这保证了 `force: true`\n * 重装仍会触发写盘(因为 lastUpdated 会重算),而普通重复装则 no-op。\n *\n * 注:幂等路径在调用方(`install()`)已经由 `isAlreadyInstalledAtVersion` 短路,\n * 所以此处多一道\"字段真实变化\"判定,主要防范未来的边界——现在大多数命中路径\n * 都会写盘,实际 `false` 返回较罕见。\n */\n private async upsertInstalledPlugin(\n ref: PluginRef,\n version: string,\n installPath: string\n ): Promise<boolean> {\n return retryOnConflict(async () => {\n const { data, mtimeMs } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const key = pluginKey(ref);\n const now = this.now();\n const existingEntries = data.plugins?.[key] ?? [];\n const userIdx = existingEntries.findIndex((e) => e.scope === 'user');\n const existingUserEntry =\n userIdx >= 0 ? existingEntries[userIdx] : undefined;\n const updatedEntry: InstalledPluginScopeEntry = {\n scope: 'user',\n installPath,\n version,\n installedAt: existingUserEntry?.installedAt ?? now,\n lastUpdated: now,\n };\n // 字段实质变化判定(忽略 lastUpdated——时间戳总在变)\n const fieldsUnchanged =\n existingUserEntry !== undefined &&\n existingUserEntry.scope === updatedEntry.scope &&\n existingUserEntry.installPath === updatedEntry.installPath &&\n existingUserEntry.version === updatedEntry.version;\n if (fieldsUnchanged) {\n return false;\n }\n const nextEntries =\n userIdx >= 0\n ? [\n ...existingEntries.slice(0, userIdx),\n updatedEntry,\n ...existingEntries.slice(userIdx + 1),\n ]\n : [...existingEntries, updatedEntry];\n const next: InstalledPluginsFile = {\n version: 2,\n plugins: {\n ...(data.plugins ?? {}),\n [key]: nextEntries,\n },\n };\n await writeAtomicWithMtimeCheck(\n this.paths.installedPluginsJson,\n next,\n mtimeMs\n );\n return true;\n });\n }\n\n /**\n * 返回 `true` 表示本次真实写入了 settings.json;`false` 表示已启用(幂等 no-op)。\n */\n private async enablePlugin(key: string): Promise<boolean> {\n return retryOnConflict(async () => {\n const { data, mtimeMs } = await readJsonFile<SettingsFile>(\n this.paths.settingsJson,\n () => ({})\n );\n const existingEnabled = data.enabledPlugins;\n if (\n existingEnabled !== undefined &&\n (typeof existingEnabled !== 'object' ||\n Array.isArray(existingEnabled) ||\n existingEnabled === null)\n ) {\n throw new Error(\n `${this.paths.settingsJson}: enabledPlugins 字段类型错误(期望 object)`\n );\n }\n if (\n existingEnabled &&\n Object.hasOwn(existingEnabled, key) &&\n existingEnabled[key] === true\n ) {\n // 幂等:已启用不动\n return false;\n }\n const next: SettingsFile = {\n ...data,\n enabledPlugins: {\n ...(existingEnabled ?? {}),\n [key]: true,\n },\n };\n await writeAtomicWithMtimeCheck(this.paths.settingsJson, next, mtimeMs);\n return true;\n });\n }\n\n private async removeInstalledPlugin(ref: PluginRef): Promise<boolean> {\n if (!(await pathExists(this.paths.installedPluginsJson))) {\n return false;\n }\n return retryOnConflict(async () => {\n const { data, mtimeMs } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const key = pluginKey(ref);\n const existingEntries = data.plugins?.[key];\n if (!Array.isArray(existingEntries) || existingEntries.length === 0) {\n return false;\n }\n const remaining = existingEntries.filter((e) => e.scope !== 'user');\n const nextPlugins = { ...(data.plugins ?? {}) };\n if (remaining.length === 0) {\n delete nextPlugins[key];\n } else {\n nextPlugins[key] = remaining;\n }\n const next: InstalledPluginsFile = { version: 2, plugins: nextPlugins };\n await writeAtomicWithMtimeCheck(\n this.paths.installedPluginsJson,\n next,\n mtimeMs\n );\n return true;\n });\n }\n\n private async disablePlugin(key: string): Promise<boolean> {\n if (!(await pathExists(this.paths.settingsJson))) {\n return false;\n }\n return retryOnConflict(async () => {\n const { data, mtimeMs } = await readJsonFile<SettingsFile>(\n this.paths.settingsJson,\n () => ({})\n );\n const enabled = data.enabledPlugins;\n if (\n !enabled ||\n typeof enabled !== 'object' ||\n Array.isArray(enabled) ||\n !Object.hasOwn(enabled, key)\n ) {\n return false;\n }\n const nextEnabled = { ...enabled };\n delete nextEnabled[key];\n const next: SettingsFile = { ...data };\n if (Object.keys(nextEnabled).length === 0) {\n delete next.enabledPlugins;\n } else {\n next.enabledPlugins = nextEnabled;\n }\n await writeAtomicWithMtimeCheck(this.paths.settingsJson, next, mtimeMs);\n return true;\n });\n }\n\n /** installed_plugins.json 是否有本 plugin 的 user scope 条目(uninstall dry-run 用) */\n private async installedPluginsHasEntry(ref: PluginRef): Promise<boolean> {\n if (!(await pathExists(this.paths.installedPluginsJson))) {\n return false;\n }\n const { data } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const entries = data.plugins?.[pluginKey(ref)];\n if (!Array.isArray(entries) || entries.length === 0) return false;\n return entries.some((e) => e.scope === 'user');\n }\n\n /** settings.json.enabledPlugins 是否有本 key(uninstall dry-run 用) */\n private async settingsHasEnabledKey(key: string): Promise<boolean> {\n if (!(await pathExists(this.paths.settingsJson))) {\n return false;\n }\n const { data } = await readJsonFile<SettingsFile>(\n this.paths.settingsJson,\n () => ({})\n );\n const enabled = data.enabledPlugins;\n if (!enabled || typeof enabled !== 'object' || Array.isArray(enabled)) {\n return false;\n }\n return Object.hasOwn(enabled, key);\n }\n\n private async collectMcpKeysFromInstalled(ref: PluginRef): Promise<string[]> {\n // 从 cache 里的 plugin.json 读回 mcpKeys。不依赖 registry——保持 Installer 自治。\n const manifestPath = await this.findLatestManifestPath(ref);\n if (!manifestPath) return [];\n const { data } = await readJsonFile<Record<string, unknown>>(\n manifestPath,\n () => ({})\n );\n const servers = data.mcpServers;\n if (!servers || typeof servers !== 'object' || Array.isArray(servers)) {\n return [];\n }\n return Object.keys(servers as Record<string, unknown>);\n }\n\n private async findLatestManifestPath(ref: PluginRef): Promise<string | null> {\n if (!(await pathExists(this.paths.installedPluginsJson))) {\n return null;\n }\n const { data } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const entries = data.plugins?.[pluginKey(ref)];\n if (!Array.isArray(entries) || entries.length === 0) return null;\n const userEntry = entries.find((e) => e.scope === 'user') ?? entries[0];\n if (!userEntry) return null;\n const manifestPath = join(\n userEntry.installPath,\n '.claude-plugin',\n 'plugin.json'\n );\n if (await pathExists(manifestPath)) {\n return manifestPath;\n }\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// 顶层 helpers(不暴露到 barrel)\n// ---------------------------------------------------------------------------\n\n/** 路径预先存在的 inode 类型,用于区分\"本次新建 vs 用户已有\"。 */\ntype InodeKind = 'dir' | 'file' | 'symlink' | 'other' | 'missing';\n\ninterface RollbackSnapshot {\n ref: PluginRef;\n version: string;\n pluginVersionDir: string;\n /** pluginVersionDir 预先存在的 inode 类型 */\n preexistedVersion: InodeKind;\n /** pluginCacheDir(`cache/<mkt>/<plugin>/`)预先存在的 inode 类型 */\n preexistedPlugin: InodeKind;\n /** marketplaceCacheDir(`cache/<mkt>/`)预先存在的 inode 类型 */\n preexistedMarketplace: InodeKind;\n /**\n * `plugins/marketplaces/<mkt>/`(bug #4)预先存在的 inode 类型。\n * 回滚时仅当 preexisted === 'missing' 才清理(symlink `unlink`,dir `rm -rf`)\n */\n preexistedMarketplaceInstall: InodeKind;\n /**\n * 若 `pluginVersionDir` 预存在(force 重装),install 前会把它 rename 到此路径;\n * 成功时 `finalizeRollback()` 删掉;失败时 `applyRollback()` rename 回原位。\n * 为 null 表示本次 install 前 version dir 不存在或不是 dir,不需备份。\n */\n backupVersionDir: string | null;\n known: JsonRollbackState;\n installed: JsonRollbackState;\n settings: JsonRollbackState;\n}\n\ninterface JsonRollbackState {\n existed: boolean;\n data?: Record<string, unknown>;\n}\n\n/**\n * `<name>@<marketplace>` → PluginRef(与 `pluginRefToKey` 反向,但容错返回 null)\n */\nfunction parsePluginKey(key: string): PluginRef | null {\n const at = key.indexOf('@');\n if (at <= 0 || at === key.length - 1) return null;\n return { name: key.slice(0, at), marketplace: key.slice(at + 1) };\n}\n\n/**\n * 构造要写入 `.claude-plugin/plugin.json` 的最终 JSON。\n *\n * 来源:\n * - `plugin.manifest` 整体透传(name / version / description / author / keywords /\n * homepage / repository / license / interface 等)\n * - `mcpServers`:用规范化过的对象覆盖(若传入 `undefined` 则 **drop** 原 manifest\n * 的 mcpServers 字段——Claude Code 不认 string 形态,也不接受 undefined/null)\n *\n * 规则:不落 `skills` 字段(Claude Code 约定自动扫描 `skills/` 目录,写入反而冗余;\n * spec §1.5:`commands/skills/rules/hooks` 目录不在 plugin.json 里声明)\n */\nfunction buildPluginJson(\n plugin: ResolvedPlugin,\n mcpServers: Record<string, unknown> | undefined\n): Record<string, unknown> {\n const { manifest } = plugin;\n const {\n mcpServers: _origMcp,\n skills: _skills, // Claude Code 不用此字段,drop\n ...rest\n } = manifest;\n const out: Record<string, unknown> = { ...rest };\n if (mcpServers && Object.keys(mcpServers).length > 0) {\n out.mcpServers = mcpServers;\n }\n return out;\n}\n\n/**\n * 读 `.rush-marketplace-source.json` 里记录的 `sourceRaw`。\n *\n * 用于 `ensureMarketplaceDir` 幂等校验——若目录是我们上次 fallback copy 来的,\n * 且 source 与当前 marketplaceSource 一致,可以安全复用;否则视为冲突。\n *\n * 损坏/字段缺失 → 返回 null(让上层按\"无法匹配\"处理,抛冲突错)。\n */\nasync function readMarketplaceCacheSource(\n metaPath: string\n): Promise<string | null> {\n try {\n const raw = await readFile(metaPath, 'utf8');\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n const sourceRaw = (parsed as Record<string, unknown>).sourceRaw;\n if (typeof sourceRaw === 'string') return sourceRaw;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * 把 `ClaudeCodeMarketplaceSource.descriptor` 转成与 `.rush-marketplace-source.json`\n * 里 `sourceRaw` 字段相同的格式——用于幂等校验。\n *\n * 和 `marketplaces/source.ts` 的 stringify 保持一致:\n * - directory → `directory:<path>`\n * - github → `github:<owner/repo>`\n * - git → `git:<url>`\n */\nfunction descriptorToSourceRaw(\n descriptor: ClaudeCodeMarketplaceSource['descriptor']\n): string {\n switch (descriptor.kind) {\n case 'directory':\n return `directory:${descriptor.path}`;\n case 'github':\n return descriptor.ref\n ? `github:${descriptor.repo}#${descriptor.ref}`\n : `github:${descriptor.repo}`;\n case 'git':\n return `git:${descriptor.url}`;\n }\n}\n\n/**\n * 默认 symlink 创建器。\n *\n * 能 symlink 就 symlink;碰到\"文件系统不支持/跨挂载点/权限\"类 errno 返回\n * `{ kind: 'fallback', reason }` 让上层走 copy;其他错误向上抛。\n *\n * 判定依据:\n * - `EXDEV`:跨文件系统挂载点\n * - `EPERM`:Windows 某些用户无权创建 symlink\n * - `ENOTSUP` / `EOPNOTSUPP`:文件系统不支持 symlink(NTFS/FAT 等)\n * - `EACCES`:无写权限(让上层 fallback 试一次 copy;若 copy 也不行会自然抛错)\n *\n * 生产:用 `fs.symlink`(默认 type);测试:可注入 stub。\n */\nasync function defaultSymlinkRunner(\n target: string,\n linkPath: string\n): Promise<{ kind: 'ok' } | { kind: 'fallback'; reason: string }> {\n try {\n await symlink(target, linkPath);\n return { kind: 'ok' };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (\n code === 'EXDEV' ||\n code === 'EPERM' ||\n code === 'ENOTSUP' ||\n code === 'EOPNOTSUPP' ||\n code === 'EACCES'\n ) {\n return { kind: 'fallback', reason: code };\n }\n throw err;\n }\n}\n\nasync function dirExists(p: string): Promise<boolean> {\n try {\n const s = await stat(p);\n return s.isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * 检查路径预先的 inode 类型——用于 rollback 判定\"本次新建 vs 用户已有\"。\n *\n * 用 `lstat` 不跟踪 symlink(要区分 symlink 和它指向的目标)。\n */\nasync function inodeKind(p: string): Promise<InodeKind> {\n try {\n const s = await lstat(p);\n if (s.isDirectory()) return 'dir';\n if (s.isSymbolicLink()) return 'symlink';\n if (s.isFile()) return 'file';\n return 'other';\n } catch {\n return 'missing';\n }\n}\n\n/**\n * 预演模式:列出**如果递归复制** srcDir 到 dstDir 会写入哪些文件+目录(相对 dstDir 路径已经转绝对)。\n *\n * 供 dry-run 用——不读写磁盘之外的文件,只读 srcDir 枚举结构。\n */\nasync function listDirFilesRecursive(\n srcDir: string,\n dstDir: string\n): Promise<string[]> {\n const planned: string[] = [];\n const entries = await readdir(srcDir, { withFileTypes: true });\n for (const entry of entries) {\n const srcPath = join(srcDir, entry.name);\n const dstPath = join(dstDir, entry.name);\n if (entry.isDirectory()) {\n planned.push(dstPath);\n const sub = await listDirFilesRecursive(srcPath, dstPath);\n planned.push(...sub);\n } else if (entry.isFile()) {\n planned.push(dstPath);\n }\n }\n return planned;\n}\n\n/**\n * 递归复制目录,返回**本次实际写入的文件绝对路径清单**。\n *\n * 返回清单用于 `InstallResult.artifacts.files`——让 CLI/registry 能精确知道\"装\n * 了哪些文件\",不止是\"哪些目录\"。\n *\n * Node 22 有 `cp` recursive 选项,但该 API 在某些版本有 behaviors 不一致问题——\n * 这里手写保守实现(只用 readdir + copyFile + mkdir)。\n *\n * 不处理 symlink(源目录里的 symlink 按目标文件复制,不保留 link 语义)。\n */\nasync function copyDirTracked(\n srcDir: string,\n dstDir: string\n): Promise<string[]> {\n const written: string[] = [];\n const entries = await readdir(srcDir, { withFileTypes: true });\n for (const entry of entries) {\n const srcPath = join(srcDir, entry.name);\n const dstPath = join(dstDir, entry.name);\n if (entry.isDirectory()) {\n await mkdir(dstPath, { recursive: true });\n written.push(dstPath);\n const subWritten = await copyDirTracked(srcPath, dstPath);\n written.push(...subWritten);\n } else if (entry.isFile()) {\n await mkdir(dirname(dstPath), { recursive: true });\n await copyFile(srcPath, dstPath);\n written.push(dstPath);\n }\n // symlink / fifo / socket 忽略\n }\n return written;\n}\n\n/**\n * 写 JSON 时做 mtime 比对;变化了抛 `AtomicJsonConflictError`。\n *\n * 注意:`writeJsonFile` 本身是原子的(write .tmp → rename);`writeAtomicWithMtimeCheck`\n * 在 `writeJsonFile` 之前做 mtime gate,两者组合等价于 \"CAS-like\" 保护。\n */\nasync function writeAtomicWithMtimeCheck(\n filePath: string,\n data: unknown,\n expectedMtimeMs: number | null\n): Promise<void> {\n if (expectedMtimeMs !== null) {\n let currentMtime: number | null = null;\n try {\n const s = await stat(filePath);\n currentMtime = s.mtimeMs;\n } catch {\n currentMtime = null;\n }\n if (currentMtime !== null && currentMtime !== expectedMtimeMs) {\n throw new AtomicJsonConflictError(filePath);\n }\n }\n await writeJsonFile(filePath, data);\n}\n\n/**\n * 遇到 `AtomicJsonConflictError` 重试一次。两次都冲突 → 抛给上层。\n *\n * 简化策略(spec §3.3 靠拢):只 retry 1 次,避免隐藏活锁。\n */\nasync function retryOnConflict<T>(fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (err) {\n if (err instanceof AtomicJsonConflictError) {\n return await fn();\n }\n throw err;\n }\n}\n","/**\n * MCP command 规范化(task-6 产物)。\n *\n * 仅对 rush-ai 自己的插件(`rush@rush-marketplace`)做 MCP command 绝对路径规范化;\n * 其他 plugin 透传(见 plan §7.1 / spec §1.5)。\n *\n * 说明:resolver 层(`plugins/resolver.ts` 的 `normalizeRushMcpCommand`)已经做过\n * 同样的规范化。本文件提供一个 Installer 侧的**兜底**:如果 resolver 没解析到绝对路径\n * (例如 rushAiBinaryResolver 返回 undefined,resolver 选择不改),Installer 写\n * plugin.json 前再尝试一次——用 `which rush-ai`(PATH 解析)。**不 fallback 到\n * `process.argv[0]`**(那通常是 `node`,会写错误 command)。\n *\n * 为什么在两个层都做:\n * - Resolver 层:给各家 Installer 一个统一的起点(避免 Claude Code / Codex / Cursor\n * Installer 各自重复实现)\n * - Installer 层:resolver 的兜底策略有意保持宽松(失败就透传),Installer 层可以\n * 更激进(Claude Code 的 plugin.json 要求绝对路径更严)\n *\n * 注意:对 rush plugin 且 resolver + 本兜底都拿不到绝对路径时,我们仍然写入 plugin.json\n * 的原始值——不抛错、不阻塞 install。Claude Code 运行时会自己 fail(下游问题),但\n * Installer 保持 \"能装就装\" 的语义。\n */\n\nimport { execFileSync } from 'node:child_process';\nimport { isAbsolute } from 'node:path';\nimport type { McpServerConfig, PluginManifest, PluginRef } from '../types.js';\n\n/** rush 自身 plugin 的 identifier(与 resolver 层保持一致) */\nexport const RUSH_AI_PLUGIN_NAME = 'rush' as const;\nexport const RUSH_AI_MARKETPLACE_NAME = 'rush-marketplace' as const;\nexport const RUSH_MCP_SERVER_KEY = 'rush' as const;\n\n/**\n * 决定是否应对该 plugin 做 MCP command 规范化。\n *\n * 约束条件(plan §7.1):\n * - `ref.name === 'rush'`\n * - `ref.marketplace === 'rush-marketplace'`\n *\n * 第三方 marketplace 的 rush plugin(同名但不同 marketplace)**不**命中——\n * 只有我们自己发布的插件才保证 `rush-ai` binary 可解析到绝对路径。\n */\nexport function isRushOwnPlugin(ref: PluginRef): boolean {\n return (\n ref.name === RUSH_AI_PLUGIN_NAME &&\n ref.marketplace === RUSH_AI_MARKETPLACE_NAME\n );\n}\n\n/**\n * 规范化后的 MCP servers 对象——返回 Claude Code plugin.json 顶层所需的 `mcpServers`\n * 结构(`Record<string, McpServerConfig>`)。\n *\n * - `manifest.mcpServers` 是 `string`(Codex 外部文件引用)→ 返回 `undefined`\n * (Claude Code 不应该写入 string 形态,Installer 调用方自行处理)\n * - 非 rush 插件 → 原样返回\n * - rush 插件:仅对 `rush` server key 做 command 规范化(其他 key 保持)\n *\n * 规范化策略(rush 插件且 command 非绝对路径时):\n * 1. 调用者注入的 `resolver`(通常是 `whichRushAiBinary`)\n * 2. 返回值是绝对路径 → 用它\n * 3. 否则保留原始 command(不抛错,让 Claude Code 运行时报)\n */\nexport function normalizeClaudeMcpServers(\n ref: PluginRef,\n manifest: PluginManifest,\n resolver: () => string | undefined = defaultRushBinaryResolver\n): Record<string, McpServerConfig> | undefined {\n const servers = manifest.mcpServers;\n if (servers === undefined || servers === null) return undefined;\n if (typeof servers === 'string') {\n // Codex 外部文件引用 — Claude Code 不认,Installer 跳过内嵌 MCP。\n // 不抛错(可能是多 IDE 通用的 plugin.json,其他字段仍然有价值)\n return undefined;\n }\n if (!isRushOwnPlugin(ref)) {\n // 第三方 plugin:透传作者写的值\n return { ...servers };\n }\n\n // rush own plugin —— 仅规范化 `rush` server key\n const rushServer = servers[RUSH_MCP_SERVER_KEY];\n if (!rushServer) {\n return { ...servers };\n }\n const currentCommand = rushServer.command;\n if (typeof currentCommand === 'string' && isAbsolute(currentCommand)) {\n // 已经是绝对路径,不动\n return { ...servers };\n }\n\n const resolved = resolver();\n if (!resolved || !isAbsolute(resolved)) {\n // 兜底失败:保留原值,不阻塞 install\n return { ...servers };\n }\n\n return {\n ...servers,\n [RUSH_MCP_SERVER_KEY]: { ...rushServer, command: resolved },\n };\n}\n\n/**\n * 默认 rush-ai binary 解析:`which rush-ai`(PATH 解析)。\n *\n * 作为 Installer 层兜底——resolver 层已用 `process.argv[1]` 试过一次;本函数\n * 用更稳的 PATH 查找逻辑。\n *\n * **不 fallback 到 `process.argv[0]`**(那通常是 `node`,会把 rush MCP command\n * 规范化成 node 路径,运行时语义错误)——宁可返回 `undefined`(保留原 command),\n * 也不冒险写入错误绝对路径。\n *\n * 失败返回 `undefined`,调用方保留原值。\n */\nexport function defaultRushBinaryResolver(): string | undefined {\n try {\n const result = execFileSync('which', ['rush-ai'], {\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n if (result.length > 0 && isAbsolute(result)) {\n return result;\n }\n } catch {\n // `which` 失败 —— rush-ai 未在 PATH 上,放弃兜底\n }\n return undefined;\n}\n","/**\n * Claude Code Installer 的路径 helper(task-6 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §1.1 目录布局。\n *\n * 所有路径都基于注入的 `home` 目录构造——生产代码传 `os.homedir()`,测试必须\n * 走 `os.tmpdir()` 下的临时目录(**禁止**写用户真实 `~/.claude/`)。\n *\n * 目录结构:\n * ```\n * <home>/.claude/\n * ├── settings.json ← enabledPlugins 合并\n * └── plugins/\n * ├── known_marketplaces.json ← marketplace 注册表\n * ├── installed_plugins.json ← V2 schema\n * ├── marketplaces/ ← marketplace 完整 repo(含 .claude-plugin/marketplace.json)\n * │ └── <mkt-name>/ (symlink/copy,指向 ~/.rush/marketplaces/<mkt>)\n * └── cache/\n * └── <mkt-name>/\n * └── <plugin-name>/\n * └── <version>/\n * ├── .claude-plugin/plugin.json\n * ├── commands/ skills/ rules/ hooks/ agents/ (各 capability 目录)\n * ```\n */\n\nimport { resolve } from 'node:path';\nimport type { PluginRef } from '../types.js';\n\n/** `.claude/` 目录名 */\nexport const CLAUDE_DIR = '.claude' as const;\n\n/** plugins 子目录名 */\nexport const PLUGINS_SUBDIR = 'plugins' as const;\n\n/** cache 子目录名 */\nexport const CACHE_SUBDIR = 'cache' as const;\n\n/** plugin manifest 相对 plugin version 目录的路径 */\nexport const PLUGIN_MANIFEST_RELATIVE = '.claude-plugin/plugin.json' as const;\n\n/**\n * Claude Code 原生识别的 capability 目录名。\n *\n * 安装时按顺序整体递归复制;卸载时 cache 目录**保留**(和原生行为一致)。\n * 注意:`mcp` 不在这里——MCP 通过 plugin.json 内嵌表达,不是独立目录。\n */\nexport const CAPABILITY_DIRS = [\n 'commands',\n 'skills',\n 'rules',\n 'hooks',\n 'agents',\n] as const;\n\nexport type CapabilityDirName = (typeof CAPABILITY_DIRS)[number];\n\n/**\n * 集中式路径工厂——所有 Installer 内部的磁盘写入都从这里取路径,不散落字符串。\n */\nexport class ClaudeCodePaths {\n constructor(public readonly home: string) {}\n\n /** `<home>/.claude/` */\n get claudeDir(): string {\n return resolve(this.home, CLAUDE_DIR);\n }\n\n /** `<home>/.claude/settings.json` */\n get settingsJson(): string {\n return resolve(this.claudeDir, 'settings.json');\n }\n\n /** `<home>/.claude/plugins/` */\n get pluginsDir(): string {\n return resolve(this.claudeDir, PLUGINS_SUBDIR);\n }\n\n /** `<home>/.claude/plugins/known_marketplaces.json` */\n get knownMarketplacesJson(): string {\n return resolve(this.pluginsDir, 'known_marketplaces.json');\n }\n\n /** `<home>/.claude/plugins/installed_plugins.json` */\n get installedPluginsJson(): string {\n return resolve(this.pluginsDir, 'installed_plugins.json');\n }\n\n /** `<home>/.claude/plugins/cache/` */\n get cacheDir(): string {\n return resolve(this.pluginsDir, CACHE_SUBDIR);\n }\n\n /**\n * `<home>/.claude/plugins/marketplaces/`。\n *\n * Claude Code 扫 marketplace repo(含 `.claude-plugin/marketplace.json`)的目录——\n * `known_marketplaces.json.installLocation` 必须指向此目录下的 `<mkt>/`,否则\n * Claude Code 会识别不到 plugin 条目(regression fix,bug #4)。\n */\n get marketplacesRootDir(): string {\n return resolve(this.pluginsDir, 'marketplaces');\n }\n\n /** `<home>/.claude/plugins/marketplaces/<mkt>/` */\n marketplaceInstallDir(marketplace: string): string {\n return resolve(this.marketplacesRootDir, marketplace);\n }\n\n /** `<home>/.claude/plugins/cache/<mkt>/` */\n marketplaceCacheDir(marketplace: string): string {\n return resolve(this.cacheDir, marketplace);\n }\n\n /** `<home>/.claude/plugins/cache/<mkt>/<plugin>/` */\n pluginCacheDir(ref: PluginRef): string {\n return resolve(this.marketplaceCacheDir(ref.marketplace), ref.name);\n }\n\n /** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/` */\n pluginVersionDir(ref: PluginRef, version: string): string {\n return resolve(this.pluginCacheDir(ref), version);\n }\n\n /** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/.claude-plugin/plugin.json` */\n pluginManifestPath(ref: PluginRef, version: string): string {\n return resolve(\n this.pluginVersionDir(ref, version),\n PLUGIN_MANIFEST_RELATIVE\n );\n }\n\n /** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/<capability>/` */\n capabilityDir(\n ref: PluginRef,\n version: string,\n capability: CapabilityDirName\n ): string {\n return resolve(this.pluginVersionDir(ref, version), capability);\n }\n}\n\n/**\n * `<plugin-name>@<marketplace>` 组合键——Claude Code installed_plugins / enabledPlugins\n * 以及 rush-ai registry 都用这个形态。\n *\n * 与 registry 里的 `pluginRefToKey` 语义一致但不直接复用(避免 cross-module\n * coupling;同时保留此处对键格式的文档)。\n */\nexport function pluginKey(ref: PluginRef): string {\n return `${ref.name}@${ref.marketplace}`;\n}\n","/**\n * Codex Installer —— `class CodexInstaller implements PluginInstaller`(task-8 产物)。\n *\n * Source of truth:\n * - `specs/plugin-schemas.md` §2(Codex 目录布局 / config.toml / plugin.json / .mcp.json)\n * - `specs/installer-interface.md` §1–§4(Installer 行为契约)\n *\n * 行为契约(严格对齐 spec §2.1):\n * - `install()` 第一行 `detect()=false` → `{status: 'skipped', ...}`\n * - 复制 plugin sourceDir 的 `skills/` 到 `<cache>/<mkt>/<plugin>/<ver>/skills/`\n * - 生成 `.codex-plugin/plugin.json`(必写 `\"skills\": \"./skills/\"`,即便 Codex\n * 实测可选——对齐 OpenAI bundled 规范和 spec §2.3 决策)\n * - 生成 `.mcp.json`(外部文件,plugin.json 引用 `\"mcpServers\": \"./.mcp.json\"`)\n * - 更新 `~/.codex/config.toml` 的 `[marketplaces.<mkt>]` + `[plugins.\"<name>@<mkt>\"]`\n * - 写前备份 `config.toml.bak.<YYYYMMDD-HHMMSS>`,`InstallResult.notes` 告知路径\n * - 写前读 mtime,写完后再 stat 确认(并发保护 —— `writeCodexConfig` 内置)\n * - 不保留注释(决策已定)\n * - Skipped capabilities: `['rules', 'hooks']`(Codex 不支持)\n * - 写入失败完整回滚(从 .bak 恢复 config.toml + 删除本次创建的 cache 目录)\n * - `InstallResult.artifacts.files` 完整列出:version dir + plugin.json + .mcp.json +\n * skills dir + config.toml + 备份路径\n * - `InstallResult.artifacts.mcpKeys` 列出 .mcp.json 里 server key\n *\n * - 删 `[plugins.\"<name>@<mkt>\"]` section\n * - 若 marketplace 下**没有其他 plugin** 使用 → 也删 `[marketplaces.<mkt>]`;否则保留\n * - cache 目录保留(和原生行为一致 + Claude Code Installer 也是这个策略)\n */\n\nimport { type Dirent, constants as fsConstants } from 'node:fs';\nimport {\n access,\n cp,\n mkdir,\n mkdtemp,\n readdir,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport {\n basename,\n dirname,\n extname,\n relative as pathRelative,\n resolve,\n} from 'node:path';\nimport type {\n InstalledPlugin,\n InstallOptions,\n InstallResult,\n PluginCapability,\n PluginInstaller,\n PluginManifest,\n PluginRef,\n ResolvedPlugin,\n UninstallOptions,\n UninstallResult,\n} from '../types.js';\nimport { buildCodexMcpContent, listMcpKeys, serializeMcpJson } from './mcp.js';\nimport {\n codexConfigBackupPath,\n codexConfigTomlPath,\n codexHomeDir,\n codexMarketplaceDir,\n codexMarketplaceManifestPath,\n codexMarketplacePluginDir,\n codexPluginManifestPath,\n codexPluginMcpPath,\n codexPluginSkillsDir,\n codexPluginVersionDir,\n pluginSectionKey,\n} from './paths.js';\nimport {\n backupCodexConfig,\n marketplaceHasOtherPlugins,\n readCodexConfig,\n removeMarketplaceSection,\n removePluginEntry,\n restoreCodexConfigFromBackup,\n setMarketplaceSection,\n setPluginEntry,\n writeCodexConfig,\n} from './toml.js';\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface CodexInstallerOptions {\n /**\n * HOME 目录注入(仅测试用)。生产传 `undefined`,默认 `os.homedir()`。\n *\n * **测试铁律**:所有单测必须传 `os.tmpdir()` 下的临时目录。\n */\n readonly home?: string;\n\n /**\n * rush-ai binary 绝对路径解析器(仅 rush@rush-marketplace 的 rush MCP server 使用)。\n *\n * 生产默认 `() => process.argv[1]`(rush-ai CLI 入口),失败时保持 manifest\n * 原 command 不改(让下游运行时报错,而非 install 阶段挂)。\n *\n * 测试里注入固定字符串最稳定。\n */\n readonly rushAiBinaryResolver?: () => string | undefined;\n\n /**\n * 当前时间注入器(仅测试用),默认 `() => new Date()`。\n *\n * 用于 marketplace `last_updated` + 备份路径时间戳,便于测试断言稳定。\n */\n readonly now?: () => Date;\n\n /**\n * marketplace source 配置(用于写 `[marketplaces.<mkt>]` section)。\n *\n * Codex Installer 不知道 plugin 来自哪个 source,必须由上层(CLI 或测试)\n * 显式传入。\n *\n * 字段:\n * - `sourceType`:`\"local\"` / `\"github\"` / `\"git\"`\n * - `source`:字符串(绝对路径 or github repo or git URL)\n *\n * 可选:上层不传时 `install` 会默认写 `source_type = \"local\"` +\n * `source = plugin.sourceDir` 的合理 fallback(对齐 phase0-spike 实测行为:\n * `codex plugin marketplace add /abs/path` 就是 source_type=local)。\n */\n readonly marketplaceSource?: {\n readonly sourceType: 'local' | 'github' | 'git';\n readonly source: string;\n };\n}\n\n// Codex 不支持的能力 —— Installer 层恒定跳过。\n// Claude `commands/*.md` 是 flat Markdown skills,安装到 Codex 时转为 skills。\nconst CODEX_UNSUPPORTED: PluginCapability[] = ['rules', 'hooks'];\n\n/**\n * Installer 自抛的预期错误基类。所有从 install()/uninstall() 预期返回\n * `status: 'failed'` 而非上抛的场景都应抛该类(或其子类)。\n *\n * `isExpectedInstallError` 会识别本基类 + 常见 FS 错误(有 errno code 的)+\n * 命名以 `CodexConfigToml` 开头的错误。\n */\nclass CodexInstallError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CodexInstallError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Installer 实现\n// ---------------------------------------------------------------------------\n\nexport class CodexInstaller implements PluginInstaller {\n readonly target = 'codex' as const;\n\n private readonly home: string;\n private readonly rushAiBinaryResolver: () => string | undefined;\n private readonly now: () => Date;\n private readonly marketplaceSource?: CodexInstallerOptions['marketplaceSource'];\n\n constructor(opts: CodexInstallerOptions = {}) {\n this.home = opts.home ?? homedir();\n this.rushAiBinaryResolver =\n opts.rushAiBinaryResolver ?? defaultRushAiBinaryResolver;\n this.now = opts.now ?? (() => new Date());\n this.marketplaceSource = opts.marketplaceSource;\n }\n\n // -------------------------------------------------------------------------\n // detect / isInstalled / list\n // -------------------------------------------------------------------------\n\n async detect(): Promise<boolean> {\n return isDir(codexHomeDir(this.home));\n }\n\n async isInstalled(ref: PluginRef): Promise<boolean> {\n const cfgPath = codexConfigTomlPath(this.home);\n if (!(await pathExists(cfgPath))) return false;\n const { data } = await readCodexConfig(cfgPath);\n const plugins = data.plugins as Record<string, unknown> | undefined;\n if (!plugins || typeof plugins !== 'object') return false;\n const key = pluginSectionKey(ref);\n const entry = plugins[key];\n if (!entry || typeof entry !== 'object') return false;\n // 仅当 enabled=true 视为已装(spec §2.2 约定 rush-ai 装 = 启用)\n return (entry as { enabled?: unknown }).enabled === true;\n }\n\n async list(): Promise<InstalledPlugin[]> {\n const cfgPath = codexConfigTomlPath(this.home);\n if (!(await pathExists(cfgPath))) return [];\n const { data } = await readCodexConfig(cfgPath);\n const plugins = data.plugins as Record<string, unknown> | undefined;\n if (!plugins || typeof plugins !== 'object') return [];\n\n const results: InstalledPlugin[] = [];\n for (const [key, raw] of Object.entries(plugins)) {\n if (!raw || typeof raw !== 'object') continue;\n const entry = raw as { enabled?: unknown };\n if (entry.enabled !== true) continue;\n const ref = parsePluginKey(key);\n if (!ref) continue;\n\n // version + installedAt:从磁盘 cache 目录的 .codex-plugin/plugin.json 推断\n // 若读不到 → 填 fallback 值,list 依旧可用\n const detected = await detectInstalledVersion(this.home, ref);\n results.push({\n ref,\n version: detected?.version ?? 'unknown',\n installedAt: detected?.installedAt ?? new Date(0).toISOString(),\n targets: ['codex'],\n });\n }\n // 字典序稳定输出\n results.sort((a, b) => {\n const ka = `${a.ref.name}@${a.ref.marketplace}`;\n const kb = `${b.ref.name}@${b.ref.marketplace}`;\n return ka < kb ? -1 : ka > kb ? 1 : 0;\n });\n return results;\n }\n\n // -------------------------------------------------------------------------\n // install / uninstall\n // -------------------------------------------------------------------------\n\n async install(\n plugin: ResolvedPlugin,\n opts: InstallOptions = {}\n ): Promise<InstallResult> {\n // spec §2.1 行为契约第一条:detect()=false → skipped(CLI 不预过滤 target)\n if (!(await this.detect())) {\n return {\n target: 'codex',\n status: 'skipped',\n included: [],\n skipped: [],\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n\n // SHOULD-FIX 防御性校验:即便上游 resolver 已约束 PluginRef 字符集,\n // Installer 层再做一次路径穿越防护 —— ref.name / ref.marketplace / version\n // 都不允许含路径分隔符或 `..`。\n const pathGuardError = assertSafePathComponents(plugin.ref, plugin.version);\n if (pathGuardError) {\n return {\n target: 'codex',\n status: 'failed',\n included: [],\n skipped: [],\n artifacts: { files: [], mcpKeys: [] },\n errors: [pathGuardError],\n };\n }\n\n const ref = plugin.ref;\n\n // 幂等:相同版本已装 + force=false → no-op\n if (!opts.force && (await this.isInstalled(ref))) {\n const existingVersion = await detectInstalledVersion(this.home, ref);\n if (existingVersion?.version === plugin.version) {\n return {\n target: 'codex',\n status: 'ok',\n included: computeIncludedCapabilities(plugin),\n skipped: [...CODEX_UNSUPPORTED],\n artifacts: { files: [], mcpKeys: [] },\n notes: [`插件已装(version=${plugin.version}),跳过重复安装`],\n };\n }\n }\n\n // dryRun:只计算、不写盘\n if (opts.dryRun) {\n return this.buildDryRunResult(plugin);\n }\n\n // 真实 install —— 走完整 pipeline 并收集回滚信息\n const rollback: RollbackPlan = {\n createdVersionDir: null,\n createdMarketplacePluginDir: null,\n backupPath: null,\n preExistingConfig: null,\n };\n\n try {\n return await this.doInstall(plugin, rollback);\n } catch (err) {\n // 回滚已写入文件 + 还原 config.toml\n await rollbackInstall(this.home, rollback);\n // 预期错误 → InstallResult.failed;bug 级异常继续上抛\n if (isExpectedInstallError(err)) {\n const message = (err as Error).message;\n return {\n target: 'codex',\n status: 'failed',\n included: [],\n skipped: [],\n artifacts: { files: [], mcpKeys: [] },\n errors: [message],\n notes: rollback.backupPath\n ? [`已从备份恢复 config.toml(${rollback.backupPath} 已删除)`]\n : undefined,\n };\n }\n throw err;\n }\n }\n\n async uninstall(\n ref: PluginRef,\n opts: UninstallOptions = {}\n ): Promise<UninstallResult> {\n if (!(await this.detect())) {\n return {\n target: 'codex',\n status: 'skipped',\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n\n // uninstall 不涉及 version 路径(只改 config.toml),但 ref.* 仍参与 section key\n // 里的 @ + \".\" 组合;做路径穿越守护避免恶意 ref 构造。\n const pathGuardError = assertSafePathComponents(ref, /* version */ null);\n if (pathGuardError) {\n return {\n target: 'codex',\n status: 'failed',\n artifacts: { files: [], mcpKeys: [] },\n errors: [pathGuardError],\n };\n }\n\n const cfgPath = codexConfigTomlPath(this.home);\n if (!(await pathExists(cfgPath))) {\n return {\n target: 'codex',\n status: 'ok',\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n\n const key = pluginSectionKey(ref);\n // 本次会删除哪些 files(不含 cache 目录,对齐 spec §task-8 \"cache 目录保留\")\n const plannedFiles: string[] = [cfgPath];\n\n // 整体 try/catch 包住从 read 开始的所有 IO/TOML 操作,保证任何预期错误\n // (Corrupt / Conflict / FS E*)都转成 status=failed 而非上抛(round-2 MUST-FIX #1)\n let actualBackup: string | null = null;\n try {\n const { data, mtimeMs } = await readCodexConfig(cfgPath);\n const pluginsSection = data.plugins as\n | Record<string, unknown>\n | undefined;\n const pluginExists =\n pluginsSection &&\n typeof pluginsSection === 'object' &&\n key in pluginsSection;\n\n if (!pluginExists) {\n // 未装 → no-op,status=ok(spec §2.1 \"若未装则 status=ok,no-op\")\n return {\n target: 'codex',\n status: 'ok',\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n\n // dryRun:只计算,不写\n if (opts.dryRun) {\n return {\n target: 'codex',\n status: 'ok',\n artifacts: { files: plannedFiles, mcpKeys: [] },\n };\n }\n\n // 真写:备份 + 修改 + 写回\n const backupPath = codexConfigBackupPath(this.home, this.now());\n actualBackup = await backupCodexConfig(cfgPath, backupPath);\n\n removePluginEntry(data, key);\n // 若该 marketplace 已无其他 plugin → 一并删 [marketplaces.<mkt>]\n if (!marketplaceHasOtherPlugins(data, ref.marketplace, key)) {\n removeMarketplaceSection(data, ref.marketplace);\n }\n\n await writeCodexConfig(cfgPath, data, mtimeMs);\n } catch (err) {\n // 失败 → 只有**备份确实发生了**(我们可能已经污染了 config.toml)才 restore。\n // 若 backup 尚未发生(读 config.toml 就挂了,例如 TOML 损坏),**不要**调用\n // restoreCodexConfigFromBackup —— 该函数在 backup=null 时会删除当前文件,\n // 而此时磁盘还是用户原始状态(虽然损坏但可人工修复),删掉 = 数据丢失。\n // Round-3 MUST-FIX #1 修复。\n if (actualBackup !== null) {\n await restoreCodexConfigFromBackup(cfgPath, actualBackup).catch(\n () => {}\n );\n }\n // 对齐 install() 的结构化失败处理:预期错误 → status=failed;bug 级上抛\n if (isExpectedInstallError(err)) {\n return {\n target: 'codex',\n status: 'failed',\n artifacts: { files: [], mcpKeys: [] },\n errors: [(err as Error).message],\n };\n }\n throw err;\n }\n\n // backup 本身也是本次 uninstall 写入的产物 —— 和 install 侧保持对称\n const finalFiles = actualBackup\n ? [...plannedFiles, actualBackup]\n : plannedFiles;\n\n return {\n target: 'codex',\n status: 'ok',\n artifacts: {\n files: finalFiles,\n mcpKeys: [],\n meta: actualBackup ? { backupPath: actualBackup } : undefined,\n },\n };\n }\n\n // -------------------------------------------------------------------------\n // Private —— install 主流程\n // -------------------------------------------------------------------------\n\n private async doInstall(\n plugin: ResolvedPlugin,\n rollback: RollbackPlan\n ): Promise<InstallResult> {\n const ref = plugin.ref;\n const key = pluginSectionKey(ref);\n const versionDir = codexPluginVersionDir(this.home, ref, plugin.version);\n const marketplaceDir = codexMarketplaceDir(this.home, ref.marketplace);\n const marketplacePluginDir = codexMarketplacePluginDir(\n marketplaceDir,\n ref.name\n );\n const cfgPath = codexConfigTomlPath(this.home);\n\n // 1. 准备 version dir —— 如果已存在(force 模式或残留),清理后重建\n const preExisted = await pathExists(versionDir);\n if (preExisted) {\n await rm(versionDir, { recursive: true, force: true });\n }\n await mkdir(versionDir, { recursive: true });\n rollback.createdVersionDir = versionDir;\n\n // 2. 生成 Codex 可扫描的 skills/(支持原生 skills/、Claude .skills/、\n // 以及 slash command markdown 转换)\n const dstSkills = codexPluginSkillsDir(versionDir);\n const writtenFiles: string[] = [versionDir];\n const skillsPlan = await planCodexSkills(plugin);\n if (skillsPlan.hasSkills) {\n await materializeCodexSkills(skillsPlan, dstSkills);\n writtenFiles.push(dstSkills);\n }\n\n // 3. 处理 MCP —— 对象形态生成 `.mcp.json`;字符串形态 copy 作者指定的外部文件\n //\n // 这是 MUST-FIX 3 的策略:不让 `mcpServers: string` 静默丢失 MCP 安装。\n //\n // 分派:\n // - 对象形态(作者内嵌声明)→ rush-ai normalize + 生成 `.mcp.json`\n // - 字符串形态(作者已备好 `.mcp.json`)→ 把源文件 copy 到 versionDir,\n // plugin.json 透传原引用字符串\n // - 无声明(undefined)→ plugin.json 不写 `mcpServers`,不生成 `.mcp.json`\n let mcpJsonRef: string | undefined; // 写入 plugin.json 的字符串引用\n let mcpKeys: string[] = [];\n const srcMcp = plugin.manifest.mcpServers;\n if (srcMcp !== undefined && srcMcp !== null) {\n if (typeof srcMcp === 'string') {\n // 作者自备 .mcp.json:copy 到 versionDir,plugin.json 引用原路径\n const resolvedExternal = await copyAuthorProvidedMcp(\n plugin.sourceDir,\n versionDir,\n srcMcp\n );\n if (resolvedExternal) {\n writtenFiles.push(resolvedExternal.destPath);\n mcpJsonRef = srcMcp;\n mcpKeys = resolvedExternal.mcpKeys;\n } else {\n // 声明了但源文件缺失 / 路径穿越 → 视为预期错误,触发 rollback\n throw new CodexInstallError(\n `plugin.json.mcpServers 引用的外部文件 '${srcMcp}' 不存在或路径非法(相对 ${plugin.sourceDir})`\n );\n }\n } else {\n // 对象形态:走 rush-ai normalize + 生成标准 .mcp.json\n const mcpContent = buildCodexMcpContent(\n ref,\n plugin.manifest,\n this.rushAiBinaryResolver\n );\n if (mcpContent) {\n const mcpPath = codexPluginMcpPath(versionDir);\n await writeFileAtomic(mcpPath, serializeMcpJson(mcpContent));\n writtenFiles.push(mcpPath);\n mcpJsonRef = './.mcp.json';\n mcpKeys = listMcpKeys(mcpContent);\n }\n }\n }\n\n // 写 plugin.json(mcpServers 字段按 mcpJsonRef 决定写不写 / 写什么值)\n const pluginJsonObj = buildCodexPluginJson(plugin.manifest, mcpJsonRef);\n const pluginJsonPath = codexPluginManifestPath(versionDir);\n await writeFileAtomic(\n pluginJsonPath,\n `${JSON.stringify(pluginJsonObj, null, 2)}\\n`\n );\n writtenFiles.push(pluginJsonPath);\n\n // 5. 更新 config.toml —— 备份 + read-modify-write\n const backupDest = codexConfigBackupPath(this.home, this.now());\n const actualBackup = await backupCodexConfig(cfgPath, backupDest);\n rollback.backupPath = actualBackup;\n\n const { data: config, mtimeMs } = await readCodexConfig(cfgPath);\n rollback.preExistingConfig = mtimeMs !== null;\n\n setMarketplaceSection(config, ref.marketplace, {\n last_updated: this.now().toISOString(),\n source_type: 'local',\n source: marketplaceDir,\n });\n setPluginEntry(config, key, { enabled: true });\n\n await writeCodexConfig(cfgPath, config, mtimeMs);\n writtenFiles.push(cfgPath);\n // backup 也算本次写入的副产物 —— spec §1.4 要求 artifacts.files 完整,\n // CLI 卸载 / 审计路径需要知道备份文件位置\n if (actualBackup) {\n writtenFiles.push(actualBackup);\n }\n\n // 6. 生成 Codex 原生 marketplace mirror。插件页读取 marketplace source\n // 下的 `.agents/plugins/marketplace.json`,只写 runtime cache 不会出现在 UI。\n await writeCodexMarketplaceMirror({\n marketplaceDir,\n pluginDir: marketplacePluginDir,\n plugin,\n versionDir,\n });\n rollback.createdMarketplacePluginDir = marketplacePluginDir;\n writtenFiles.push(\n marketplaceDir,\n codexMarketplaceManifestPath(marketplaceDir),\n marketplacePluginDir\n );\n\n // 7. 组装 InstallResult\n const included = computeIncludedCapabilities(plugin);\n\n const notes: string[] = [];\n if (actualBackup) {\n notes.push(`已备份 config.toml 到 ${actualBackup}`);\n }\n\n const artifacts = {\n files: dedup(writtenFiles),\n mcpKeys,\n meta: actualBackup ? { backupPath: actualBackup } : undefined,\n };\n\n return {\n target: 'codex',\n status: 'ok',\n included,\n skipped: [...CODEX_UNSUPPORTED],\n artifacts,\n notes: notes.length > 0 ? notes : undefined,\n };\n }\n\n /**\n * 生成 dryRun InstallResult —— 计算会写入哪些文件 + MCP key,不触磁盘。\n *\n * 和真实 install 行为对齐(round-2 MUST-FIX #2):\n * - mcpServers 对象形态 → 预报 `.mcp.json` 路径 + mcpKeys\n * - mcpServers 字符串形态 → 预报 copy 后的 `<versionDir>/<ref>` 路径 + 从源文件\n * 解析出的 mcpKeys(如果源文件存在且合法);若源文件缺失则仍给出预期写入路径\n * - 无 mcpServers → 不列 MCP 文件\n */\n private async buildDryRunResult(\n plugin: ResolvedPlugin\n ): Promise<InstallResult> {\n const ref = plugin.ref;\n const versionDir = codexPluginVersionDir(this.home, ref, plugin.version);\n const marketplaceDir = codexMarketplaceDir(this.home, ref.marketplace);\n const marketplacePluginDir = codexMarketplacePluginDir(\n marketplaceDir,\n ref.name\n );\n const cfgPath = codexConfigTomlPath(this.home);\n\n const files: string[] = [\n versionDir,\n codexPluginManifestPath(versionDir),\n marketplaceDir,\n codexMarketplaceManifestPath(marketplaceDir),\n marketplacePluginDir,\n cfgPath,\n ];\n\n const skillsPlan = await planCodexSkills(plugin);\n if (skillsPlan.hasSkills) {\n files.push(codexPluginSkillsDir(versionDir));\n }\n\n let mcpKeys: string[] = [];\n const notes: string[] = ['dry-run:未写入磁盘'];\n let dryRunStatus: 'ok' | 'failed' = 'ok';\n const dryRunErrors: string[] = [];\n const srcMcp = plugin.manifest.mcpServers;\n if (srcMcp !== undefined && srcMcp !== null) {\n if (typeof srcMcp === 'string') {\n // 字符串形态:预报 copy 目标路径\n const destPath = resolve(versionDir, srcMcp);\n files.push(destPath);\n const srcPath = resolve(plugin.sourceDir, srcMcp);\n // 和真实 install 对齐(round-3 SHOULD-FIX):路径穿越 / 源文件缺失 →\n // 真实 install 会 failed;dryRun 也应该给 failed,让 CLI 预演输出准确\n const rel = pathRelative(resolve(plugin.sourceDir), srcPath);\n const traversal =\n rel === '..' || rel.startsWith('..') || rel.startsWith('/');\n const exists = await pathExists(srcPath);\n if (traversal || !exists) {\n dryRunStatus = 'failed';\n dryRunErrors.push(\n `plugin.json.mcpServers 引用的外部文件 '${srcMcp}' ${traversal ? '路径非法' : '不存在'}(相对 ${plugin.sourceDir})`\n );\n } else {\n // 源存在 → 尽力解析 mcpKeys(宽容失败,解析错不降级 status)\n try {\n const raw = await readFile(srcPath, 'utf8');\n const parsed = JSON.parse(raw) as {\n mcpServers?: Record<string, unknown>;\n };\n if (parsed.mcpServers && typeof parsed.mcpServers === 'object') {\n mcpKeys = Object.keys(parsed.mcpServers).sort();\n }\n } catch {\n // dryRun 不让解析失败阻塞 status\n }\n }\n } else {\n const mcpContent = buildCodexMcpContent(\n ref,\n plugin.manifest,\n this.rushAiBinaryResolver\n );\n if (mcpContent) {\n files.push(codexPluginMcpPath(versionDir));\n mcpKeys = listMcpKeys(mcpContent);\n }\n }\n }\n\n return {\n target: 'codex',\n status: dryRunStatus,\n included: computeIncludedCapabilities(plugin),\n skipped: [...CODEX_UNSUPPORTED],\n artifacts: { files: dedup(files), mcpKeys },\n notes,\n ...(dryRunErrors.length > 0 ? { errors: dryRunErrors } : {}),\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers —— exported for tests where useful\n// ---------------------------------------------------------------------------\n\n/**\n * 计算本次 install 实际写入的 capabilities(plugin 声明 ∩ Codex 支持)。\n *\n * Codex 支持:`skills` / `mcp`。\n *\n * `rules` / `hooks` 即使 plugin 声明也不写入(跳过),通过\n * `InstallResult.skipped` 明示。\n * Claude-style `commands/*.md` 会转换成 Codex skills。\n */\nexport function computeIncludedCapabilities(\n plugin: ResolvedPlugin\n): PluginCapability[] {\n const included: PluginCapability[] = [];\n if (\n plugin.capabilities.includes('skills') ||\n plugin.capabilities.includes('commands')\n ) {\n included.push('skills');\n }\n if (plugin.capabilities.includes('mcp')) {\n included.push('mcp');\n }\n return included;\n}\n\n/**\n * 生成 Codex plugin.json 内容(spec §2.3)。\n *\n * 处理:\n * - 透传 manifest 里和 Codex 相关的字段(`name`/`version`/`description`/`author`/\n * `homepage`/`license`/`keywords`/`interface`)\n * - **必写** `\"skills\": \"./skills/\"`(决策已定,和 OpenAI bundled 保持一致,\n * 即使 Codex 实测可选)\n * - `mcpServers` 字段写字符串 `\"./.mcp.json\"`(Codex 约定,不能内嵌)。如果\n * plugin 没有 MCP 声明 → 不写 `mcpServers` 字段\n * - **不写** `commands`/`rules`/`hooks`/`repository`——Codex 不使用;\n * command markdown 已在安装阶段转换成 skills\n *\n * 返回的是\"可直接 JSON.stringify 成磁盘文件\"的对象。\n */\nexport function buildCodexPluginJson(\n manifest: PluginManifest,\n mcpServersRef: string | undefined\n): Record<string, unknown> {\n const out: Record<string, unknown> = {\n name: manifest.name,\n version: manifest.version,\n };\n if (manifest.description !== undefined)\n out.description = manifest.description;\n if (manifest.author !== undefined) out.author = manifest.author;\n if (manifest.homepage !== undefined) out.homepage = manifest.homepage;\n if (manifest.license !== undefined) out.license = manifest.license;\n if (manifest.keywords !== undefined) out.keywords = manifest.keywords;\n\n // spec §2.3 决策:skills 字段必写\n out.skills = './skills/';\n\n // mcpServers 字段 —— 字符串引用(只有在 plugin 有 MCP 声明时才写)\n if (mcpServersRef !== undefined) {\n out.mcpServers = mcpServersRef;\n }\n\n // Codex 桌面端展示用。第三方 Claude-style plugin 往往没有 interface,\n // 这里补一份最小可展示 metadata;作者显式字段优先。\n out.interface = buildCodexInterfaceMetadata(manifest);\n\n return out;\n}\n\nexport function buildCodexInterfaceMetadata(\n manifest: PluginManifest\n): NonNullable<PluginManifest['interface']> {\n const existing = manifest.interface ?? {};\n return {\n displayName: existing.displayName ?? manifest.name,\n shortDescription:\n existing.shortDescription ?? manifest.description ?? manifest.name,\n longDescription: existing.longDescription ?? manifest.description,\n developerName: existing.developerName ?? manifest.author?.name,\n category: existing.category ?? 'Engineering',\n capabilities: existing.capabilities ?? ['Read', 'Write'],\n websiteURL: existing.websiteURL ?? manifest.homepage,\n privacyPolicyURL: existing.privacyPolicyURL,\n termsOfServiceURL: existing.termsOfServiceURL,\n defaultPrompt: existing.defaultPrompt,\n brandColor: existing.brandColor,\n composerIcon: existing.composerIcon,\n logo: existing.logo,\n screenshots: existing.screenshots,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\ninterface RollbackPlan {\n createdVersionDir: string | null;\n createdMarketplacePluginDir: string | null;\n backupPath: string | null;\n /** config.toml 在 install 之前是否已存在(决定 restore 策略)。 */\n preExistingConfig: boolean | null;\n}\n\nasync function rollbackInstall(\n home: string,\n rollback: RollbackPlan\n): Promise<void> {\n // 1. 还原 config.toml:\n // - 有 backup → restore(覆盖当前文件,删 backup)\n // - 无 backup + preExistingConfig === false → install 前文件不存在且我们可能刚\n // 写了一份 → 删掉(best-effort)回到 install 前\"无 config\"状态\n // - 无 backup + preExistingConfig !== false → 我们在 read 或更早阶段就失败了,\n // 磁盘仍是用户原始状态,**不动**(MUST-FIX round-3 #1 同构保护)\n const cfgPath = codexConfigTomlPath(home);\n if (rollback.backupPath) {\n await restoreCodexConfigFromBackup(cfgPath, rollback.backupPath).catch(\n () => {}\n );\n } else if (rollback.preExistingConfig === false) {\n // install 之前 config.toml 本不存在;如果我们写了半道失败,删除之\n await rm(cfgPath, { force: true }).catch(() => {});\n }\n // 2. 删除本次创建的 version dir(best-effort)\n if (rollback.createdVersionDir) {\n await rm(rollback.createdVersionDir, {\n recursive: true,\n force: true,\n }).catch(() => {});\n }\n if (rollback.createdMarketplacePluginDir) {\n await rm(rollback.createdMarketplacePluginDir, {\n recursive: true,\n force: true,\n }).catch(() => {});\n }\n}\n\nfunction isExpectedInstallError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n // 允许接受:FS 错误(ENOENT/EACCES/EEXIST 等)、CodexConfigToml*Error、\n // CodexInstallError(Installer 自抛的预期失败信号)\n if (err instanceof CodexInstallError) return true;\n const name = err.name;\n if (name?.startsWith('CodexConfigToml')) return true;\n const code = (err as NodeJS.ErrnoException).code;\n if (typeof code === 'string' && /^E[A-Z]+$/.test(code)) return true;\n return false;\n}\n\n/**\n * 路径分量安全校验(SHOULD-FIX 防御性防护)。\n *\n * `ref.name` / `ref.marketplace` / `version` 都会参与构造 cache 目录路径,\n * 如果上游允许 `../` 或 `/` 进入,`path.resolve` 会把文件写出 `~/.codex/plugins/cache/`\n * 之外。此函数做\"严格白名单\":只允许字母、数字、 `-`、`_`、 `.`、`@`、`+`(version\n * 允许 semver build metadata),且**不得**以 `.` 开头(防 `.`、`..`)。\n *\n * version 的白名单比 name/marketplace 宽 1 个字符(`+`)——对应 semver build\n * metadata `1.2.3+build.1`(round-2 SHOULD-FIX)。\n *\n * 返回 null = 校验通过;返回字符串 = 人类可读的错误消息(供 InstallResult.errors)。\n */\nfunction assertSafePathComponents(\n ref: PluginRef,\n version: string | null\n): string | null {\n const checkWithCharset = (\n value: string,\n label: string,\n charset: RegExp\n ): string | null => {\n if (typeof value !== 'string' || value.length === 0) {\n return `${label} 非法:必须是非空字符串`;\n }\n // 不允许任何路径分隔符 / parent traversal\n if (value.includes('/') || value.includes('\\\\')) {\n return `${label} 非法:不允许路径分隔符(${JSON.stringify(value)})`;\n }\n if (value === '.' || value === '..' || value.startsWith('..')) {\n return `${label} 非法:不允许以 '.' / '..' 开头(${JSON.stringify(value)})`;\n }\n if (!charset.test(value)) {\n return `${label} 非法:字符集不允许(${JSON.stringify(value)})`;\n }\n return null;\n };\n const nameCharset = /^[A-Za-z0-9_.\\-@]+$/;\n // version 额外允许 `+` 支持 semver build metadata(如 `1.2.3+build.1`)\n const versionCharset = /^[A-Za-z0-9_.\\-@+]+$/;\n return (\n checkWithCharset(ref.name, 'plugin name', nameCharset) ??\n checkWithCharset(ref.marketplace, 'marketplace name', nameCharset) ??\n (version !== null\n ? checkWithCharset(version, 'plugin version', versionCharset)\n : null)\n );\n}\n\n/**\n * 处理 `plugin.json.mcpServers: string` 形态 —— 把作者指向的外部文件 copy 到\n * versionDir 对应路径,plugin.json 里透传原字符串引用。\n *\n * 仅支持**相对 plugin sourceDir** 的路径(最常见:`\"./.mcp.json\"`);绝对路径\n * 或含 `../` 逃逸 → 返回 null,调用方会把这视为错误场景触发 rollback。\n *\n * 返回 `{ destPath, mcpKeys }`:destPath = 写入 versionDir 的绝对路径;\n * mcpKeys = 从源文件里解析出的 server key 清单(供 InstallResult.artifacts 使用)。\n * 源文件不存在 / 非法路径 → 返回 null。\n */\nasync function copyAuthorProvidedMcp(\n sourceDir: string,\n versionDir: string,\n relativeRef: string\n): Promise<{ destPath: string; mcpKeys: string[] } | null> {\n // 相对路径规范化\n if (typeof relativeRef !== 'string' || relativeRef.length === 0) return null;\n const srcPath = resolve(sourceDir, relativeRef);\n // 安全校验:必须落在 sourceDir 下\n const rel = pathRelative(resolve(sourceDir), srcPath);\n if (rel === '..' || rel.startsWith('..') || rel.startsWith('/')) {\n return null;\n }\n if (!(await pathExists(srcPath))) return null;\n\n // dst 位置 = versionDir/<relativeRef 解析后路径>\n const destPath = resolve(versionDir, relativeRef);\n await mkdir(dirname(destPath), { recursive: true });\n const raw = await readFile(srcPath, 'utf8');\n await writeFileAtomic(destPath, raw);\n\n // 提取 mcpKeys:宽容解析 JSON 的 mcpServers top-level keys\n let mcpKeys: string[] = [];\n try {\n const parsed = JSON.parse(raw) as { mcpServers?: Record<string, unknown> };\n if (parsed.mcpServers && typeof parsed.mcpServers === 'object') {\n mcpKeys = Object.keys(parsed.mcpServers).sort();\n }\n } catch {\n // 源文件不是合法 JSON —— 不阻塞 install,mcpKeys 留空\n mcpKeys = [];\n }\n return { destPath, mcpKeys };\n}\n\nasync function writeCodexMarketplaceMirror(input: {\n marketplaceDir: string;\n pluginDir: string;\n plugin: ResolvedPlugin;\n versionDir: string;\n}): Promise<void> {\n const pluginParent = dirname(input.pluginDir);\n const pluginBase = basename(input.pluginDir);\n await mkdir(pluginParent, { recursive: true });\n const tmpPluginDir = await mkdtemp(\n resolve(pluginParent, `.${pluginBase}.tmp-`)\n );\n const backupPluginDir = await mkdtemp(\n resolve(pluginParent, `.${pluginBase}.bak-`)\n );\n let hadExistingPluginDir = false;\n let swappedPluginDir = false;\n try {\n await rm(tmpPluginDir, { recursive: true, force: true });\n await rm(backupPluginDir, { recursive: true, force: true }).catch(() => {});\n await cp(input.versionDir, tmpPluginDir, {\n recursive: true,\n dereference: false,\n preserveTimestamps: true,\n verbatimSymlinks: true,\n });\n hadExistingPluginDir = await pathExists(input.pluginDir);\n if (hadExistingPluginDir) {\n await rename(input.pluginDir, backupPluginDir);\n }\n // tmp/backup live beside pluginDir, so the directory swaps stay on one device.\n await rename(tmpPluginDir, input.pluginDir);\n swappedPluginDir = true;\n await upsertCodexMarketplaceManifest(input.marketplaceDir, input.plugin);\n if (hadExistingPluginDir) {\n await rm(backupPluginDir, { recursive: true, force: true });\n }\n } catch (err) {\n await rm(tmpPluginDir, { recursive: true, force: true }).catch(() => {});\n let restoredBackup = false;\n if (hadExistingPluginDir && (await pathExists(backupPluginDir))) {\n let displacedPluginDir: string | null = null;\n try {\n if (await pathExists(input.pluginDir)) {\n displacedPluginDir = await mkdtemp(\n resolve(pluginParent, `.${pluginBase}.failed-`)\n );\n await rm(displacedPluginDir, { recursive: true, force: true });\n await rename(input.pluginDir, displacedPluginDir);\n }\n await rename(backupPluginDir, input.pluginDir);\n restoredBackup = true;\n if (displacedPluginDir) {\n await rm(displacedPluginDir, { recursive: true, force: true }).catch(\n () => {}\n );\n }\n } catch {\n if (\n displacedPluginDir &&\n !(await pathExists(input.pluginDir)) &&\n (await pathExists(displacedPluginDir))\n ) {\n await rename(displacedPluginDir, input.pluginDir).catch(() => {});\n }\n }\n } else if (swappedPluginDir) {\n await rm(input.pluginDir, { recursive: true, force: true }).catch(\n () => {}\n );\n }\n if (restoredBackup || !hadExistingPluginDir) {\n await rm(backupPluginDir, { recursive: true, force: true }).catch(\n () => {}\n );\n }\n throw err;\n }\n}\n\nasync function upsertCodexMarketplaceManifest(\n marketplaceDir: string,\n plugin: ResolvedPlugin\n): Promise<void> {\n const manifestPath = codexMarketplaceManifestPath(marketplaceDir);\n const manifest = await readCodexMarketplaceManifest(manifestPath, {\n name: plugin.ref.marketplace,\n interface: {\n displayName: plugin.ref.marketplace,\n },\n plugins: [],\n });\n const plugins = Array.isArray(manifest.plugins) ? manifest.plugins : [];\n const nextEntry = {\n name: plugin.ref.name,\n source: {\n source: 'local',\n path: `./plugins/${plugin.ref.name}`,\n },\n policy: {\n installation: 'AVAILABLE',\n authentication: 'ON_INSTALL',\n },\n category: buildCodexInterfaceMetadata(plugin.manifest).category,\n };\n const nextPlugins = [\n ...plugins.filter((entry) => entry?.name !== plugin.ref.name),\n nextEntry,\n ].sort((a, b) => a.name.localeCompare(b.name));\n await writeFileAtomic(\n manifestPath,\n `${JSON.stringify({ ...manifest, plugins: nextPlugins }, null, 2)}\\n`\n );\n}\n\ninterface CodexMarketplaceManifest {\n name: string;\n interface?: { displayName?: string };\n plugins: Array<{\n name: string;\n source?: unknown;\n policy?: unknown;\n category?: unknown;\n }>;\n}\n\nasync function readCodexMarketplaceManifest(\n manifestPath: string,\n fallback: CodexMarketplaceManifest\n): Promise<CodexMarketplaceManifest> {\n if (!(await pathExists(manifestPath))) return fallback;\n try {\n const parsed = JSON.parse(await readFile(manifestPath, 'utf8')) as {\n name?: unknown;\n interface?: unknown;\n plugins?: unknown;\n };\n return {\n name: typeof parsed.name === 'string' ? parsed.name : fallback.name,\n interface:\n parsed.interface &&\n typeof parsed.interface === 'object' &&\n !Array.isArray(parsed.interface)\n ? (parsed.interface as CodexMarketplaceManifest['interface'])\n : fallback.interface,\n plugins: Array.isArray(parsed.plugins)\n ? parsed.plugins.filter(isMarketplacePluginEntry)\n : fallback.plugins,\n };\n } catch {\n return fallback;\n }\n}\n\nfunction isMarketplacePluginEntry(\n value: unknown\n): value is CodexMarketplaceManifest['plugins'][number] {\n return (\n !!value &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n typeof (value as { name?: unknown }).name === 'string'\n );\n}\n\ntype SkillSource =\n | { kind: 'copy-skill'; sourceDir: string; skillName: string }\n | { kind: 'markdown'; sourceFile: string; skillName: string };\n\ninterface CodexSkillsPlan {\n sources: SkillSource[];\n hasSkills: boolean;\n}\n\nasync function planCodexSkills(\n plugin: ResolvedPlugin\n): Promise<CodexSkillsPlan> {\n const sources: SkillSource[] = [];\n const usedNames = new Set<string>();\n const coveredAliases = new Set<string>();\n\n const commandFiles = await listMarkdownFiles(\n resolve(plugin.sourceDir, 'commands'),\n {\n recursive: false,\n }\n );\n const commandAliases = new Set(\n await Promise.all(\n commandFiles.map((file) => markdownSkillAlias(plugin.manifest.name, file))\n )\n );\n\n const nativeSkillsDir = resolve(plugin.sourceDir, 'skills');\n if (await hasCodexSkillDir(nativeSkillsDir)) {\n for (const name of await listCodexSkillNames(nativeSkillsDir)) {\n usedNames.add(name);\n coveredAliases.add(skillAlias(plugin.manifest.name, name));\n sources.push({\n kind: 'copy-skill',\n sourceDir: resolve(nativeSkillsDir, name),\n skillName: name,\n });\n }\n } else {\n const dotSkillsDir = resolve(plugin.sourceDir, '.skills');\n const claudeSkills = await listMarkdownFiles(dotSkillsDir);\n const claudeAliases = new Map<string, number>();\n for (const file of claudeSkills) {\n const alias = await markdownSkillAlias(plugin.manifest.name, file);\n claudeAliases.set(alias, (claudeAliases.get(alias) ?? 0) + 1);\n }\n for (const file of claudeSkills) {\n const alias = await markdownSkillAlias(plugin.manifest.name, file);\n if (commandAliases.has(alias)) {\n continue;\n }\n const uniqueAlias =\n (claudeAliases.get(alias) ?? 0) > 1\n ? markdownPathAlias(plugin.manifest.name, dotSkillsDir, file)\n : alias;\n const skillName = uniqueSkillName(\n usedNames,\n `${plugin.manifest.name}-${uniqueAlias}`\n );\n coveredAliases.add(alias);\n coveredAliases.add(uniqueAlias);\n sources.push({ kind: 'markdown', sourceFile: file, skillName });\n }\n }\n\n for (const file of commandFiles) {\n const alias = await markdownSkillAlias(plugin.manifest.name, file);\n if (coveredAliases.has(alias)) {\n continue;\n }\n const skillName = uniqueSkillName(\n usedNames,\n `${plugin.manifest.name}-${alias}`\n );\n coveredAliases.add(alias);\n sources.push({ kind: 'markdown', sourceFile: file, skillName });\n }\n\n return { sources, hasSkills: sources.length > 0 };\n}\n\nasync function materializeCodexSkills(\n plan: CodexSkillsPlan,\n dstSkills: string\n): Promise<void> {\n for (const source of plan.sources) {\n if (source.kind === 'copy-skill') {\n await cp(source.sourceDir, resolve(dstSkills, source.skillName), {\n recursive: true,\n dereference: false,\n preserveTimestamps: true,\n verbatimSymlinks: true,\n });\n continue;\n }\n const raw = await readFile(source.sourceFile, 'utf8');\n const normalized = normalizeSkillMarkdown(raw, source.skillName);\n await writeFileAtomic(\n resolve(dstSkills, source.skillName, 'SKILL.md'),\n normalized\n );\n }\n}\n\nfunction normalizeSkillMarkdown(raw: string, skillName: string): string {\n const text = raw.replace(/\\r\\n/g, '\\n');\n const parsed = splitMarkdownFrontmatter(text);\n if (parsed) {\n const lines = parsed.frontmatter.split('\\n');\n const hasName = lines.some((line) => /^name:\\s*/.test(line));\n const nextLines = hasName\n ? lines.map((line) =>\n /^name:\\s*/.test(line) ? `name: ${skillName}` : line\n )\n : [`name: ${skillName}`, ...lines];\n const frontmatter = nextLines.join('\\n');\n const next = `---\\n${frontmatter}${\n frontmatter.endsWith('\\n') ? '' : '\\n'\n }${parsed.afterFrontmatter}`;\n return next.endsWith('\\n') ? next : `${next}\\n`;\n }\n return `---\\nname: ${skillName}\\ndescription: ${\n firstMarkdownLine(raw) ?? skillName\n }\\n---\\n\\n${text.endsWith('\\n') ? text : `${text}\\n`}`;\n}\n\nfunction splitMarkdownFrontmatter(\n text: string\n): { frontmatter: string; afterFrontmatter: string } | null {\n if (!text.startsWith('---\\n')) return null;\n const lines = text.split('\\n');\n if (lines[0] !== '---') return null;\n let blockScalarIndent: number | null = null;\n for (let i = 1; i < lines.length; i += 1) {\n const line = lines[i] ?? '';\n const indent = leadingSpaces(line);\n if (blockScalarIndent !== null) {\n if (line.trim() === '' || indent > blockScalarIndent) {\n continue;\n }\n blockScalarIndent = null;\n }\n if (line === '---') {\n return {\n frontmatter: lines.slice(1, i).join('\\n'),\n afterFrontmatter: lines.slice(i).join('\\n'),\n };\n }\n if (/^[A-Za-z0-9_.-]+:\\s*[|>]/.test(line)) {\n blockScalarIndent = indent;\n }\n }\n return null;\n}\n\nfunction leadingSpaces(value: string): number {\n const match = value.match(/^ */);\n return match?.[0].length ?? 0;\n}\n\nfunction frontmatterValue(frontmatter: string, key: string): string | null {\n for (const line of frontmatter.split('\\n')) {\n const match = line.match(/^([A-Za-z0-9_.-]+):\\s*(.*)$/);\n if (match?.[1] === key) {\n return match[2]?.trim() ?? '';\n }\n }\n return null;\n}\n\nfunction firstMarkdownLine(raw: string): string | null {\n const line = raw\n .split(/\\r?\\n/)\n .map((value) => value.trim())\n .find((value) => value.length > 0 && value !== '---');\n if (!line) return null;\n return line.replace(/^#+\\s*/, '').slice(0, 140);\n}\n\nasync function hasCodexSkillDir(skillsDir: string): Promise<boolean> {\n return (await listCodexSkillNames(skillsDir)).length > 0;\n}\n\nasync function listCodexSkillNames(skillsDir: string): Promise<string[]> {\n if (!(await isDir(skillsDir))) return [];\n let entries: Dirent[];\n try {\n entries = await readdir(skillsDir, { withFileTypes: true });\n } catch {\n return [];\n }\n const names: string[] = [];\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (await pathExists(resolve(skillsDir, entry.name, 'SKILL.md'))) {\n names.push(entry.name);\n }\n }\n return names.sort();\n}\n\nasync function listMarkdownFiles(\n dir: string,\n opts: { recursive?: boolean } = {}\n): Promise<string[]> {\n if (!(await isDir(dir))) return [];\n const recursive = opts.recursive !== false;\n const out: string[] = [];\n async function walk(current: string): Promise<void> {\n let entries: Dirent[];\n try {\n entries = await readdir(current, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const abs = resolve(current, entry.name);\n if (entry.isDirectory()) {\n if (recursive) await walk(abs);\n continue;\n }\n if (entry.isFile() && extname(entry.name).toLowerCase() === '.md') {\n out.push(abs);\n }\n }\n }\n await walk(dir);\n return out.sort();\n}\n\nfunction uniqueSkillName(used: Set<string>, desired: string): string {\n const base = desired.length > 0 ? desired : 'skill';\n let candidate = base;\n let i = 2;\n while (used.has(candidate)) {\n candidate = `${base}-${i}`;\n i += 1;\n }\n used.add(candidate);\n return candidate;\n}\n\nasync function markdownSkillAlias(\n pluginName: string,\n filePath: string\n): Promise<string> {\n const triggerAlias = extractTriggerAlias(await readFile(filePath, 'utf8'));\n return skillAlias(\n pluginName,\n triggerAlias ?? basename(filePath, extname(filePath))\n );\n}\n\nfunction markdownPathAlias(\n pluginName: string,\n baseDir: string,\n filePath: string\n): string {\n const rel = pathRelative(baseDir, filePath);\n return skillAlias(pluginName, rel.slice(0, -extname(rel).length));\n}\n\nfunction skillAlias(pluginName: string, value: string): string {\n let alias = slugify(value);\n const pluginPrefix = `${slugify(pluginName)}-`;\n if (alias.startsWith(pluginPrefix)) {\n alias = alias.slice(pluginPrefix.length);\n }\n return alias.length > 0 ? alias : 'skill';\n}\n\nfunction extractTriggerAlias(raw: string): string | null {\n const parsed = splitMarkdownFrontmatter(raw.replace(/\\r\\n/g, '\\n'));\n const trigger = parsed\n ? frontmatterValue(parsed.frontmatter, 'trigger')\n : null;\n if (!trigger) return null;\n const match = trigger.match(/\\/[A-Za-z0-9_.-]+:([A-Za-z0-9_.-]+)/);\n return match?.[1] ?? null;\n}\n\nfunction slugify(value: string): string {\n const slug = value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n return slug.length > 0 ? slug : 'skill';\n}\n\nasync function writeFileAtomic(\n filePath: string,\n content: string\n): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n // 写 .tmp → rename\n const tmp = `${filePath}.${Math.random().toString(36).slice(2)}.tmp`;\n try {\n await writeFile(tmp, content, { encoding: 'utf8', flag: 'w' });\n // rename\n const { rename } = await import('node:fs/promises');\n await rename(tmp, filePath);\n } catch (err) {\n await rm(tmp, { force: true }).catch(() => {});\n throw err;\n }\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p, fsConstants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function isDir(p: string): Promise<boolean> {\n try {\n const s = await stat(p);\n return s.isDirectory();\n } catch {\n return false;\n }\n}\n\nfunction dedup<T>(arr: T[]): T[] {\n return Array.from(new Set(arr));\n}\n\nfunction parsePluginKey(key: string): PluginRef | null {\n // 用**最后一个** `@` 拆分(round-2 SHOULD-FIX #2)—— plugin name 白名单允许 `@`\n // (如 npm scoped package `@scope/tool` 形式),marketplace name 不含 `@`。\n // 对 `@a/b@mkt` 这种 key,期望得到 name=`@a/b`、marketplace=`mkt`;对更保险的\n // 常规 `name@mkt` 也等价。\n const at = key.lastIndexOf('@');\n if (at <= 0 || at === key.length - 1) return null;\n return { name: key.slice(0, at), marketplace: key.slice(at + 1) };\n}\n\nasync function detectInstalledVersion(\n home: string,\n ref: PluginRef\n): Promise<{ version: string; installedAt: string } | null> {\n // 扫 `<cache>/<mkt>/<plugin>/` 下的子目录,读 `.codex-plugin/plugin.json` 的 version\n // 取 mtime 最新的目录作为\"当前 version\"\n const baseDir = resolve(\n codexHomeDir(home),\n 'plugins',\n 'cache',\n ref.marketplace,\n ref.name\n );\n if (!(await isDir(baseDir))) return null;\n\n let entries: string[];\n try {\n entries = await readdir(baseDir);\n } catch {\n return null;\n }\n\n let best: { version: string; mtimeMs: number } | null = null;\n for (const entry of entries) {\n const versionDir = resolve(baseDir, entry);\n const manifestPath = resolve(versionDir, '.codex-plugin', 'plugin.json');\n if (!(await pathExists(manifestPath))) continue;\n try {\n const raw = await readFile(manifestPath, 'utf8');\n const obj = JSON.parse(raw) as { version?: unknown };\n const version =\n typeof obj.version === 'string' && obj.version.length > 0\n ? obj.version\n : entry;\n const s = await stat(versionDir);\n if (!best || s.mtimeMs > best.mtimeMs) {\n best = { version, mtimeMs: s.mtimeMs };\n }\n } catch {\n // 单个 version 目录读失败不影响其他——继续\n }\n }\n if (!best) return null;\n return {\n version: best.version,\n installedAt: new Date(best.mtimeMs).toISOString(),\n };\n}\n\nfunction defaultRushAiBinaryResolver(): string | undefined {\n const argv1 = process.argv[1];\n if (typeof argv1 === 'string' && argv1.length > 0) {\n return argv1;\n }\n return undefined;\n}\n\n// 避免 \"unused import\" lint warning(pathRelative 仅用于 readability 的 future-proof)\n// 若真不需要则删;保留是为 rollback 时可能扩展相对路径比对\nvoid pathRelative;\n","/**\n * Codex 外部 `.mcp.json` 生成 + normalize(task-8 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §2.4 + plan §7.1 MCP command 规则。\n *\n * 关键差异(vs Claude Code):\n * - Codex 的 MCP 是**外部文件**:plugin.json 写 `\"mcpServers\": \"./.mcp.json\"`,\n * 真正的 server 声明在 `<versionDir>/.mcp.json` 里\n * - `.mcp.json` 结构:`{ \"mcpServers\": { <name>: { command, args, env, cwd } } }`\n * - `cwd: \".\"` —— phase0-spike S2 实测证明 Codex 会把 cwd 锁定为 plugin cache\n * 版本目录的绝对路径,作者写 `.` 即可(不需要也不应该写绝对路径)\n *\n * command 规范化规则(和 Claude Code 一致):\n * - 仅对 `rush@rush-marketplace` 的 `rush` server key + 非绝对路径 command 调用\n * `rushAiBinaryResolver`,把 command 换成绝对路径\n * - 其他 server key / 其他 plugin / 已经是绝对路径 → **透传不改**\n * - normalize 后**新建**对象,不 mutate 入参(便于测试断言)\n */\n\nimport { isAbsolute } from 'node:path';\nimport type { McpServerConfig, PluginManifest, PluginRef } from '../types.js';\n\n/** rush-ai 自己发布的插件 identifier(和 plugins/resolver.ts 共用约定) */\nexport const RUSH_AI_PLUGIN_NAME = 'rush' as const;\nexport const RUSH_AI_MARKETPLACE_NAME = 'rush-marketplace' as const;\n/** rush plugin 内 MCP server 的 key —— 只对这个 key 做 command normalize */\nexport const RUSH_MCP_SERVER_KEY = 'rush' as const;\n\n/**\n * 由 ResolvedPlugin.manifest 的 MCP 声明生成 `.mcp.json` 内容(JSON 对象)。\n *\n * @param ref 插件引用(用于 normalize 触发条件判断)\n * @param manifest plugin.json 解析结果\n * @param rushAiBinaryResolver 仅对 rush-ai 自身插件生效的 binary 解析器\n *\n * @returns `.mcp.json` 的顶层对象;若 plugin 无 MCP 声明则返回 null\n */\nexport function buildCodexMcpContent(\n ref: PluginRef,\n manifest: PluginManifest,\n rushAiBinaryResolver?: () => string | undefined\n): { mcpServers: Record<string, McpServerConfig> } | null {\n const mcp = manifest.mcpServers;\n if (mcp === undefined || mcp === null) return null;\n\n // Codex 这边我们只支持**对象形态**的 mcpServers——作者如果直接在 plugin.json 里\n // 写了 `\"mcpServers\": \"./.mcp.json\"` 字符串,说明他们自己已经备好 .mcp.json,\n // rush-ai Installer 不应覆盖(此时应由 Installer 层直接 copy 原 .mcp.json 文件)。\n // 这里只处理对象形态 → 生成 .mcp.json。\n if (typeof mcp === 'string') return null;\n\n if (Object.keys(mcp).length === 0) return null;\n\n const normalized = normalizeMcpServers(ref, mcp, rushAiBinaryResolver);\n return { mcpServers: normalized };\n}\n\n/**\n * 返回 `.mcp.json` 里需要注入 cwd 字段的 server config(cwd=\".\" 如果没写)。\n *\n * phase0-spike 实测结论(spec §2.4 S2):Codex 会把 cwd 解析为 plugin cache\n * 版本目录的绝对路径,所以作者写 `\"cwd\": \".\"` 即可。如果原 manifest 没写 cwd\n * 我们显式补 `\".\"`——这样下游调试 `.mcp.json` 时明显看出\"cwd 是有意相对 plugin\n * 版本目录\"而非遗漏。\n */\nfunction normalizeMcpServers(\n ref: PluginRef,\n servers: Record<string, McpServerConfig>,\n rushAiBinaryResolver?: () => string | undefined\n): Record<string, McpServerConfig> {\n const isRushAiPlugin =\n ref.name === RUSH_AI_PLUGIN_NAME &&\n ref.marketplace === RUSH_AI_MARKETPLACE_NAME;\n\n const result: Record<string, McpServerConfig> = {};\n for (const [serverKey, config] of Object.entries(servers)) {\n let finalCommand = config.command;\n\n // rush-ai 自身插件 + rush server key + 非绝对 command → 解析绝对路径\n if (\n isRushAiPlugin &&\n serverKey === RUSH_MCP_SERVER_KEY &&\n typeof finalCommand === 'string' &&\n finalCommand.length > 0 &&\n !isAbsolute(finalCommand)\n ) {\n const resolved = rushAiBinaryResolver?.();\n if (resolved && isAbsolute(resolved)) {\n finalCommand = resolved;\n }\n // 如果 resolver 返回 undefined 或相对路径 → 保持原值不改(对齐 resolver 层\n // \"不挂 installer\" 的保守策略;若确实 command 跑不通,让运行时报错)\n }\n\n // 组装:显式加 cwd: \".\"(如果没写)\n const next: McpServerConfig = {\n ...config,\n command: finalCommand,\n cwd: config.cwd ?? '.',\n };\n result[serverKey] = next;\n }\n return result;\n}\n\n/**\n * 从 `.mcp.json` 内容提取 server key 清单(供 `InstallResult.artifacts.mcpKeys` 使用)。\n */\nexport function listMcpKeys(\n content: { mcpServers: Record<string, unknown> } | null\n): string[] {\n if (!content) return [];\n return Object.keys(content.mcpServers).sort();\n}\n\n/**\n * 序列化 `.mcp.json` 对象为写盘用字符串(稳定格式:2-space 缩进 + 尾换行)。\n *\n * 和 Installer 磁盘约定一致:后续 `diff` / `cat` 得到稳定输出。\n */\nexport function serializeMcpJson(content: {\n mcpServers: Record<string, McpServerConfig>;\n}): string {\n return `${JSON.stringify(content, null, 2)}\\n`;\n}\n","/**\n * Codex Installer 路径 helper。\n *\n * Source of truth: `specs/plugin-schemas.md` §2.1 目录布局。\n *\n * 磁盘布局(全部以 `home` 注入目录为根,生产默认 `os.homedir()`):\n * ```\n * <home>/.codex/\n * ├── config.toml ← TOML 合并文件\n * └── plugins/\n * ├── marketplaces/\n * │ └── <marketplace>/\n * │ ├── .agents/plugins/marketplace.json ← 插件页 marketplace manifest\n * │ └── plugins/<plugin>/... ← 最新 plugin 镜像\n * └── cache/\n * └── <marketplace>/<plugin>/<version>/... ← 运行时 cache\n * ```\n *\n * **测试铁律**:所有单测必须通过 `home` 注入 `os.tmpdir()` 下的临时目录,\n * **禁止**写用户真实 `~/.codex/`。\n */\n\nimport { resolve as pathResolve } from 'node:path';\nimport type { PluginRef } from '../types.js';\n\n/**\n * `<home>/.codex/` 目录。`detect()` 用此判断 Codex 是否安装。\n */\nexport function codexHomeDir(home: string): string {\n return pathResolve(home, '.codex');\n}\n\n/**\n * `<home>/.codex/config.toml` —— 合并 TOML 文件。\n */\nexport function codexConfigTomlPath(home: string): string {\n return pathResolve(codexHomeDir(home), 'config.toml');\n}\n\n/**\n * `<home>/.codex/plugins/cache/` —— plugin cache 根目录。\n */\nexport function codexPluginsCacheDir(home: string): string {\n return pathResolve(codexHomeDir(home), 'plugins', 'cache');\n}\n\n/**\n * `<home>/.codex/plugins/marketplaces/` —— Codex 插件页读取的 marketplace 根。\n */\nexport function codexPluginsMarketplacesDir(home: string): string {\n return pathResolve(codexHomeDir(home), 'plugins', 'marketplaces');\n}\n\n/**\n * 单个 Codex marketplace mirror 目录:\n * `<home>/.codex/plugins/marketplaces/<marketplace>/`。\n */\nexport function codexMarketplaceDir(home: string, marketplace: string): string {\n return pathResolve(codexPluginsMarketplacesDir(home), marketplace);\n}\n\n/**\n * Codex marketplace manifest:\n * `<marketplaceDir>/.agents/plugins/marketplace.json`。\n */\nexport function codexMarketplaceManifestPath(marketplaceDir: string): string {\n return pathResolve(marketplaceDir, '.agents', 'plugins', 'marketplace.json');\n}\n\n/**\n * Marketplace mirror 中单个 plugin 的目录:\n * `<marketplaceDir>/plugins/<plugin>/`。\n */\nexport function codexMarketplacePluginDir(\n marketplaceDir: string,\n pluginName: string\n): string {\n return pathResolve(marketplaceDir, 'plugins', pluginName);\n}\n\n/**\n * 单个 plugin 版本的 cache 目录:\n * `<home>/.codex/plugins/cache/<marketplace>/<plugin>/<version>/`。\n *\n * 对齐 spec §2.1:三段 marketplace/plugin/version 目录,和 Claude Code\n * cache layout 完全同构。\n */\nexport function codexPluginVersionDir(\n home: string,\n ref: PluginRef,\n version: string\n): string {\n return pathResolve(\n codexPluginsCacheDir(home),\n ref.marketplace,\n ref.name,\n version\n );\n}\n\n/**\n * plugin cache 版本目录下的 `.codex-plugin/plugin.json`(Codex 原生约定)。\n */\nexport function codexPluginManifestPath(versionDir: string): string {\n return pathResolve(versionDir, '.codex-plugin', 'plugin.json');\n}\n\n/**\n * plugin cache 版本目录下的外部 `.mcp.json`(Codex 特有——spec §2.4)。\n */\nexport function codexPluginMcpPath(versionDir: string): string {\n return pathResolve(versionDir, '.mcp.json');\n}\n\n/**\n * plugin cache 版本目录下的 `skills/` 子目录。\n */\nexport function codexPluginSkillsDir(versionDir: string): string {\n return pathResolve(versionDir, 'skills');\n}\n\n/**\n * 生成 config.toml 备份路径——`config.toml.bak.<YYYYMMDD-HHMMSS>`。\n *\n * 时间戳使用 UTC 以便多机协作时备份路径可比较(spec §2.2 不要求 local time)。\n */\nexport function codexConfigBackupPath(home: string, now: Date): string {\n const ts = formatBackupTimestamp(now);\n return `${codexConfigTomlPath(home)}.bak.${ts}`;\n}\n\n/**\n * 格式化备份时间戳 `YYYYMMDD-HHMMSS`(UTC)。\n *\n * 导出便于测试。\n */\nexport function formatBackupTimestamp(date: Date): string {\n const pad = (n: number) => String(n).padStart(2, '0');\n const yyyy = date.getUTCFullYear();\n const mm = pad(date.getUTCMonth() + 1);\n const dd = pad(date.getUTCDate());\n const hh = pad(date.getUTCHours());\n const mi = pad(date.getUTCMinutes());\n const ss = pad(date.getUTCSeconds());\n return `${yyyy}${mm}${dd}-${hh}${mi}${ss}`;\n}\n\n/**\n * config.toml 里 marketplace section 的 header key(spec §2.2)。\n * 例如 `\"marketplaces.rush-marketplace\"`。\n */\nexport function marketplaceSectionKey(marketplaceName: string): string {\n return `marketplaces.${marketplaceName}`;\n}\n\n/**\n * config.toml 里 plugin section 的 header key(spec §2.2)。\n * 例如 `plugins.\"rush@rush-marketplace\"`(带引号——@iarna/toml 自动处理)。\n *\n * 返回\"逻辑 key\"——插入 TOML 对象时直接作为嵌套 key 使用,stringify 时\n * @iarna/toml 会自动按 TOML 规范加引号。\n */\nexport function pluginSectionKey(ref: PluginRef): string {\n return `${ref.name}@${ref.marketplace}`;\n}\n","/**\n * `~/.codex/config.toml` 读-改-写(task-8 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §2.2。\n *\n * 关键规则(spec §2.2):\n * - 用 `@iarna/toml` parse / stringify(注释不保留——决策已定)\n * - 写前**必须**备份 `config.toml.bak.<YYYYMMDD-HHMMSS>`\n * - 写前读 mtime,写完后再 stat 确认(并发保护)\n * - 保留用户 sections:`[model_providers.*]` / `[projects.*]` / 顶层字段\n * `model` / `approval_policy` / `sandbox_mode` 等——全部通过 \"read-merge-write\" 保留\n * - 所有写入走 write `.tmp` → `rename` 原子替换\n * - `[plugins.\"<name>@<mkt>\"]` key 含 `@`,@iarna/toml 自动处理引号(已实测)\n *\n * 错误处理:\n * - 文件不存在 → 视为空 config(返回 `{}`)\n * - TOML 解析失败 → 抛 `CodexConfigTomlCorruptError`(CLI 层提示备份路径)\n * - mtime 冲突 → 抛 `CodexConfigTomlConflictError`(让用户重试)\n *\n * 命名约定:\n * - `CodexConfigToml` = 数据 shape(`JsonMap` 别名)\n * - `readCodexConfig` / `writeCodexConfig` = 顶层读写 API\n * - `backupCodexConfig` = 备份当前文件到 `<path>.bak.<ts>`\n * - `setMarketplaceSection` / `removeMarketplaceSection` / `setPluginEntry` / `removePluginEntry`\n * = 结构化的 section 操作函数(纯函数,接受 config → 返回新 config)\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { constants as fsConstants } from 'node:fs';\nimport {\n access,\n copyFile,\n mkdir,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport tomlModule from '@iarna/toml';\n\nconst { parse: tomlParse, stringify: tomlStringify } = tomlModule;\n\n// ---------------------------------------------------------------------------\n// 类型 & 错误\n// ---------------------------------------------------------------------------\n\n/**\n * config.toml 解析后的顶层对象。\n *\n * 不强约束 shape——@iarna/toml 返回的 `JsonMap` 即为本类型,保留用户任意自定义\n * sections / 顶层字段(spec §2.2 明确要求保留 `[model_providers.*]` 等)。\n */\n// @iarna/toml 的 JsonMap 不直接导出,这里用与 stringify 入参相同的 shape:\nexport type CodexConfigToml = Record<string, unknown>;\n\n/** 基类 —— CLI 层统一 `instanceof CodexConfigTomlError` catch。 */\nexport class CodexConfigTomlError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CodexConfigTomlError';\n }\n}\n\n/** TOML 解析失败 —— 指向备份路径供用户处理。 */\nexport class CodexConfigTomlCorruptError extends CodexConfigTomlError {\n constructor(\n public readonly filePath: string,\n public readonly cause: unknown\n ) {\n super(\n `Codex config.toml 解析失败(${filePath})。请备份后手工修复。原因:${String(\n (cause as Error | undefined)?.message ?? cause\n )}`\n );\n this.name = 'CodexConfigTomlCorruptError';\n }\n}\n\n/** 并发写冲突(mtime 在读-改-写期间被其他进程修改)。 */\nexport class CodexConfigTomlConflictError extends CodexConfigTomlError {\n constructor(public readonly filePath: string) {\n super(\n `Codex config.toml 并发写冲突:读取和写入之间 ${filePath} 被其他进程修改。请重试。`\n );\n this.name = 'CodexConfigTomlConflictError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// 读 / 写 / 备份\n// ---------------------------------------------------------------------------\n\n/**\n * 从磁盘读 config.toml + 记录 mtime(用于并发保护)。\n *\n * - 文件不存在 → 返回空配置 `{}`,`mtimeMs = null`\n * - 解析失败 → `CodexConfigTomlCorruptError`\n *\n * 返回的 `data` 是可 mutate 的副本(@iarna/toml 每次 parse 都新建对象)。\n */\nexport async function readCodexConfig(\n filePath: string\n): Promise<{ data: CodexConfigToml; mtimeMs: number | null }> {\n if (!(await pathExists(filePath))) {\n return { data: {}, mtimeMs: null };\n }\n\n const stats = await stat(filePath);\n const raw = await readFile(filePath, 'utf8');\n\n try {\n const parsed = tomlParse(raw);\n return { data: parsed as CodexConfigToml, mtimeMs: stats.mtimeMs };\n } catch (err) {\n throw new CodexConfigTomlCorruptError(filePath, err);\n }\n}\n\n/**\n * 备份当前 config.toml 到 `<path>.bak.<ts>`。\n *\n * - 源文件不存在 → no-op,返回 null(无需备份)\n * - 使用 `copyFile`(非 rename,保留原文件位置给后续 `writeCodexConfig` 覆盖)\n * - 若目标 `.bak.<ts>` 已存在(同秒多次 backup),追加 `.<uuid>` 避免覆盖\n *\n * @returns 备份文件绝对路径;源文件不存在时返回 null\n */\nexport async function backupCodexConfig(\n filePath: string,\n backupPath: string\n): Promise<string | null> {\n if (!(await pathExists(filePath))) {\n return null;\n }\n // 目标已存在 → 附加 uuid,不覆盖已有备份\n let finalBackup = backupPath;\n if (await pathExists(finalBackup)) {\n finalBackup = `${backupPath}.${randomUUID().slice(0, 8)}`;\n }\n await copyFile(filePath, finalBackup);\n return finalBackup;\n}\n\n/**\n * 原子写 config.toml(+ mtime 冲突检测)。\n *\n * 流程:\n * 1. 写前 `stat` 当前磁盘 mtime,与 `expectedMtimeMs` 比较\n * - `null` 意味着我们 load 时文件不存在;此时允许仅当现在依然不存在\n * - 值不等 → `CodexConfigTomlConflictError`(不 retry——spec §2.2 要求\n * \"写前读 mtime,写完后再 stat 确认\";retry 策略留给 Installer 层感知)\n * 2. stringify → write `.tmp` → rename 原子替换\n * 3. 写完后 stat 获取新 mtime 返回(方便 Installer 后续串联操作)\n *\n * **注意**:本函数不处理备份——备份由调用方显式调用 `backupCodexConfig`。\n * 解耦原因:installer.ts 需要在失败回滚时把备份 restore 回来,备份路径必须\n * 由上层管控、透传到 rollback。\n */\nexport async function writeCodexConfig(\n filePath: string,\n data: CodexConfigToml,\n expectedMtimeMs: number | null\n): Promise<{ mtimeMs: number }> {\n await assertNoMtimeDrift(filePath, expectedMtimeMs);\n\n const serialized = tomlStringify(data as Parameters<typeof tomlStringify>[0]);\n await atomicWrite(filePath, serialized);\n\n const afterStats = await stat(filePath);\n return { mtimeMs: afterStats.mtimeMs };\n}\n\n/**\n * 从备份文件恢复 config.toml(用于失败回滚)。\n *\n * - 备份路径为空字符串 / null / 不存在 → 说明 install 前没有原文件,直接删除当前\n * config.toml(如果存在)即可,让磁盘彻底回到\"install 前状态\"\n * - 备份存在 → copy 回去(原子替换)并删掉备份\n */\nexport async function restoreCodexConfigFromBackup(\n filePath: string,\n backupPath: string | null\n): Promise<void> {\n if (!backupPath || !(await pathExists(backupPath))) {\n // 没有备份 → install 前本无 config.toml,回滚 = 清掉当前文件(如果我们写过)\n if (await pathExists(filePath)) {\n await rm(filePath, { force: true });\n }\n return;\n }\n // 有备份 → 原子覆盖当前文件 + 删备份\n const raw = await readFile(backupPath, 'utf8');\n await atomicWrite(filePath, raw);\n await rm(backupPath, { force: true }).catch(() => {});\n}\n\n// ---------------------------------------------------------------------------\n// Section 操作(纯函数,不触磁盘)\n// ---------------------------------------------------------------------------\n\n/**\n * 在 config 上写入 / 更新 `[marketplaces.<name>]` section。\n *\n * - 若 `config.marketplaces` 缺失,自动创建 object\n * - 保留同 `marketplaces` 下其他 marketplace(只更新 `name` 这一个 key)\n *\n * **mutates** 传入对象(调用方已是内存副本,无外部副作用风险)。\n */\nexport function setMarketplaceSection(\n config: CodexConfigToml,\n name: string,\n entry: { last_updated: string; source_type: string; source: string }\n): void {\n const mkts = ensureObjectSection(config, 'marketplaces');\n mkts[name] = { ...entry };\n}\n\n/**\n * 移除 `[marketplaces.<name>]` section。若本就没有 → no-op。\n *\n * 若移除后 `marketplaces` 变空对象,**同步删除父级 `marketplaces` key**——\n * 否则 @iarna/toml stringify 出 `marketplaces = { }` 空 inline table,视觉\n * 和语义上都是污染(regression fix,回归测试 bug #5)。\n */\nexport function removeMarketplaceSection(\n config: CodexConfigToml,\n name: string\n): void {\n const mkts = config.marketplaces as Record<string, unknown> | undefined;\n if (!mkts || typeof mkts !== 'object') return;\n delete mkts[name];\n if (Object.keys(mkts).length === 0) {\n delete config.marketplaces;\n }\n}\n\n/**\n * 在 config 上写入 / 更新 `[plugins.\"<name>@<mkt>\"]` section。\n *\n * - key 带 `@`,stringify 时 @iarna/toml 自动加引号(已实测)\n * - 保留同 `plugins` 下其他 plugin 条目\n * - rush-ai 始终写 `enabled = true`(spec §2.2 约定)\n */\nexport function setPluginEntry(\n config: CodexConfigToml,\n key: string,\n entry: { enabled: boolean }\n): void {\n const plugins = ensureObjectSection(config, 'plugins');\n plugins[key] = { ...entry };\n}\n\n/**\n * 移除 `[plugins.\"<name>@<mkt>\"]` section。若本就没有 → no-op。\n *\n * 若移除后 `plugins` 变空对象,同步删除父级 `plugins` key——避免留下\n * `plugins = { }` 空 inline table 污染 config.toml(regression fix,回归测试 bug #5)。\n */\nexport function removePluginEntry(config: CodexConfigToml, key: string): void {\n const plugins = config.plugins as Record<string, unknown> | undefined;\n if (!plugins || typeof plugins !== 'object') return;\n delete plugins[key];\n if (Object.keys(plugins).length === 0) {\n delete config.plugins;\n }\n}\n\n/**\n * 判断 `[marketplaces.<name>]` 下是否至少还有一个 plugin 使用。\n *\n * Codex Installer uninstall 时用:若某 marketplace 已经没有任何 plugin 安装\n * (说明 rush-ai 不再需要这个 marketplace 注册表项)→ 才允许删该 marketplace section。\n *\n * 实现:扫所有 `[plugins.\"<name>@<mkt>\"]` key,找出后缀 `@<marketplace>` 匹配的条目。\n */\nexport function marketplaceHasOtherPlugins(\n config: CodexConfigToml,\n marketplaceName: string,\n excludedPluginKey: string\n): boolean {\n const plugins = config.plugins as Record<string, unknown> | undefined;\n if (!plugins || typeof plugins !== 'object') return false;\n const suffix = `@${marketplaceName}`;\n for (const key of Object.keys(plugins)) {\n if (key === excludedPluginKey) continue;\n if (key.endsWith(suffix)) return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p, fsConstants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function atomicWrite(filePath: string, content: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n const tmp = `${filePath}.${randomUUID()}.tmp`;\n try {\n await writeFile(tmp, content, { encoding: 'utf8', flag: 'w' });\n await rename(tmp, filePath);\n } catch (err) {\n await rm(tmp, { force: true }).catch(() => {});\n throw err;\n }\n}\n\nasync function assertNoMtimeDrift(\n filePath: string,\n expectedMtimeMs: number | null\n): Promise<void> {\n if (expectedMtimeMs === null) {\n // 我们读取时文件不存在;写入前再看一次,仍不存在才允许\"首次写\"路径\n if (await pathExists(filePath)) {\n throw new CodexConfigTomlConflictError(filePath);\n }\n return;\n }\n const current = await stat(filePath).catch(() => null);\n if (current === null) {\n // 读取时存在,现在消失 → 冲突\n throw new CodexConfigTomlConflictError(filePath);\n }\n if (current.mtimeMs !== expectedMtimeMs) {\n throw new CodexConfigTomlConflictError(filePath);\n }\n}\n\n/**\n * 确保 `config[key]` 是 object(不存在时初始化空 object),返回可 mutate 的引用。\n *\n * 若已存在但不是 object(例如用户手写成了数组或字符串)→ 抛 `CodexConfigTomlError`,\n * 提示不会破坏用户内容(对齐 spec §5.3 \"TOML 解析失败 → 报错\")。\n */\nfunction ensureObjectSection(\n config: CodexConfigToml,\n key: string\n): Record<string, unknown> {\n const existing = config[key];\n if (existing === undefined) {\n const fresh: Record<string, unknown> = {};\n config[key] = fresh;\n return fresh;\n }\n if (\n typeof existing !== 'object' ||\n existing === null ||\n Array.isArray(existing)\n ) {\n throw new CodexConfigTomlError(\n `config.toml 顶层 '${key}' 已存在但不是 object(type=${Array.isArray(existing) ? 'array' : typeof existing})。请手工备份并整理该字段后重试。`\n );\n }\n return existing as Record<string, unknown>;\n}\n","/**\n * Cursor Installer 主类(task-7 产物)。\n *\n * Source of truth:\n * - `specs/plugin-schemas.md` §3(Cursor 摊开适配格式)\n * - `specs/installer-interface.md` §1-§4(Installer 接口契约 / 原子性)\n *\n * 核心设计:\n * - Cursor 没有原生 plugin 容器:skills 走**双写**(skill dir + bridge .mdc rule)\n * - `commands` / `hooks` 能力跳过(Cursor 不支持),`skipped` 字段标明\n * - 冲突检测靠 **marker + registry 双重保险**(见 marker.ts / skills.ts)\n * - 写入失败**完全回滚**(恢复所有已变更文件到写入前状态,返回 `failed`)\n * - 所有写入路径如实回报到 `InstallResult.artifacts.files`(包括被修改的 mcp.json)\n *\n * 测试铁律:constructor 接受 `{ home }` 注入,默认 `os.homedir()`。\n * 所有单测必须注入 tmp HOME,**禁止**写用户真实 `~/.cursor/`。\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { constants as fsConstants } from 'node:fs';\nimport {\n access,\n cp,\n mkdir,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, relative, resolve } from 'node:path';\nimport { RushRegistryStore } from '../registry.js';\nimport type {\n InstalledPlugin,\n InstallOptions,\n InstallResult,\n McpServerConfig,\n PluginCapability,\n PluginInstaller,\n PluginRef,\n ResolvedPlugin,\n UninstallOptions,\n UninstallResult,\n} from '../types.js';\nimport { hasRushAiMarker } from './marker.js';\nimport {\n CursorMcpConflictError,\n mergeMcpServers,\n readCursorMcpJson,\n readCursorMcpJsonWithMtime,\n removeMcpServers,\n writeCursorMcpJson,\n} from './mcp.js';\nimport {\n bridgeSkillFileReference,\n cursorDir,\n cursorMcpJsonPath,\n cursorRuleMdcPath,\n cursorSkillDir,\n} from './paths.js';\nimport { parseMarkdown, renderBridgeRule, renderMdc } from './rules.js';\nimport { resolveSkillDescription } from './skills.js';\n\n// ---------------------------------------------------------------------------\n// 本 Installer 明确跳过的能力(Cursor 不支持 commands / hooks)\n// ---------------------------------------------------------------------------\n\n/**\n * Cursor 不支持的能力(spec §3.5)。\n *\n * 注意:`rules` 能力 Cursor 是支持的(走 .md→.mdc 转换);`skills` 也支持(双写)。\n * 只有 commands / hooks 是硬性不支持。\n */\nconst CURSOR_UNSUPPORTED_CAPABILITIES: readonly PluginCapability[] = [\n 'commands',\n 'hooks',\n] as const;\n\n// ---------------------------------------------------------------------------\n// Installer options\n// ---------------------------------------------------------------------------\n\nexport interface CursorInstallerOptions {\n /**\n * 注入的 HOME 目录。测试里必须传临时路径;生产代码传 `undefined`,默认 `os.homedir()`。\n */\n home?: string;\n}\n\n// ---------------------------------------------------------------------------\n// 回滚 ledger(install 中途失败时用它精确还原)\n// ---------------------------------------------------------------------------\n\n/**\n * 一次 install 期间每次文件变更的记录。失败时按**逆序**重放 undo。\n *\n * 四类:\n * - `restore-file`:目标文件原本存在(pre-existing),需还原到原内容\n * - `remove-file`:目标文件原本不存在,失败后应该删除\n * - `remove-dir`:目标目录原本不存在,失败后应该递归删除\n * - `restore-dir-from-backup`:目标目录原本存在,已被 rename 到 backup 位置,\n * 失败时把 backup 挪回原位(先删新写的空位,再 rename 回来)\n */\ntype RollbackEntry =\n | { kind: 'restore-file'; path: string; originalContent: string }\n | { kind: 'remove-file'; path: string }\n | { kind: 'remove-dir'; path: string }\n | { kind: 'restore-dir-from-backup'; path: string; backupPath: string };\n\n/**\n * 执行回滚:按逆序处理每条 entry,best-effort 清理。\n *\n * 不抛错——回滚过程中的任何错误会被吞掉(日志在 installer 顶层以 `notes` 返回)。\n * 理由:已经进入错误路径,二次抛错只会掩盖原始失败原因。\n */\nasync function executeRollback(ledger: RollbackEntry[]): Promise<string[]> {\n const errors: string[] = [];\n for (let i = ledger.length - 1; i >= 0; i--) {\n const entry = ledger[i];\n try {\n if (entry.kind === 'restore-file') {\n // 走 atomicWriteFile:spec §2.1 \"所有文件写入走写 .tmp → rename\"——\n // 回滚路径也要遵循,避免崩溃留下半写产物\n await atomicWriteFile(entry.path, entry.originalContent);\n } else if (entry.kind === 'remove-file') {\n await rm(entry.path, { force: true });\n } else if (entry.kind === 'remove-dir') {\n await rm(entry.path, { recursive: true, force: true });\n } else {\n // restore-dir-from-backup:先清掉当前占位(copy 写进去的新目录),再把备份搬回\n await rm(entry.path, { recursive: true, force: true });\n await safeRename(entry.backupPath, entry.path);\n }\n } catch (err) {\n errors.push(\n `rollback ${entry.kind} ${entry.path} failed: ${(err as Error).message}`\n );\n }\n }\n return errors;\n}\n\n/**\n * write-temp-rename 原子写入(spec §2.1 \"所有文件写入走写 .tmp → rename\")。\n *\n * 实现细节:\n * - tmp 文件名用 uuid 避免并发冲突(即便同进程多次写同一路径也安全)\n * - mkdir 父目录(递归),对没创建过的路径容错\n * - 失败 best-effort 清 tmp,不覆盖原始错误\n */\nasync function atomicWriteFile(\n filePath: string,\n content: string\n): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n const tmp = `${filePath}.${randomUUID()}.tmp`;\n try {\n await writeFile(tmp, content, { encoding: 'utf8', flag: 'w' });\n await rename(tmp, filePath);\n } catch (err) {\n await rm(tmp, { force: true }).catch(() => {});\n throw err;\n }\n}\n\n/**\n * 读 mcp.json 当前 mtime(ms)。文件不存在返回 `null`。\n *\n * 用于 pre-write CAS —— 在 read-then-write 窗口里再 stat 一次,对比\"read 时\" vs\n * \"write 前\"的 mtime,检测 read-to-write race。\n */\nasync function getMcpMtimeMs(filePath: string): Promise<number | null> {\n try {\n const s = await stat(filePath);\n return s.mtimeMs;\n } catch {\n return null; // 不存在 / 权限错误都当作 \"null\"\n }\n}\n\n/**\n * 两个 mtime 是否\"实质相等\"。null 和 null 相等;null 和数字不等。\n *\n * 用 `Object.is` 而不是 `===` 是为了处理理论上的 `NaN`(虽然 fs.stat 不会返回 NaN)。\n */\nfunction mtimeEqual(a: number | null, b: number | null): boolean {\n if (a === null && b === null) return true;\n if (a === null || b === null) return false;\n return Object.is(a, b);\n}\n\n/**\n * 产出 mcp.json 的**规范化**序列化结果,用于 post-write CAS 内容比对。\n *\n * 必须与 `writeCursorMcpJson` 的实际落盘格式(`JSON.stringify(data, null, 2) + '\\n'`)\n * 完全一致——否则 round-trip 会因为格式差异(空白/尾换行)假冲突。\n *\n * 注:JSON.stringify 对 object 按 key 插入顺序输出,Node 保留插入顺序。\n * 合并策略(`mergeMcpServers`)先拷贝 current、再写 additions,顺序稳定 →\n * 同一 install 调用里 expected == actual 的前提成立。\n */\nfunction serializeMcpJsonForComparison(\n data: Parameters<typeof writeCursorMcpJson>[1]\n): string {\n return `${JSON.stringify(data, null, 2)}\\n`;\n}\n\n/**\n * 判断 `childPath` 是否严格位于 `parentDir` 下(spec §3 Cursor 摊开适配:所有产物路径\n * 必须在注入的 HOME 下的 `.cursor/`)。\n *\n * 防御 registry 被污染时 uninstall 删到 `~/.cursor/` 外。\n * - 允许 `childPath === parentDir`:false(只接受严格子路径)\n * - 符号链接:不做 realpath(避免 I/O + 自锁逻辑)。spec §2.1 \"只 catch 预期错误\",\n * 若用户主动把 `~/.cursor/skills/<x>` symlink 到外部,他们已默认承担该风险。\n * 我们只防 registry.files 含有 `/etc/passwd` 这种明显越界的绝对路径\n */\nfunction isPathWithin(parentDir: string, childPath: string): boolean {\n const relPath = relative(parentDir, childPath);\n if (relPath.length === 0) return false; // 相同路径不算\"在内部\"\n if (relPath.startsWith('..')) return false;\n // Windows: path.relative 在跨盘符时返回绝对路径\n if (/^([a-zA-Z]:)?[\\\\/]/.test(relPath)) return false;\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// 冲突错误 —— 让主流程能用 instanceof 区分\"应该 failed 的冲突\" vs \"意外异常\"\n// ---------------------------------------------------------------------------\n\nclass CursorInstallConflictError extends Error {\n constructor(\n public readonly conflictPath: string,\n message: string\n ) {\n super(message);\n this.name = 'CursorInstallConflictError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Installer 主类\n// ---------------------------------------------------------------------------\n\nexport class CursorInstaller implements PluginInstaller {\n readonly target = 'cursor' as const;\n\n private readonly home: string;\n\n constructor(options: CursorInstallerOptions = {}) {\n this.home = options.home ?? homedir();\n }\n\n // -------------------------------------------------------------------------\n // PluginInstaller 接口\n // -------------------------------------------------------------------------\n\n /**\n * IDE 是否装在这台机器上:`~/.cursor/` 目录存在即可(spec §3 detect)。\n *\n * 必须幂等 + 无副作用。\n */\n async detect(): Promise<boolean> {\n return dirExists(cursorDir(this.home));\n }\n\n /**\n * 通过 rush-ai registry 查询该插件是否已装(Cursor 没有原生清单,完全靠 registry)。\n */\n async isInstalled(ref: PluginRef): Promise<boolean> {\n const store = await RushRegistryStore.load({ home: this.home });\n const entry = store.get(ref);\n return Boolean(entry?.targets?.cursor);\n }\n\n /**\n * 列出当前 target 已装的插件。完全靠 rush-ai registry(Cursor 没自己的清单)。\n */\n async list(): Promise<InstalledPlugin[]> {\n const store = await RushRegistryStore.load({ home: this.home });\n const out: InstalledPlugin[] = [];\n for (const [ref, entry] of store.list()) {\n const cursorEntry = entry.targets?.cursor;\n if (!cursorEntry) continue;\n out.push({\n ref,\n version: cursorEntry.version,\n installedAt: cursorEntry.installedAt,\n targets: ['cursor'],\n });\n }\n return out;\n }\n\n /**\n * 装一个插件。\n *\n * 流程(spec §2.1 行为契约):\n * 1. `detect()` = false → 立即返回 `skipped`\n * 2. dryRun → 计算 \"会写哪些文件 / mcpKeys\",不落盘\n * 3. 真装:按 skills → rules → mcp 顺序写入,记 ledger\n * 4. 冲突 / 写失败 → 按 ledger 逆序回滚,返回 `failed`\n * 5. 成功 → 返回 `ok`,artifacts.files 列出所有被写/修改的绝对路径\n *\n * **原子性**:任何单步失败都会触发 ledger 回滚,磁盘回到 install 前状态。\n */\n async install(\n plugin: ResolvedPlugin,\n opts: InstallOptions = {}\n ): Promise<InstallResult> {\n // 1. detect 短路\n if (!(await this.detect())) {\n return skippedResult();\n }\n\n const { included, skipped } = partitionCapabilities(plugin.capabilities);\n const force = opts.force === true;\n const dryRun = opts.dryRun === true;\n\n // 2. 同版本重复 install 默认 no-op(spec §2.1 \"同版本重复 install:默认 no-op\n // status=ok,artifacts 为空数组,`opts.force=true` 才覆盖\")\n //\n // 判定依据:rush-ai registry.cursor 条目里的 version 等于 plugin.version。\n // Cursor 没有原生清单,靠 registry 独撑。\n //\n // dryRun 仍按 plan 走——调用者可能想看\"如果没装会写什么\"。\n if (!force && !dryRun) {\n const store = await RushRegistryStore.load({ home: this.home });\n const existing = store.get(plugin.ref)?.targets?.cursor;\n if (existing && existing.version === plugin.version) {\n return {\n target: 'cursor',\n status: 'ok',\n included,\n skipped,\n artifacts: { files: [], mcpKeys: [] },\n notes: [\n `rush-ai registry 已记录同版本(${plugin.version})的 cursor 安装,默认 no-op(使用 --force 覆盖)`,\n ],\n };\n }\n }\n\n // 3. 真正写入——记 rollback ledger\n //\n // `planInstall` 本身可能抛 `CursorInstallConflictError`(marker 冲突 / skill 目录\n // 占用),必须在 try/catch 范围内以便统一包装成 status=failed;plan 阶段不会\n // 写磁盘,所以无需 rollback。\n const ledger: RollbackEntry[] = [];\n const writtenFiles: string[] = [];\n const writtenMcpKeys: string[] = [];\n\n try {\n // 预计算:skills / rules / mcp 各自要写哪些 artifact\n const plan = await this.planInstall(plugin, { force });\n\n // dry-run:直接返回计划(不写磁盘)\n if (dryRun) {\n return {\n target: 'cursor',\n status: 'ok',\n included,\n skipped,\n artifacts: {\n files: plan.plannedFiles,\n mcpKeys: plan.plannedMcpKeys,\n },\n notes: plan.conflictNotes,\n };\n }\n // skills\n for (const s of plan.skillsToWrite) {\n await this.writeSkillDir({\n plugin,\n srcDir: s.srcDir,\n destDir: s.destDir,\n ledger,\n force,\n });\n writtenFiles.push(s.destDir);\n\n await this.writeMdcFile({\n path: s.bridgeRulePath,\n content: s.bridgeRuleContent,\n ledger,\n force,\n });\n writtenFiles.push(s.bridgeRulePath);\n }\n\n // rules\n for (const r of plan.rulesToWrite) {\n await this.writeMdcFile({\n path: r.destPath,\n content: r.content,\n ledger,\n force,\n });\n writtenFiles.push(r.destPath);\n }\n\n // mcp.json\n if (plan.mcpAdditions && Object.keys(plan.mcpAdditions).length > 0) {\n const mcpPath = cursorMcpJsonPath(this.home);\n await this.writeMcpMerge({\n mcpPath,\n additions: plan.mcpAdditions,\n ledger,\n });\n writtenFiles.push(mcpPath);\n writtenMcpKeys.push(...Object.keys(plan.mcpAdditions));\n }\n\n return {\n target: 'cursor',\n status: 'ok',\n included,\n skipped,\n artifacts: {\n files: writtenFiles,\n mcpKeys: writtenMcpKeys,\n },\n notes: plan.conflictNotes,\n };\n } catch (err) {\n const rollbackErrors = await executeRollback(ledger);\n const errorMsg =\n err instanceof CursorInstallConflictError\n ? `冲突:${err.message}`\n : `写入失败:${(err as Error).message}`;\n return {\n target: 'cursor',\n status: 'failed',\n included,\n skipped,\n artifacts: { files: [], mcpKeys: [] },\n errors: [errorMsg, ...rollbackErrors],\n };\n }\n }\n\n /**\n * 卸载一个插件。\n *\n * 流程(spec §3.6):\n * 1. detect()=false → skipped\n * 2. 未装(registry 无记录)→ ok no-op\n * 3. 按 registry.cursor.files 逐个处理:\n * - skill dir → 递归删\n * - `.mdc` → 读 marker 确认是 rush-ai 产物再删;用户重写过则跳过 + warn\n * 4. 从 mcp.json 移除 registry.cursor.mcpKeys 里的 keys(写回 mcp.json)\n * 5. dryRun → 只返回 artifacts,不真删\n */\n async uninstall(\n ref: PluginRef,\n opts: UninstallOptions = {}\n ): Promise<UninstallResult> {\n if (!(await this.detect())) {\n return {\n target: 'cursor',\n status: 'skipped',\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n\n const store = await RushRegistryStore.load({ home: this.home });\n const entry = store.get(ref);\n const cursorEntry = entry?.targets?.cursor;\n if (!cursorEntry) {\n return {\n target: 'cursor',\n status: 'ok',\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n\n const dryRun = opts.dryRun === true;\n const removedFiles: string[] = [];\n const removedMcpKeys: string[] = [];\n const warnings: string[] = [];\n\n // `mcp.json` 需要用\"删 key 而非删文件\"语义,单独走下面的 mcpKeys 分支处理,\n // 这里从 files 清单里剔除,避免误把整个 mcp.json 当作 artifact 删掉\n // (artifacts.files 里包含 mcp.json 路径是 spec §1.4 的要求:回报所有被\n // 修改的路径以便 registry 记账,但这不意味着 uninstall 就直接 rm 它)。\n const mcpPath = cursorMcpJsonPath(this.home);\n const cursorRootDir = cursorDir(this.home);\n\n // 1. 文件:skill dir / `.mdc`\n for (const filePath of cursorEntry.files ?? []) {\n if (filePath === mcpPath) continue; // mcp.json 走 mcpKeys 分支\n\n // 防御 registry 被污染:registry.files 里的绝对路径必须严格位于\n // 注入 HOME 下的 `~/.cursor/` 目录内。超出范围 → 跳过 + warn,\n // 绝不删。即便 attacker 在 registry.json 里塞 /etc/passwd 也安全。\n if (!isPathWithin(cursorRootDir, filePath)) {\n warnings.push(\n `跳过 ${filePath}:registry 记录的路径越出 ~/.cursor/ 作用域,疑似脏数据`\n );\n continue;\n }\n\n const info = await classifyArtifactPath(filePath);\n if (info === 'missing') continue;\n\n if (info === 'skill-dir') {\n // skill 目录:直接删(registry 记录即授权——spec §3.3 \"是 rush-ai 装的 → 覆盖\"\n // 的反向操作等价于\"删除\")。\n if (!dryRun) {\n await rm(filePath, { recursive: true, force: true });\n }\n removedFiles.push(filePath);\n continue;\n }\n\n if (info === 'mdc-file') {\n // .mdc:marker 二次确认\n const content = await readFile(filePath, 'utf8').catch(() => '');\n if (!hasRushAiMarker(content)) {\n warnings.push(\n `${filePath} 已被修改(marker 丢失),跳过不动——请用户手动处理`\n );\n continue;\n }\n if (!dryRun) {\n await rm(filePath, { force: true });\n }\n removedFiles.push(filePath);\n continue;\n }\n\n // 其他文件(理论上不应有,防御性处理)\n if (!dryRun) {\n await rm(filePath, { force: true });\n }\n removedFiles.push(filePath);\n }\n\n // 2. mcp.json:移除 registry.cursor.mcpKeys\n const mcpKeys = cursorEntry.mcpKeys ?? [];\n if (mcpKeys.length > 0) {\n const current = await readCursorMcpJson(mcpPath);\n const keysActuallyPresent = mcpKeys.filter(\n (k) => current.mcpServers?.[k] !== undefined\n );\n if (keysActuallyPresent.length > 0) {\n const next = removeMcpServers(current, keysActuallyPresent);\n if (!dryRun) {\n await writeCursorMcpJson(mcpPath, next);\n }\n removedMcpKeys.push(...keysActuallyPresent);\n }\n }\n\n return {\n target: 'cursor',\n status: 'ok',\n artifacts: {\n files: removedFiles,\n mcpKeys: removedMcpKeys,\n },\n ...(warnings.length > 0 ? { errors: warnings } : {}),\n };\n }\n\n // -------------------------------------------------------------------------\n // 内部:install 计划计算\n // -------------------------------------------------------------------------\n\n /**\n * 扫描源 plugin,计算要写的 skill / rule / mcp 列表。\n *\n * 冲突检测在此阶段完成:\n * - `.mdc` 目标已存在 + 无 marker → 抛 `CursorInstallConflictError`\n * - skill 目录已存在 + 未在 registry 里记录 + 未被 force → 抛 `CursorInstallConflictError`\n */\n private async planInstall(\n plugin: ResolvedPlugin,\n args: { force: boolean }\n ): Promise<InstallPlan> {\n const plannedFiles: string[] = [];\n const plannedMcpKeys: string[] = [];\n const skillsToWrite: SkillWritePlan[] = [];\n const rulesToWrite: RuleWritePlan[] = [];\n const conflictNotes: string[] = [];\n\n // 从 registry 查本插件历史,用于\"自己装的 skill 覆盖自己\"判定\n const registryStore = await RushRegistryStore.load({ home: this.home });\n const prevEntry = registryStore.get(plugin.ref);\n const prevFiles = new Set(prevEntry?.targets?.cursor?.files ?? []);\n\n // skills\n if (plugin.capabilities.includes('skills')) {\n const skillsSourceDir = resolve(plugin.sourceDir, 'skills');\n const skillDirs = await listSkillDirs(skillsSourceDir);\n for (const skillName of skillDirs) {\n const srcDir = resolve(skillsSourceDir, skillName);\n const destDir = cursorSkillDir(this.home, skillName);\n const bridgeRulePath = cursorRuleMdcPath(this.home, skillName);\n\n // 冲突:skill 目录存在\n if (await dirExists(destDir)) {\n const isOurs = prevFiles.has(destDir) || args.force;\n if (!isOurs) {\n throw new CursorInstallConflictError(\n destDir,\n `skill 目录已存在且非 rush-ai 装(${destDir})——请先备份或删除`\n );\n }\n }\n\n // 冲突:bridge rule 已存在且无 marker\n await assertMdcWritable(bridgeRulePath, args.force);\n\n const description = await resolveSkillDescription({\n skillSourceDir: srcDir,\n skillName,\n manifest: plugin.manifest,\n });\n const bridgeRuleContent = renderBridgeRule({\n description,\n skillFileReference: bridgeSkillFileReference(skillName),\n });\n\n skillsToWrite.push({\n srcDir,\n destDir,\n bridgeRulePath,\n bridgeRuleContent,\n });\n plannedFiles.push(destDir, bridgeRulePath);\n }\n }\n\n // rules\n if (plugin.capabilities.includes('rules')) {\n const rulesSourceDir = resolve(plugin.sourceDir, 'rules');\n const rulesFiles = await listRuleMdFiles(rulesSourceDir);\n for (const ruleFile of rulesFiles) {\n const srcPath = resolve(rulesSourceDir, ruleFile);\n const ruleName = ruleFile.replace(/\\.md$/i, '');\n const destPath = cursorRuleMdcPath(this.home, ruleName);\n await assertMdcWritable(destPath, args.force);\n\n // skill 双写也会占用 `<name>.mdc`——如果 plugin 同时有 skill 和 rule\n // 撞名,我们保守处理:让 plan 顺序里 skill 先写,rule 会检测到 marker\n // 后允许覆盖(但会产生 notes 让用户知情)\n if (\n skillsToWrite.some((s) => s.bridgeRulePath === destPath) &&\n !args.force\n ) {\n conflictNotes.push(\n `rule \"${ruleName}\" 与 skill \"${ruleName}\" 的 bridge rule 同名,rule 内容会覆盖 bridge rule`\n );\n }\n\n const src = await readFile(srcPath, 'utf8');\n const parsed = parseMarkdown(src);\n const content = renderMdc(parsed, {\n descriptionFallback: plugin.manifest.description ?? ruleName,\n });\n rulesToWrite.push({ srcPath, destPath, content });\n plannedFiles.push(destPath);\n }\n }\n\n // mcp\n const mcpAdditions = extractMcpAdditions(plugin);\n if (mcpAdditions && Object.keys(mcpAdditions).length > 0) {\n plannedFiles.push(cursorMcpJsonPath(this.home));\n plannedMcpKeys.push(...Object.keys(mcpAdditions));\n }\n\n return {\n plannedFiles,\n plannedMcpKeys,\n skillsToWrite,\n rulesToWrite,\n mcpAdditions: mcpAdditions ?? {},\n conflictNotes,\n };\n }\n\n // -------------------------------------------------------------------------\n // 内部:各写入 primitive(都带 rollback ledger 记账)\n // -------------------------------------------------------------------------\n\n private async writeSkillDir(args: {\n plugin: ResolvedPlugin;\n srcDir: string;\n destDir: string;\n ledger: RollbackEntry[];\n force: boolean;\n }): Promise<void> {\n const preExisted = await dirExists(args.destDir);\n await mkdir(dirname(args.destDir), { recursive: true });\n if (preExisted) {\n // 覆盖场景(registry 授权 / force):先把旧目录整体 rename 到 backup,\n // 再把新内容 copy 到原位置。失败时把 backup 挪回原位。\n // 这样回滚无需深度比较 / 重新写入旧文件内容。\n const backup = `${args.destDir}.${process.pid}.${Date.now()}.bak`;\n await safeRename(args.destDir, backup);\n args.ledger.push({\n kind: 'restore-dir-from-backup',\n path: args.destDir,\n backupPath: backup,\n });\n } else {\n // 新建场景:失败时递归删除即可(目录原本不存在)\n args.ledger.push({ kind: 'remove-dir', path: args.destDir });\n }\n await cp(args.srcDir, args.destDir, { recursive: true });\n }\n\n private async writeMdcFile(args: {\n path: string;\n content: string;\n ledger: RollbackEntry[];\n force: boolean;\n }): Promise<void> {\n const preExisted = await fileExists(args.path);\n if (preExisted) {\n const original = await readFile(args.path, 'utf8');\n args.ledger.push({\n kind: 'restore-file',\n path: args.path,\n originalContent: original,\n });\n } else {\n args.ledger.push({ kind: 'remove-file', path: args.path });\n }\n // spec §2.1 \"所有文件写入走写 .tmp → rename\"\n await atomicWriteFile(args.path, args.content);\n }\n\n private async writeMcpMerge(args: {\n mcpPath: string;\n additions: Record<string, McpServerConfig>;\n ledger: RollbackEntry[];\n }): Promise<void> {\n // 并发保护(spec §3.2 没明说并发,但对齐 registry.ts §3.3 的 mtime retry 思路):\n // read-modify-write 中途若文件 mtime 变更(另一 rush-ai 进程或用户手改) →\n // 重试一次:reload 最新磁盘状态,在此基础上重新 merge 本次 additions。\n // 再次冲突则抛 `CursorMcpConflictError`,由上层统一包成 status=failed + rollback。\n\n const preExisted = await fileExists(args.mcpPath);\n if (preExisted) {\n const original = await readFile(args.mcpPath, 'utf8');\n args.ledger.push({\n kind: 'restore-file',\n path: args.mcpPath,\n originalContent: original,\n });\n } else {\n args.ledger.push({ kind: 'remove-file', path: args.mcpPath });\n }\n\n await this.writeMcpMergeWithRetry(args, 0);\n }\n\n /**\n * mcp.json merge + mtime-based CAS(对齐 spec §3.2 + RushRegistryStore §3.3\n * \"read-modify-write 中途 mtime 变化 → retry 1 次\" 的思路)。\n *\n * CAS 语义(两步检测,同时覆盖 pre-read / post-write 两个时间点):\n *\n * 1. **pre-write 检测**(防 read-to-write race):\n * 在 `readCursorMcpJsonWithMtime` 之后、调用 `writeCursorMcpJson` 之前,再 stat 一次\n * mcp.json。若 mtime 已变 → 说明另一个 writer 在我们 \"读完 but 还没 write\" 的窗口\n * 里改了文件,我们的 merge base 已过期。触发 retry。\n *\n * (理论上 read-to-stat 之间仍有微小窗口,但比 read-to-write 小一个数量级——\n * 这是单文件无 fcntl 锁场景的最佳能力。)\n *\n * 2. **post-write 检测**(防 write-to-observe race 及 lost-update):\n * 写完后读回一次,比对磁盘序列化后的 content 是否与本次 `merged` 内容一致。\n * 若不一致 → 说明我们 `rename` 之后又有人 `rename` 上去覆盖了我们。触发 retry。\n * 这个断言等价于\"本次 additions 的 key **以及** 原来 current 里的 key 都还在\n * 且值正确\"——比\"只检查 additions.key 是否存在\"严格得多,可检测到\"他人覆盖导致\n * `existing` 丢失\"的 lost-update 场景。\n *\n * 3. 第 1 轮 retry 仍冲突 → 抛 `CursorMcpConflictError`,外层 try/catch 统一\n * 包成 `status: 'failed'` + rollback。\n *\n * `preMtimeMs === null`(文件原本不存在):\n * - pre-write 检测退化为\"仍然不存在\"(新文件被别人抢先 create 也算冲突)\n * - post-write 检测仍然生效(防他人紧接我们 create 之后再 rename 覆盖)\n */\n private async writeMcpMergeWithRetry(\n args: {\n mcpPath: string;\n additions: Record<string, McpServerConfig>;\n },\n attempt: number\n ): Promise<void> {\n const { data: current, mtimeMs: preMtimeMs } =\n await readCursorMcpJsonWithMtime(args.mcpPath);\n\n // 1. pre-write CAS:再次 stat 拿最新 mtime,对比 preMtimeMs\n const midMtimeMs = await getMcpMtimeMs(args.mcpPath);\n if (!mtimeEqual(preMtimeMs, midMtimeMs)) {\n if (attempt === 0) return this.writeMcpMergeWithRetry(args, 1);\n throw new CursorMcpConflictError(args.mcpPath);\n }\n\n const merged = mergeMcpServers(current, args.additions);\n const expectedSerialized = serializeMcpJsonForComparison(merged);\n await writeCursorMcpJson(args.mcpPath, merged);\n\n // 2. post-write CAS:读回磁盘,对比序列化后的内容\n const { data: post, mtimeMs: postMtimeMs } =\n await readCursorMcpJsonWithMtime(args.mcpPath);\n if (postMtimeMs === null) {\n // 被删了\n if (attempt === 0) return this.writeMcpMergeWithRetry(args, 1);\n throw new CursorMcpConflictError(args.mcpPath);\n }\n const actualSerialized = serializeMcpJsonForComparison(post);\n if (actualSerialized !== expectedSerialized) {\n // 我们写的内容被后续的 writer 覆盖了 → lost-update\n if (attempt === 0) return this.writeMcpMergeWithRetry(args, 1);\n throw new CursorMcpConflictError(args.mcpPath);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// 辅助函数\n// ---------------------------------------------------------------------------\n\n/** Cursor 不支持的能力从 included 里剥离到 skipped,其他保持 included */\nfunction partitionCapabilities(all: PluginCapability[]): {\n included: PluginCapability[];\n skipped: PluginCapability[];\n} {\n const included: PluginCapability[] = [];\n const skipped: PluginCapability[] = [];\n for (const cap of all) {\n if (CURSOR_UNSUPPORTED_CAPABILITIES.includes(cap)) {\n skipped.push(cap);\n } else {\n included.push(cap);\n }\n }\n return { included, skipped };\n}\n\nfunction skippedResult(): InstallResult {\n return {\n target: 'cursor',\n status: 'skipped',\n included: [],\n skipped: [],\n artifacts: { files: [], mcpKeys: [] },\n };\n}\n\nasync function assertMdcWritable(path: string, force: boolean): Promise<void> {\n if (!(await fileExists(path))) return;\n const raw = await readFile(path, 'utf8').catch(() => '');\n if (hasRushAiMarker(raw)) return; // 有 marker = rush-ai 自己的,允许覆盖\n if (force) return; // --force 显式覆盖用户产物(CLI 层 opt-in)\n throw new CursorInstallConflictError(\n path,\n `目标 .mdc 已存在且无 rush-ai marker(${path})——不覆盖用户手写 rule`\n );\n}\n\ninterface InstallPlan {\n plannedFiles: string[];\n plannedMcpKeys: string[];\n skillsToWrite: SkillWritePlan[];\n rulesToWrite: RuleWritePlan[];\n mcpAdditions: Record<string, McpServerConfig>;\n conflictNotes: string[];\n}\n\ninterface SkillWritePlan {\n srcDir: string;\n destDir: string;\n bridgeRulePath: string;\n bridgeRuleContent: string;\n}\n\ninterface RuleWritePlan {\n srcPath: string;\n destPath: string;\n content: string;\n}\n\n/**\n * 从 plugin.manifest.mcpServers 提取要合并进 mcp.json 的 stdio server 记录。\n *\n * Cursor 只接受对象形态(`Record<string, McpServerConfig>`)。若 manifest\n * 写的是字符串(Codex 外部文件引用),Cursor 无法消费——按 spec §3.2 处理:\n * 不写入(视为该 plugin 没声明 cursor 可用的 MCP),不报错。\n */\nfunction extractMcpAdditions(\n plugin: ResolvedPlugin\n): Record<string, McpServerConfig> | null {\n const m = plugin.manifest.mcpServers;\n if (m === undefined || m === null) return null;\n if (typeof m === 'string') return null; // Codex 外部引用,Cursor 不消费\n const out: Record<string, McpServerConfig> = {};\n for (const [key, cfg] of Object.entries(m)) {\n if (cfg && typeof cfg === 'object' && typeof cfg.command === 'string') {\n out[key] = cfg;\n }\n }\n return out;\n}\n\nasync function listSkillDirs(skillsDir: string): Promise<string[]> {\n if (!(await dirExists(skillsDir))) return [];\n const { readdir } = await import('node:fs/promises');\n const entries = await readdir(skillsDir, { withFileTypes: true });\n return entries\n .filter((e) => e.isDirectory())\n .map((e) => e.name)\n .filter((name) => !name.startsWith('.'))\n .sort();\n}\n\nasync function listRuleMdFiles(rulesDir: string): Promise<string[]> {\n if (!(await dirExists(rulesDir))) return [];\n const { readdir } = await import('node:fs/promises');\n const entries = await readdir(rulesDir, { withFileTypes: true });\n return entries\n .filter((e) => e.isFile() && /\\.md$/i.test(e.name))\n .map((e) => e.name)\n .sort();\n}\n\nasync function dirExists(p: string): Promise<boolean> {\n try {\n const s = await stat(p);\n return s.isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function fileExists(p: string): Promise<boolean> {\n try {\n await access(p, fsConstants.F_OK);\n const s = await stat(p);\n return s.isFile();\n } catch {\n return false;\n }\n}\n\nasync function safeRename(from: string, to: string): Promise<void> {\n const { rename } = await import('node:fs/promises');\n await rename(from, to);\n}\n\n/**\n * uninstall 时判定 registry 记录的文件路径是什么——用于决定删除策略。\n *\n * - `.mdc` 文件 → `mdc-file`(走 marker 二次校验)\n * - `mcp.json` 路径(特殊)→ 不会进来(mcp.json 在 mcpKeys 分支处理)\n * - 目录 → `skill-dir`(递归删)\n * - 其他普通文件 → `other-file`(直接 rm)\n * - 不存在 → `missing`(跳过)\n */\nasync function classifyArtifactPath(\n p: string\n): Promise<'mdc-file' | 'skill-dir' | 'other-file' | 'missing'> {\n let s: Awaited<ReturnType<typeof stat>>;\n try {\n s = await stat(p);\n } catch {\n return 'missing';\n }\n if (s.isDirectory()) return 'skill-dir';\n if (s.isFile()) {\n if (/\\.mdc$/i.test(p)) return 'mdc-file';\n return 'other-file';\n }\n return 'missing';\n}\n","/**\n * Cursor Installer 的 marker 机制(task-7 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §3.3 / §3.4。\n *\n * 问题背景:Cursor 没有原生 plugin 清单。rush-ai 装进 `~/.cursor/rules/*.mdc`\n * 的文件需要和用户手写的 rule 区分——否则 uninstall 可能覆盖用户产物。\n *\n * 机制:所有 rush-ai 生成的 `.mdc` 文件在 body 顶部(frontmatter 之后)加一行\n * `<!-- rush-ai:auto-generated -->`。冲突检测:\n *\n * - 目标 `.mdc` 已存在 + 有 marker → 覆盖(认为是 rush-ai 自己的产物)\n * - 目标 `.mdc` 已存在 + 无 marker → 报冲突 failed(不覆盖用户手写 rule)\n *\n * 卸载时双重校验(marker + registry 双重保险,spec §3.6):\n * - registry 记录了该文件路径 + 实际文件有 marker → 删\n * - registry 记录但文件无 marker(用户重写过)→ 跳过 + warn\n */\n\n/** rush-ai 生成产物的 marker 字符串(HTML 注释形式,在 `.mdc` 的 body 里不显示) */\nexport const RUSH_AI_MARKER = '<!-- rush-ai:auto-generated -->' as const;\n\n/**\n * 检测 `.mdc` 文件内容是否包含 rush-ai 生成 marker。\n *\n * 松匹配:只要文本出现过 `<!-- rush-ai:auto-generated -->` 子串就算有(不要求\n * 在特定位置)。这样即使用户在 marker 上方/下方加了自己的内容,我们仍认定\n * 该文件源自 rush-ai(只要不是**删掉** marker)。\n *\n * 如果用户主动删掉 marker(视为\"接管\"该 rule),我们在卸载时会尊重这个\n * 信号:即便 registry 有记录,也跳过 + warn(见 spec §3.6)。\n */\nexport function hasRushAiMarker(fileContent: string): boolean {\n return fileContent.includes(RUSH_AI_MARKER);\n}\n","/**\n * Cursor Installer 的 `~/.cursor/mcp.json` 合并(task-7 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §3.2。\n *\n * 行为:\n * - 读-改-写 `mcp.json`,顶层合并到 `mcpServers`\n * - stdio server 写入完整字段(`type`/`command`/`args`/`env`/`envFile`)。\n * rush-ai 只写 stdio,用户已有的 stdio/remote server 原样保留\n * - 同 key 覆盖(插件提供的优先)\n * - 卸载时只删本插件**明确声明**的 key(来自 InstallArtifacts.mcpKeys)\n *\n * 并发安全:使用 write-temp-rename 原子替换。\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { constants as fsConstants } from 'node:fs';\nimport {\n access,\n mkdir,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport type { McpServerConfig } from '../types.js';\n\n/**\n * `mcp.json` 顶层 shape。其他字段我们不关心,原样透传保留。\n */\nexport interface CursorMcpJson {\n mcpServers?: Record<string, CursorMcpServerEntry>;\n [extra: string]: unknown;\n}\n\n/**\n * Cursor 的 mcp.json 单 server 条目——stdio 或 remote 变体的并集。\n *\n * rush-ai 生成的只用 stdio 字段(`type: 'stdio'` 显式写,或省略——默认 stdio)。\n * remote 变体字段(type: 'http'|'sse' + url/headers/auth)读取时原样保留。\n */\nexport type CursorMcpServerEntry =\n | CursorMcpStdioEntry\n | CursorMcpRemoteEntry\n | CursorMcpUnknownEntry;\n\nexport interface CursorMcpStdioEntry {\n type?: 'stdio';\n command: string;\n args?: string[];\n env?: Record<string, string>;\n envFile?: string;\n cwd?: string;\n [extra: string]: unknown;\n}\n\nexport interface CursorMcpRemoteEntry {\n type: 'http' | 'sse';\n url: string;\n headers?: Record<string, string>;\n auth?: {\n CLIENT_ID?: string;\n CLIENT_SECRET?: string;\n scopes?: string[];\n [extra: string]: unknown;\n };\n [extra: string]: unknown;\n}\n\n/** 宽容形态:字段不全 / 用户手写格式,我们不强制校验,原样保留 */\nexport type CursorMcpUnknownEntry = Record<string, unknown>;\n\n/**\n * 读取 `mcp.json`(文件不存在返回空对象)。\n *\n * JSON 损坏不尝试修复,直接抛错(对齐 spec §5.4 \"无效 JSON → 报错\")。\n */\nexport async function readCursorMcpJson(\n filePath: string\n): Promise<CursorMcpJson> {\n return (await readCursorMcpJsonWithMtime(filePath)).data;\n}\n\n/**\n * 读取 `mcp.json` 并一并返回文件 mtime(ms),用于 read-modify-write 的并发检测。\n *\n * 文件不存在:data = {},mtimeMs = null\n */\nexport async function readCursorMcpJsonWithMtime(\n filePath: string\n): Promise<{ data: CursorMcpJson; mtimeMs: number | null }> {\n if (!(await pathExists(filePath))) {\n return { data: {}, mtimeMs: null };\n }\n const stats = await stat(filePath);\n const raw = await readFile(filePath, 'utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `~/.cursor/mcp.json 解析失败(${filePath}):${(err as Error).message}`\n );\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(`~/.cursor/mcp.json 顶层必须是 object(${filePath})`);\n }\n return { data: parsed as CursorMcpJson, mtimeMs: stats.mtimeMs };\n}\n\n/**\n * mcp.json 并发写冲突。另一个进程在我们 read-modify-write 之间改了文件。\n *\n * 并发保护策略(对齐 RushRegistryStore 的 mtime retry 方案):\n * 调用方可在 catch 到本错误后 reload + re-apply 本次 addition,最多重试一次。\n */\nexport class CursorMcpConflictError extends Error {\n constructor(public readonly filePath: string) {\n super(\n `~/.cursor/mcp.json 并发写冲突:另一个进程修改了 ${filePath}。请重试。`\n );\n this.name = 'CursorMcpConflictError';\n }\n}\n\n/**\n * 把 plugin 声明的 stdio MCP server 合并进 mcp.json 的 `mcpServers` 字段。\n *\n * @param current 当前 mcp.json 内容(readCursorMcpJson 的返回)\n * @param additions plugin 声明的 MCP server,key 覆盖同名已有 server\n * @returns 合并后的 mcp.json(deep-clone,不 mutate 入参)\n */\nexport function mergeMcpServers(\n current: CursorMcpJson,\n additions: Record<string, McpServerConfig>\n): CursorMcpJson {\n // 先 deep-clone 保留未知字段(用户 / 其他工具写入的 remote server、自定义扩展)\n const cloned: CursorMcpJson = structuredClone(current);\n if (!cloned.mcpServers) {\n cloned.mcpServers = {};\n }\n for (const [key, config] of Object.entries(additions)) {\n cloned.mcpServers[key] = toCursorStdioEntry(config);\n }\n return cloned;\n}\n\n/**\n * 把通用 `McpServerConfig`(rush-ai Installer 共用类型)转为 Cursor stdio entry。\n *\n * 显式写入 `type: 'stdio'`(spec §3.2 推荐显式写,规范更清晰),其他字段原样映射。\n *\n * 透传策略(对齐 spec §3.2 stdio server 完整字段 `type`/`command`/`args`/`env`/`envFile`):\n * - 已知字段 `command` / `args` / `env` / `cwd` 显式映射(`McpServerConfig` 接口直接支持)\n * - 其他字段(`envFile` / 上游插件作者自定义的扩展字段)按 forward-compat 策略透传:\n * 通过宽泛索引访问,保留任何 string/object 类型的附加字段。这样当 shared\n * `McpServerConfig` 类型未来扩展(或第三方 plugin 作者写了 Cursor 原生字段但\n * rush-ai 侧还没更新类型)时不会丢信息\n */\nfunction toCursorStdioEntry(config: McpServerConfig): CursorMcpStdioEntry {\n const entry: CursorMcpStdioEntry = {\n type: 'stdio',\n command: config.command,\n };\n if (config.args !== undefined) entry.args = [...config.args];\n if (config.env !== undefined) entry.env = { ...config.env };\n if (config.cwd !== undefined) entry.cwd = config.cwd;\n\n // Forward-compat 透传:`envFile` + 上游作者自定义字段。\n // 已映射的字段跳过避免重复;值深拷贝(对象/数组)防止外部 mutate。\n const handledKeys = new Set(['command', 'args', 'env', 'cwd']);\n const raw = config as unknown as Record<string, unknown>;\n for (const [key, value] of Object.entries(raw)) {\n if (handledKeys.has(key)) continue;\n if (value === undefined) continue;\n entry[key] = deepCopyPrimitive(value);\n }\n return entry;\n}\n\n/** structuredClone 兼容浅层深拷贝(只处理 JSON-safe 值)。 */\nfunction deepCopyPrimitive(v: unknown): unknown {\n if (v === null || typeof v !== 'object') return v;\n try {\n return structuredClone(v);\n } catch {\n return v; // 含 function / symbol 等不可 clone 的值:原样透传\n }\n}\n\n/**\n * 从 mcp.json 删除指定 keys(uninstall 时用)。\n *\n * @param current 当前 mcp.json 内容\n * @param keysToRemove 要删的 top-level MCP server keys(来自 registry 的 mcpKeys)\n * @returns 删改后的 mcp.json(deep-clone)\n */\nexport function removeMcpServers(\n current: CursorMcpJson,\n keysToRemove: string[]\n): CursorMcpJson {\n const cloned: CursorMcpJson = structuredClone(current);\n if (!cloned.mcpServers) return cloned;\n for (const key of keysToRemove) {\n delete cloned.mcpServers[key];\n }\n // 如果 mcpServers 变空了,保留空对象(不删字段——用户可能在意 schema 完整性)\n return cloned;\n}\n\n/**\n * 原子写入 mcp.json。\n *\n * 序列化风格:2-space 缩进,末尾换行(对齐大部分编辑器偏好)。\n */\nexport async function writeCursorMcpJson(\n filePath: string,\n data: CursorMcpJson\n): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n const content = `${JSON.stringify(data, null, 2)}\\n`;\n const tmp = `${filePath}.${randomUUID()}.tmp`;\n try {\n await writeFile(tmp, content, { encoding: 'utf8', flag: 'w' });\n await rename(tmp, filePath);\n } catch (err) {\n await rm(tmp, { force: true }).catch(() => {});\n throw err;\n }\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p, fsConstants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n","/**\n * Cursor Installer 的路径 helper(task-7 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §3.1 Cursor 目录布局。\n *\n * Cursor 没有 plugin 容器,摊开分发到:\n * - `~/.cursor/mcp.json` —— MCP 合并\n * - `~/.cursor/skills/<name>/` —— skill 目录(完整 copy 源)\n * - `~/.cursor/rules/<name>.mdc` —— rules + bridge rule\n *\n * 所有 helper 接受 `home` 参数以便测试注入临时 HOME——**禁止**写用户真实 `~/.cursor/`。\n */\n\nimport { resolve } from 'node:path';\n\n/**\n * `.cursor/` 相对 HOME 的路径(spec §3.1)。\n */\nexport const CURSOR_RELATIVE_DIR = '.cursor' as const;\n\n/** `~/.cursor/` 绝对路径 */\nexport function cursorDir(home: string): string {\n return resolve(home, CURSOR_RELATIVE_DIR);\n}\n\n/** `~/.cursor/mcp.json` 绝对路径 */\nexport function cursorMcpJsonPath(home: string): string {\n return resolve(cursorDir(home), 'mcp.json');\n}\n\n/** `~/.cursor/rules/` 绝对路径 */\nexport function cursorRulesDir(home: string): string {\n return resolve(cursorDir(home), 'rules');\n}\n\n/** `~/.cursor/rules/<name>.mdc` 绝对路径 */\nexport function cursorRuleMdcPath(home: string, ruleName: string): string {\n return resolve(cursorRulesDir(home), `${ruleName}.mdc`);\n}\n\n/** `~/.cursor/skills/` 绝对路径 */\nexport function cursorSkillsDir(home: string): string {\n return resolve(cursorDir(home), 'skills');\n}\n\n/** `~/.cursor/skills/<name>/` 绝对路径 */\nexport function cursorSkillDir(home: string, skillName: string): string {\n return resolve(cursorSkillsDir(home), skillName);\n}\n\n/**\n * bridge rule body 里 `@file` 引用 skill 的相对路径(相对 `~/`)。\n *\n * 对齐 reskill 实践(spec §3.3):`@file .cursor/skills/<name>/SKILL.md`。\n * Cursor 官方接受此形式。\n */\nexport function bridgeSkillFileReference(skillName: string): string {\n return `.cursor/skills/${skillName}/SKILL.md`;\n}\n","/**\n * Cursor Installer 的 rules 转换(task-7 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §3.4。\n *\n * 行为:\n * - 源 `.md` 有 frontmatter → 透传所有字段,补齐 `alwaysApply: false`(缺失时)\n * - 源 `.md` 无 frontmatter → 加默认 frontmatter,`description` 从第一行/标题提取\n * - Body 原样透传,**顶部插入** `<!-- rush-ai:auto-generated -->` marker(见 marker.ts)\n * - 目标扩展名 `.mdc`(Cursor 约定)\n *\n * 设计取舍:\n * - **不引入 yaml 解析库**。rush-ai 的依赖表里没有 yaml;Cursor `.mdc` frontmatter\n * 的字段非常简单(key: value,偶有 multi-line 引号),手工解析 + 序列化即可。\n * 官方 Cursor frontmatter 只定义 3 个字段:`description` / `globs` / `alwaysApply`。\n * 我们对其他字段**原样字符串透传**(保留原行,不解析 value),最大化兼容性。\n * - `alwaysApply` 强制值为布尔,缺失时补 `false`。\n */\n\nimport { RUSH_AI_MARKER } from './marker.js';\n\n/**\n * 分离后的 frontmatter / body。\n */\nexport interface ParsedMarkdown {\n /** null 表示源无 frontmatter */\n readonly frontmatter: FrontmatterFields | null;\n /** frontmatter 之外的 body 内容(不含分隔符 `---`) */\n readonly body: string;\n}\n\n/**\n * 保守的 frontmatter 字段视图。\n *\n * 用 `raw` 保留原始行文本(用于透传未知字段时不丢失格式)。\n * `parsed` 只抽我们关心的字段:`description` / `alwaysApply`。\n * `globs` 等其他字段归入 raw 原样透传。\n */\nexport interface FrontmatterFields {\n /** 解析到的 description(顶层 key: value,不支持 multiline) */\n description?: string;\n /** 解析到的 alwaysApply(true/false 字面) */\n alwaysApply?: boolean;\n /**\n * 所有 frontmatter 行的原始文本(不含开头/结尾的 `---` 分隔符),\n * 按源文件顺序。透传时原样输出,但会被我们覆盖 alwaysApply 行(若需要补齐)。\n */\n rawLines: string[];\n}\n\nconst FRONTMATTER_FENCE = '---';\nconst NEWLINE = '\\n';\n\n/**\n * 解析 Markdown 文本为 frontmatter + body。\n *\n * Frontmatter 判定规则(对齐 Cursor `.mdc` 与常见 Jekyll 约定):\n * - 文件**首行**必须恰好是 `---`\n * - 从下一行起扫描到**下一个** `---` 行(单独一行,不含前后空格)\n * - 未找到结束分隔符 → 视为**无 frontmatter**,整个文件作为 body\n *\n * 为避免在 body 里误解析(比如 body 里用到 `---` 作水平线),我们只在第一个 `---`\n * **紧贴文件开头**时才启用 frontmatter 解析。\n */\nexport function parseMarkdown(text: string): ParsedMarkdown {\n // 使用 split + 保留行号,避免误处理 CRLF(我们统一按 LF 解析,写回时也按 LF;\n // Cursor 生态默认 LF)\n const normalized = text.replace(/\\r\\n/g, '\\n');\n const lines = normalized.split('\\n');\n\n if (lines.length === 0 || lines[0] !== FRONTMATTER_FENCE) {\n return { frontmatter: null, body: normalized };\n }\n\n // 从第 1 行开始找结束分隔符\n let endIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === FRONTMATTER_FENCE) {\n endIdx = i;\n break;\n }\n }\n\n if (endIdx === -1) {\n // 开头是 `---` 但没有结束分隔符 —— 不算 frontmatter,整段当 body\n return { frontmatter: null, body: normalized };\n }\n\n const rawLines = lines.slice(1, endIdx);\n const bodyLines = lines.slice(endIdx + 1);\n const body = bodyLines.join(NEWLINE);\n\n const fields: FrontmatterFields = { rawLines };\n for (const line of rawLines) {\n const colonIdx = line.indexOf(':');\n if (colonIdx <= 0) continue;\n const key = line.slice(0, colonIdx).trim();\n const rawValue = line.slice(colonIdx + 1).trim();\n if (key === 'description') {\n fields.description = stripQuotes(rawValue);\n } else if (key === 'alwaysApply') {\n const lowered = rawValue.toLowerCase();\n if (lowered === 'true') fields.alwaysApply = true;\n else if (lowered === 'false') fields.alwaysApply = false;\n // 其他值(比如 `alwaysApply:` 空)→ 视为未定义,后续会补齐 false\n }\n }\n\n return { frontmatter: fields, body };\n}\n\n/**\n * 剥掉 YAML 风格的 `\"...\"` 或 `'...'` 引号(frontmatter value 常用,实测 Cursor\n * 不强制要求)。不做 escape 还原,保留子字符串——纯粹为生成 bridge rule 时的\n * description 提取用。\n */\nfunction stripQuotes(v: string): string {\n if (v.length >= 2) {\n const first = v.charAt(0);\n const last = v.charAt(v.length - 1);\n if ((first === '\"' && last === '\"') || (first === \"'\" && last === \"'\")) {\n return v.slice(1, -1);\n }\n }\n return v;\n}\n\n/**\n * 从 body 文本第一行或首个 H1/H2 标题提取 description 候选值。\n *\n * 规则:\n * 1. 去掉前导空白 / 空行\n * 2. 第一行若以 `# ` / `## ` 开头,取标题文本\n * 3. 否则取该行原文(截断到 120 字符以内,避免超长)\n * 4. 全空 body → 返回 undefined\n */\nexport function extractDescriptionFromBody(body: string): string | undefined {\n const lines = body.split(NEWLINE);\n for (const rawLine of lines) {\n const line = rawLine.trim();\n if (line.length === 0) continue;\n const headingMatch = /^#{1,6}\\s+(.+)$/.exec(line);\n const candidate = headingMatch ? headingMatch[1].trim() : line;\n if (candidate.length === 0) continue;\n return candidate.length > 120 ? `${candidate.slice(0, 117)}...` : candidate;\n }\n return undefined;\n}\n\n/**\n * 构建 `.mdc` 目标文件内容。\n *\n * @param parsed 从源 `.md` 解析出的 frontmatter + body\n * @param opts.descriptionFallback 当源无 frontmatter 或无 description 时的兜底\n * (一般是 plugin.json.description 或 rule 文件名)\n *\n * 输出结构(固定顺序):\n * ```\n * ---\n * <frontmatter 行,保留源顺序;alwaysApply 缺失时补齐>\n * ---\n *\n * <!-- rush-ai:auto-generated -->\n * <body 原文>\n * ```\n *\n * 行为细则:\n * - 源有 frontmatter:保留 `rawLines` 顺序透传,**仅**当缺 `alwaysApply` 时在\n * rawLines 末尾追加 `alwaysApply: false`。不触碰 `description` / `globs`\n * - 源无 frontmatter:生成最小 frontmatter(`description` + `alwaysApply: false`)\n * 其中 description 先用 body 首行/标题;再用 opts.descriptionFallback;再\n * 用空字符串兜底(但通常 fallback 总能给出值)\n * - Body 顶部**强制**插入 marker 行(紧跟 frontmatter + 一个空行)\n */\nexport function renderMdc(\n parsed: ParsedMarkdown,\n opts: { descriptionFallback: string }\n): string {\n let rawLines: string[];\n if (parsed.frontmatter) {\n rawLines = [...parsed.frontmatter.rawLines];\n if (parsed.frontmatter.alwaysApply === undefined) {\n rawLines.push('alwaysApply: false');\n }\n } else {\n const bodyDescription = extractDescriptionFromBody(parsed.body);\n const description = bodyDescription ?? opts.descriptionFallback;\n rawLines = [\n `description: ${quoteIfNeeded(description)}`,\n 'alwaysApply: false',\n ];\n }\n\n const frontmatterBlock = [\n FRONTMATTER_FENCE,\n ...rawLines,\n FRONTMATTER_FENCE,\n ].join(NEWLINE);\n\n // body 可能本来就以换行开头(源 `.md` frontmatter 之后通常有空行),\n // 统一规范化:frontmatter → 空行 → marker → 空行 → body(去掉 body 的前导空行)\n const normalizedBody = parsed.body.replace(/^[\\n\\r]+/, '');\n\n return `${frontmatterBlock}${NEWLINE}${NEWLINE}${RUSH_AI_MARKER}${NEWLINE}${NEWLINE}${normalizedBody}`;\n}\n\n/**\n * bridge rule 的生成(spec §3.3)。和 `renderMdc` 分开——bridge rule 有固定模板,\n * 不接受源 frontmatter 透传,只用 description 三级 fallback。\n */\nexport function renderBridgeRule(args: {\n description: string;\n skillFileReference: string;\n}): string {\n const rawLines = [\n `description: ${quoteIfNeeded(args.description)}`,\n 'globs:',\n 'alwaysApply: false',\n ];\n const frontmatterBlock = [\n FRONTMATTER_FENCE,\n ...rawLines,\n FRONTMATTER_FENCE,\n ].join(NEWLINE);\n return `${frontmatterBlock}${NEWLINE}${NEWLINE}${RUSH_AI_MARKER}${NEWLINE}@file ${args.skillFileReference}${NEWLINE}`;\n}\n\n/**\n * 如果 description 含冒号、井号、引号等特殊字符,用双引号包起来以保证 YAML 合法。\n *\n * 简化策略:只要包含冒号或以特殊字符开头就加引号;内部双引号转义为 `\\\"`。\n * 这样生成的结果始终是合法 YAML(Cursor 侧实测也接受无引号形式,但我们保守)。\n */\nfunction quoteIfNeeded(value: string): string {\n const needsQuote = /[\":\\n\\r#&*!|>'%@`\\t]/.test(value) || value.length === 0;\n if (!needsQuote) return value;\n const escaped = value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n return `\"${escaped}\"`;\n}\n","/**\n * Cursor Installer 的 skills 双写逻辑(task-7 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §3.3。\n *\n * **关键设计(phase0-spike 实测结论)**:Cursor 没有原生 skills 扫描。\n * 单纯把 `SKILL.md` 放进 `~/.cursor/skills/<name>/` 不会被 Cursor Agent 自动加载。\n *\n * 解决方案 = **双写**:\n * 1. `~/.cursor/skills/<name>/SKILL.md`(+ 附属文件)完整 copy 源目录\n * 2. `~/.cursor/rules/<name>.mdc` 生成 bridge rule,frontmatter 带 description,\n * body 用 `@file` 引入 SKILL.md\n *\n * bridge rule 的 `description` 三级 fallback(spec §3.3,顺序固定):\n * 1. 源 `SKILL.md` 的 frontmatter.description\n * 2. 源 `plugin.json` 的 description\n * 3. skill 名本身(如 `\"rush-task\"`)\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport type { PluginManifest } from '../types.js';\nimport { parseMarkdown } from './rules.js';\n\n/**\n * 从源 SKILL.md 提取 frontmatter.description。\n *\n * @param skillSourceDir 源 skill 目录绝对路径(`<plugin>/skills/<name>/`)\n * @returns description 字符串 | undefined(文件不存在或无 frontmatter.description 都返回 undefined)\n */\nexport async function readSkillDescription(\n skillSourceDir: string\n): Promise<string | undefined> {\n const skillMdPath = resolve(skillSourceDir, 'SKILL.md');\n let raw: string;\n try {\n raw = await readFile(skillMdPath, 'utf8');\n } catch {\n return undefined;\n }\n const parsed = parseMarkdown(raw);\n const desc = parsed.frontmatter?.description?.trim();\n if (!desc) return undefined;\n return desc;\n}\n\n/**\n * description 三级 fallback 解析(spec §3.3 顺序固定)。\n *\n * @param skillSourceDir 源 skill 目录\n * @param skillName skill 名(fallback 3)\n * @param manifest plugin.json manifest(fallback 2 用它的 description)\n */\nexport async function resolveSkillDescription(args: {\n skillSourceDir: string;\n skillName: string;\n manifest: PluginManifest;\n}): Promise<string> {\n // 1. 源 SKILL.md frontmatter.description\n const fromSkill = await readSkillDescription(args.skillSourceDir);\n if (fromSkill) return fromSkill;\n\n // 2. plugin.json.description\n const fromManifest = args.manifest.description?.trim();\n if (fromManifest) return fromManifest;\n\n // 3. skill 名本身\n return args.skillName;\n}\n","/**\n * 清理老版本 rush-ai 磁盘痕迹(task-12)。\n *\n *\n * **调用时机铁律**(plan §4.3 / spec §10 风险矩阵):\n * 本模块**只能**在新格式已成功安装且验证通过**之后**调用——绝对不允许\n * \"先清旧再装新\"。调用方(`migrate.ts`)负责严格遵守顺序。\n *\n * 清理项:\n * 1. 删除目录 `~/.rush/plugins/claude-code/`(递归)\n * 2. `unlink` `~/.claude/skills/rush-task`(仅当它是 symlink;若是目录/文件为安全起见跳过)\n * 3. 从 `~/.claude/settings.json` 移除 `mcpServers.rush`(读-改-写,原子替换)\n *\n * 每个清理子步骤独立 try/catch,**任一子步骤失败不影响其他**——整体迁移已经\n * 成功(新格式装好+验证通过),清理残留只是 best-effort。失败信息通过返回值传递\n * 让调用方记录,不抛错。\n */\n\nimport type { Stats } from 'node:fs';\nimport { lstat, rm, unlink } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { resolve } from 'node:path';\nimport {\n readJsonFile,\n writeJsonFile,\n} from '../installers/claude-code/atomic-json.js';\nimport {\n CLAUDE_SETTINGS_JSON_RELATIVE,\n LEGACY_CLAUDE_CODE_DIR_RELATIVE,\n LEGACY_SKILL_SYMLINK_RELATIVE,\n} from './detect.js';\n\nexport interface CleanupLegacyOptions {\n /** 注入 HOME(测试必填) */\n home?: string;\n}\n\n/**\n * 清理结果。`ok=true` 代表该步骤全部完成或无需执行(幂等成功);\n * `ok=false` 代表发生错误——`error` 字段描述原因。\n */\nexport interface CleanupStepResult {\n readonly ok: boolean;\n readonly error?: string;\n}\n\n/**\n * 整体清理结果——各子步骤独立记录,任一失败不中断其他步骤。\n */\nexport interface CleanupLegacyResult {\n readonly legacyDir: CleanupStepResult;\n readonly skillSymlink: CleanupStepResult;\n readonly settingsMcp: CleanupStepResult;\n}\n\n// ---------------------------------------------------------------------------\n// 各子步骤\n// ---------------------------------------------------------------------------\n\n/**\n * 删 `~/.rush/plugins/claude-code/`(递归)。\n *\n * 不存在 → no-op 视为成功。\n */\nasync function cleanupLegacyDir(home: string): Promise<CleanupStepResult> {\n const dir = resolve(home, LEGACY_CLAUDE_CODE_DIR_RELATIVE);\n try {\n await rm(dir, { recursive: true, force: true });\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n error: `remove ${dir} failed: ${errorMessage(err)}`,\n };\n }\n}\n\n/**\n * `unlink` `~/.claude/skills/rush-task`。\n *\n * 安全:只在 path 是 symlink 时才 unlink;普通目录/文件**不动**(避免误删用户自建内容)。\n * 不存在 → no-op 成功。\n */\nasync function cleanupLegacySymlink(home: string): Promise<CleanupStepResult> {\n const path = resolve(home, LEGACY_SKILL_SYMLINK_RELATIVE);\n try {\n let lst: Stats;\n try {\n lst = await lstat(path);\n } catch (err) {\n if (isNotFound(err)) return { ok: true };\n throw err;\n }\n if (!lst.isSymbolicLink()) {\n // 保守:非 symlink 不动。返回 ok=true 让 migrate 主流程继续——\n // 从用户视角看\"symlink 已不存在\"也算完成。\n return { ok: true };\n }\n await unlink(path);\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n error: `unlink ${path} failed: ${errorMessage(err)}`,\n };\n }\n}\n\n/**\n * 从 `~/.claude/settings.json` 移除 `mcpServers.rush`(并在 `mcpServers` 变空\n * 后保留空对象——Claude Code schema 允许空 `mcpServers`,且保留能防止后续\n * 写入时 key 排序变化产生的 diff)。\n *\n * 各情形返回值:\n * - settings.json 不存在 → `ok:true`(no-op 成功)\n * - 文件存在但无 `mcpServers` / 无 `mcpServers.rush` → `ok:true`(no-op 成功)\n * - 文件存在但 JSON 损坏 → `ok:false` + error(让 summarizeCleanup 捕获;\n * 我们不尝试\"智能修复\"损坏的用户配置,对齐 plan §10 风险矩阵)\n * - 写入 IO 失败 → `ok:false` + error\n *\n * 不做 mtime 冲突保护(settings.json 没有 mtime 契约)——简单原子 rename 足够。\n */\nasync function cleanupLegacyMcp(home: string): Promise<CleanupStepResult> {\n const path = resolve(home, CLAUDE_SETTINGS_JSON_RELATIVE);\n try {\n const { data, existed } = await readJsonFile<Record<string, unknown>>(\n path,\n () => ({})\n );\n if (!existed) return { ok: true };\n\n const mcpServers = data.mcpServers;\n if (\n !mcpServers ||\n typeof mcpServers !== 'object' ||\n Array.isArray(mcpServers)\n ) {\n return { ok: true };\n }\n const mcpObj = mcpServers as Record<string, unknown>;\n if (!Object.hasOwn(mcpObj, 'rush')) {\n return { ok: true };\n }\n\n // 构造删除后的新视图(不变字段原样透传)\n const { rush: _removed, ...rest } = mcpObj;\n void _removed;\n const next: Record<string, unknown> = { ...data, mcpServers: rest };\n await writeJsonFile(path, next);\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n error: `settings.json cleanup failed: ${errorMessage(err)}`,\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// 主 API\n// ---------------------------------------------------------------------------\n\n/**\n * 清理所有老版本 rush-ai 痕迹。**不抛**——所有失败走返回值。\n *\n * 调用前提:新格式已装好且通过验证(plan §4.3 严格顺序)。\n */\nexport async function cleanupLegacyInstall(\n opts: CleanupLegacyOptions = {}\n): Promise<CleanupLegacyResult> {\n const home = opts.home ?? homedir();\n // 三步独立 await,一个失败不影响其他——仍尽量把能清的都清掉。\n // 串行而非并行——避免三个写操作相互影响(settings.json 写入可能触发别的 IO)\n const legacyDir = await cleanupLegacyDir(home);\n const skillSymlink = await cleanupLegacySymlink(home);\n const settingsMcp = await cleanupLegacyMcp(home);\n return { legacyDir, skillSymlink, settingsMcp };\n}\n\n/**\n * 把 `CleanupLegacyResult` 总结成一个 `failed: boolean, reasons: string[]` 的\n * 便于日志记录的 shape。\n */\nexport function summarizeCleanup(result: CleanupLegacyResult): {\n failed: boolean;\n reasons: string[];\n} {\n const reasons: string[] = [];\n if (!result.legacyDir.ok && result.legacyDir.error) {\n reasons.push(result.legacyDir.error);\n }\n if (!result.skillSymlink.ok && result.skillSymlink.error) {\n reasons.push(result.skillSymlink.error);\n }\n if (!result.settingsMcp.ok && result.settingsMcp.error) {\n reasons.push(result.settingsMcp.error);\n }\n return { failed: reasons.length > 0, reasons };\n}\n\n// ---------------------------------------------------------------------------\n// internals\n// ---------------------------------------------------------------------------\n\nfunction isNotFound(err: unknown): boolean {\n return (\n typeof err === 'object' &&\n err !== null &&\n (err as { code?: string }).code === 'ENOENT'\n );\n}\n\nfunction errorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n","/**\n * 老版本 rush-ai(0.7.x)磁盘状态检测(task-12)。\n *\n *\n * 三个\"旧格式标记\"(or 关系,任一命中即触发迁移):\n * 1. `~/.rush/plugins/claude-code/asset-manifest.json` 存在\n * 2. `~/.claude/skills/rush-task` 是 symlink 且指向 `~/.rush/plugins/claude-code/assets`\n * (或其子目录)\n * 3. `~/.claude/settings.json` 有 `mcpServers.rush`,但 `enabledPlugins` 中\n * **不存在任何 `rush@*` 键** —— 即旧格式的 MCP 注册尚未被新格式接管\n *\n * 所有检测函数:\n * - 都走注入的 `home` 目录,便于测试\n * - 对不存在 / 损坏 / 权限错等返回 false 或中性值,不抛错(detect 不允许 throw)\n * - 不写磁盘,纯读\n */\n\nimport { access, lstat, readFile, readlink, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { isAbsolute, resolve } from 'node:path';\n\n/** 老版本 rush-ai 的资源清单路径(相对 HOME)。 */\nexport const LEGACY_ASSET_MANIFEST_RELATIVE =\n '.rush/plugins/claude-code/asset-manifest.json' as const;\n\n/** 老版本 rush-ai 的资源目录(相对 HOME)——symlink 目标比对用。 */\nexport const LEGACY_ASSETS_DIR_RELATIVE =\n '.rush/plugins/claude-code/assets' as const;\n\n/** 老版本 rush-ai 的整个 plugins/claude-code 目录(清理时整块删)。 */\nexport const LEGACY_CLAUDE_CODE_DIR_RELATIVE =\n '.rush/plugins/claude-code' as const;\n\n/** 老版本 Claude Code skills symlink 的绝对路径(相对 HOME)。 */\nexport const LEGACY_SKILL_SYMLINK_RELATIVE =\n '.claude/skills/rush-task' as const;\n\n/** `~/.claude/settings.json`(相对 HOME)。 */\nexport const CLAUDE_SETTINGS_JSON_RELATIVE = '.claude/settings.json' as const;\n\nexport interface DetectLegacyOptions {\n /** 注入 HOME(测试必填,生产默认 `os.homedir()`) */\n home?: string;\n}\n\n/**\n * 具体检测到的老版本痕迹——用于 migrate/cleanup 决策和日志。\n */\nexport interface LegacyDetectionResult {\n /** 任一触发条件命中即 true。 */\n readonly detected: boolean;\n /** 条件 1:`~/.rush/plugins/claude-code/asset-manifest.json` 存在。 */\n readonly hasAssetManifest: boolean;\n /** 条件 2:`~/.claude/skills/rush-task` 是指向老 assets 目录的 symlink。 */\n readonly hasLegacySkillSymlink: boolean;\n /** 条件 3:settings.json 有 `mcpServers.rush` 但无 `enabledPlugins[\"rush@*\"]`。 */\n readonly hasLegacyMcpWithoutEnabled: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * 判断给定 symlink 是否指向 `expectedTarget` 或其子路径。\n *\n * symlink target 可能是相对路径,需要基于 symlink 所在目录解析为绝对路径再比对。\n * 非 symlink / symlink 损坏 / 读不到 → 返回 false(不抛)。\n */\nasync function isSymlinkPointingInto(\n linkPath: string,\n expectedTarget: string\n): Promise<boolean> {\n try {\n const lst = await lstat(linkPath);\n if (!lst.isSymbolicLink()) return false;\n const rawTarget = await readlink(linkPath);\n const resolvedTarget = isAbsolute(rawTarget)\n ? rawTarget\n : resolve(linkPath, '..', rawTarget);\n // 匹配完全相等 或 指向 expectedTarget 的子路径(path + '/' 前缀)\n return (\n resolvedTarget === expectedTarget ||\n resolvedTarget.startsWith(`${expectedTarget}/`)\n );\n } catch {\n return false;\n }\n}\n\n/**\n * 读 `~/.claude/settings.json`,判断 `mcpServers.rush` 是否存在 + `enabledPlugins`\n * 是否缺少任何 `rush@*` 键。\n *\n * 读失败 / 损坏 / 缺文件 → 返回 false(不抛)。\n * detect 不能因为用户 settings.json 损坏就挂掉——那是迁移要不要触发的判断,\n * 真正迁移时若再失败会进入失败分支。\n */\nasync function hasLegacyMcpWithoutEnabled(\n settingsJsonPath: string\n): Promise<boolean> {\n try {\n if (!(await pathExists(settingsJsonPath))) return false;\n const raw = await readFile(settingsJsonPath, 'utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return false;\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return false;\n }\n const obj = parsed as Record<string, unknown>;\n const mcpServers = obj.mcpServers;\n const hasRushMcp =\n mcpServers !== null &&\n typeof mcpServers === 'object' &&\n !Array.isArray(mcpServers) &&\n Object.hasOwn(mcpServers, 'rush');\n if (!hasRushMcp) return false;\n\n const enabledPlugins = obj.enabledPlugins;\n if (\n enabledPlugins === null ||\n enabledPlugins === undefined ||\n typeof enabledPlugins !== 'object' ||\n Array.isArray(enabledPlugins)\n ) {\n // 有 rush mcp 但 enabledPlugins 字段缺失/非 object → 视为\"没有任何 rush@* 键\"\n return true;\n }\n const hasRushEnabled = Object.keys(\n enabledPlugins as Record<string, unknown>\n ).some((k) => k.startsWith('rush@'));\n return !hasRushEnabled;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// 主 API\n// ---------------------------------------------------------------------------\n\n/**\n * 检测老版本 rush-ai 磁盘状态。**不抛**——所有 IO 错误降级为 false。\n */\nexport async function detectLegacyInstall(\n opts: DetectLegacyOptions = {}\n): Promise<LegacyDetectionResult> {\n const home = opts.home ?? homedir();\n const assetManifestPath = resolve(home, LEGACY_ASSET_MANIFEST_RELATIVE);\n const legacyAssetsDir = resolve(home, LEGACY_ASSETS_DIR_RELATIVE);\n const skillSymlinkPath = resolve(home, LEGACY_SKILL_SYMLINK_RELATIVE);\n const settingsJsonPath = resolve(home, CLAUDE_SETTINGS_JSON_RELATIVE);\n\n const [hasAssetManifest, hasLegacySkillSymlink, legacyMcpWithoutEnabled] =\n await Promise.all([\n pathExists(assetManifestPath),\n isSymlinkPointingInto(skillSymlinkPath, legacyAssetsDir),\n hasLegacyMcpWithoutEnabled(settingsJsonPath),\n ]);\n\n return {\n detected:\n hasAssetManifest || hasLegacySkillSymlink || legacyMcpWithoutEnabled,\n hasAssetManifest,\n hasLegacySkillSymlink,\n hasLegacyMcpWithoutEnabled: legacyMcpWithoutEnabled,\n };\n}\n\n/**\n * 尝试从老 asset-manifest.json 提取老版本号(用于 registry.migrations.fromVersion)。\n *\n * 失败(文件不存在 / 损坏 / 无 version 字段)→ 返回 `'0.7.x'`(plan §4.3 的泛指)。\n */\nexport async function detectLegacyVersion(\n opts: DetectLegacyOptions = {}\n): Promise<string> {\n const home = opts.home ?? homedir();\n const manifestPath = resolve(home, LEGACY_ASSET_MANIFEST_RELATIVE);\n try {\n const lst = await stat(manifestPath);\n if (!lst.isFile()) return '0.7.x';\n const raw = await readFile(manifestPath, 'utf8');\n const parsed = JSON.parse(raw) as unknown;\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n const v = (parsed as Record<string, unknown>).version;\n if (typeof v === 'string' && v.trim() !== '') return v.trim();\n }\n return '0.7.x';\n } catch {\n return '0.7.x';\n }\n}\n","/**\n * 迁移失败日志(task-12)。\n *\n *\n * 写入路径:`<home>/.rush/plugins/migration-failed.log`(**追加**模式)。\n *\n * 设计考量:\n * - 追加而非覆盖:用户可能重复触发迁移失败,每次失败都应留痕供事后排查\n * - 每条一行带 ISO 时间戳 + reason;必要时再跟缩进的 stack 或 detail\n * - 写失败(例如磁盘满、权限)**不抛错**——迁移本身已经失败,日志写不上只能吞掉\n * (不能在本已失败的分支里再抛一个错)\n * - 父目录不存在先 mkdir -p(对齐 registry / installer 的惯例)\n *\n * 测试铁律:所有测试必须走注入的临时 HOME,禁止写真实 `~/.rush/`。\n */\n\nimport { appendFile, mkdir } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, resolve } from 'node:path';\n\n/** 迁移失败日志相对 HOME 的路径。 */\nexport const MIGRATION_LOG_RELATIVE_PATH =\n '.rush/plugins/migration-failed.log' as const;\n\nexport interface AppendMigrationFailureOptions {\n /** 注入 HOME(测试必填,生产默认 `os.homedir()`) */\n home?: string;\n /** 注入时间戳生成器(测试里固定时间),默认 `() => new Date().toISOString()` */\n now?: () => string;\n}\n\n/**\n * 计算迁移失败日志的绝对路径。\n */\nexport function resolveMigrationLogPath(\n opts: AppendMigrationFailureOptions = {}\n): string {\n const home = opts.home ?? homedir();\n return resolve(home, MIGRATION_LOG_RELATIVE_PATH);\n}\n\n/**\n * 追加一条迁移失败记录到日志。\n *\n * 格式:\n * ```\n * [<ISO-timestamp>] <reason>\n * [<ISO-timestamp>] detail: <detail-line-1>\n * [<ISO-timestamp>] detail: <detail-line-2>\n * ```\n *\n * - `reason` 是一行摘要(不换行;内部换行会被替换为空格)\n * - `detail` 可选,多行时每行都带时间戳前缀,便于 grep\n *\n * **永不抛错**——日志写失败只能吞掉,不能在已失败的 migrate 路径里再加一层失败。\n */\nexport async function appendMigrationFailure(\n reason: string,\n opts: AppendMigrationFailureOptions & { detail?: string } = {}\n): Promise<void> {\n const now = opts.now ?? (() => new Date().toISOString());\n const ts = now();\n const logPath = resolveMigrationLogPath(opts);\n const safeReason = reason.replace(/\\r?\\n/g, ' ').trim() || '<no reason>';\n const lines: string[] = [`[${ts}] ${safeReason}`];\n if (opts.detail) {\n for (const detailLine of opts.detail.replace(/\\r\\n/g, '\\n').split('\\n')) {\n if (detailLine.trim() === '') continue;\n lines.push(`[${ts}] detail: ${detailLine}`);\n }\n }\n const payload = `${lines.join('\\n')}\\n`;\n try {\n await mkdir(dirname(logPath), { recursive: true });\n await appendFile(logPath, payload, { encoding: 'utf8' });\n } catch {\n // 吞掉——迁移失败分支不允许再抛错\n }\n}\n","/**\n * 老版本 rush-ai → 新格式迁移主流程(task-12)。\n *\n * Source of truth:\n * - `specs/cli-commands.md` §4(UX)\n *\n * # 核心铁律(绝对不能颠倒)\n * 1. 读 registry.migrations,若 `legacy-rush-0.7.x` 已 `status=ok` → 整个跳过(幂等)\n * 2. 按新格式装 rush@rush-marketplace 到 `~/.claude/plugins/cache/...`\n * 3. 验证新格式能读回(plugin.json 存在 + JSON 解析 + name 匹配)\n * 4. 验证通过 → 清旧(删老目录 + symlink + settings.mcpServers.rush)\n * 5. 记 registry `migrations.legacy-rush-0.7.x = { migratedAt, status: 'ok', fromVersion }`\n *\n * 验证失败分支:\n * - **不清旧**(新旧并存)\n * - 写 `~/.rush/plugins/migration-failed.log`\n * - 记 registry status='failed' + notes\n * - 函数返回,**不抛**——不打断外层 install 命令\n *\n * # 关键设计:避免递归\n * `install.ts` 的 pre-install hook 调 `maybeRunMigration`;本函数内部为了\"装新\"\n * 需要调 `ClaudeCodeInstaller.install()`——这里**直接用 Installer,不走\n * `runInstall`**(runInstall 里才有 hook 调用点)。所以递归天然不会发生。\n *\n * # RUSH_SKIP_MIGRATION=1\n * 函数开头检查,命中立即 no-op 返回(plan §10 回滚开关)。\n *\n * # 测试\n * 所有测试铁律注入临时 HOME,严禁写真实 `~/`。\n */\n\nimport { access, readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { ClaudeCodeInstaller } from '../installers/claude-code/installer.js';\nimport {\n ClaudeCodePaths,\n PLUGIN_MANIFEST_RELATIVE,\n} from '../installers/claude-code/paths.js';\nimport { RushRegistryStore } from '../installers/registry.js';\nimport type {\n MigrationRecord,\n PluginInstaller,\n PluginRef,\n ResolvedPlugin,\n} from '../installers/types.js';\nimport {\n RUSH_AI_MARKETPLACE_NAME,\n RUSH_AI_PLUGIN_NAME,\n} from '../plugins/resolver.js';\nimport { cleanupLegacyInstall, summarizeCleanup } from './cleanup.js';\nimport { detectLegacyInstall, detectLegacyVersion } from './detect.js';\nimport { appendMigrationFailure } from './log.js';\n\n// ---------------------------------------------------------------------------\n// 常量 / 契约\n// ---------------------------------------------------------------------------\n\n/**\n * 迁移来源 key——写入 registry.migrations 的索引键。\n *\n * 语义:老版本 rush-ai 0.7.x 整体迁移。后续若有 0.8.x → 1.0.0 这类迁移再加新 key。\n */\nexport const LEGACY_MIGRATION_KEY = 'legacy-rush-0.7.x' as const;\n\n/** 环境变量名——命中则跳过迁移(plan §10 回滚开关)。 */\nexport const SKIP_MIGRATION_ENV = 'RUSH_SKIP_MIGRATION' as const;\n\n// ---------------------------------------------------------------------------\n// Input / Output\n// ---------------------------------------------------------------------------\n\nexport interface MaybeRunMigrationInput {\n /** 当前 install 命令目标 ref——非 `rush@rush-marketplace` 会直接 no-op。 */\n ref: PluginRef;\n /** 已 resolve 的 ResolvedPlugin——装新时直接喂给 Installer(避免重复 resolve)。 */\n resolvedPlugin?: ResolvedPlugin;\n\n /** 注入 HOME(测试必填,生产默认 `os.homedir()`)。 */\n home?: string;\n\n /** 注入 ISO 时间戳生成器(测试里固定)。默认 `() => new Date().toISOString()`。 */\n now?: () => string;\n\n /**\n * 注入环境变量 lookup(测试里不污染真实 env)。\n * 默认 `(key) => process.env[key]`。\n */\n env?: (key: string) => string | undefined;\n\n /**\n * 注入 Installer 工厂——生产走默认 `ClaudeCodeInstaller`;测试里可替换为 mock\n * 来控制 install 成功/失败/抛错。\n *\n * **铁律**:此 Installer 必须**不触发** install.ts 的 pre-install hook,否则\n * 会递归。本模块内直接 `new ClaudeCodeInstaller()` 绕过 `runInstall`,天然无递归。\n */\n installerFactory?: (home: string) => PluginInstaller;\n\n /**\n * 注入 RushRegistryStore(测试里复用已加载的 store 做并发断言);\n * 默认 `await RushRegistryStore.load({ home })`。\n */\n registry?: RushRegistryStore;\n\n /**\n * 注入\"装新后的新格式 plugin.json 路径解析器\"——测试里可以指向 fixture。\n * 默认用 `ClaudeCodePaths.pluginManifestPath(ref, version)`。\n */\n newManifestPathResolver?: (\n home: string,\n ref: PluginRef,\n version: string\n ) => string;\n\n /**\n * 人看输出回调(stdout/stderr)。v1 默认为 no-op——上层 CLI 决定是否打印\n * `ⓘ Detected rush-ai 0.7.x installation...` 这类行。\n *\n * 分两类:`info` / `warn`——失败走 warn,进度走 info。\n */\n reporter?: MigrationReporter;\n}\n\nexport interface MigrationReporter {\n info?: (line: string) => void;\n warn?: (line: string) => void;\n}\n\nexport type MaybeRunMigrationOutcome =\n | { kind: 'skipped'; reason: SkipReason }\n | { kind: 'already-done'; fromVersion?: string }\n | { kind: 'success'; fromVersion: string; cleanupIssues?: string[] }\n | { kind: 'failure'; reason: string; stage: MigrationStage };\n\nexport type SkipReason = 'env-flag' | 'non-rush-plugin' | 'no-legacy-traces';\n\nexport type MigrationStage = 'install-new' | 'verify-new' | 'registry-write';\n\n// ---------------------------------------------------------------------------\n// 主 API\n// ---------------------------------------------------------------------------\n\n/**\n * Pre-install hook 入口。调用方:`install.ts` 在 resolvePlugin 之后、Installer 之前。\n *\n * 契约(**铁律**):\n * - **永不抛错**——任何内部异常(registry load / registry save / IO / Installer bug)\n * 都被吸收到 `MaybeRunMigrationOutcome`,install 主流程不被打断\n * - 对非 `rush@rush-marketplace` 立即 no-op\n * - `RUSH_SKIP_MIGRATION=1` 立即 no-op\n * - 已迁移过(registry.migrations 有 ok 记录)立即 no-op\n * - 上次失败(status='failed')→ 再次尝试(用户可能已修问题)\n * - 成功:装新 → 验证 → 清旧 → registry 记 ok\n * - 三阶段失败(stage):\n * - `install-new` :Installer 返回 failed/partial/skipped 或 Installer 抛异常\n * - `verify-new` :Installer 成功但新格式 plugin.json 读不回\n * - `registry-write`:registry load / save 失败(包括各失败分支\"记 failed\"本身\n * 再失败的情况;此时走 log + 返回 registry-write 失败)\n * - 任何失败分支都**保留新旧并存**(不清旧),并追加写 `migration-failed.log`\n */\nexport async function maybeRunMigration(\n input: MaybeRunMigrationInput\n): Promise<MaybeRunMigrationOutcome> {\n const home = input.home ?? homedir();\n const now = input.now ?? (() => new Date().toISOString());\n const envGet = input.env ?? ((key) => process.env[key]);\n const reporter = input.reporter ?? {};\n\n // 0. RUSH_SKIP_MIGRATION=1 → no-op\n // plan §10 明确把\"关 migration hook\"作为回滚手段。\n // 任何非空 \"0\" / \"false\" / \"no\" 之外的值都视为启用跳过——用户通常写 \"1\"。\n const skipValue = envGet(SKIP_MIGRATION_ENV);\n if (isTruthyEnvValue(skipValue)) {\n return { kind: 'skipped', reason: 'env-flag' };\n }\n\n // 1. 只在装 rush@rush-marketplace 时才迁移——装其他插件不需要动用户老状态\n if (\n input.ref.name !== RUSH_AI_PLUGIN_NAME ||\n input.ref.marketplace !== RUSH_AI_MARKETPLACE_NAME\n ) {\n return { kind: 'skipped', reason: 'non-rush-plugin' };\n }\n\n // 2. 加载 registry,检查已迁移记录(幂等)\n // registry 加载失败本身**不**中断 hook——fall through 到后续流程;\n // 真正 registry 写盘失败会走 registry-write 失败分支。\n let registry: RushRegistryStore;\n try {\n registry = input.registry ?? (await RushRegistryStore.load({ home }));\n } catch (err) {\n // Registry 损坏:我们宁可不迁移也不在损坏的 registry 上操作。\n // 写 log 记一笔方便排查,整体视为 failure。\n const reason = errorMessage(err);\n await appendMigrationFailure(\n `registry load failed before migration: ${reason}`,\n { home, now }\n );\n reporter.warn?.(\n `⚠ Migration skipped: rush-ai registry load failed (${reason})`\n );\n return { kind: 'failure', reason, stage: 'registry-write' };\n }\n\n const existing = registry.getMigration(LEGACY_MIGRATION_KEY);\n if (existing && existing.status === 'ok') {\n // 幂等:已成功迁移过,直接跳过(即使磁盘上残留一些老痕迹也不再动——\n // 用户可能自己重新创建了这些文件;status=ok 的不变式由上次迁移成功保证)\n return {\n kind: 'already-done',\n ...(existing.fromVersion !== undefined\n ? { fromVersion: existing.fromVersion }\n : {}),\n };\n }\n\n // 3. 检测老痕迹——没检测到 → no-op(全新用户或上次已经清理过)\n const detection = await detectLegacyInstall({ home });\n if (!detection.detected) {\n return { kind: 'skipped', reason: 'no-legacy-traces' };\n }\n\n const fromVersion = await detectLegacyVersion({ home });\n reporter.info?.(\n `ⓘ Detected rush-ai ${fromVersion} installation at ~/.rush/plugins/claude-code/`\n );\n reporter.info?.(' Migrating to native plugin format...');\n\n // 4. 装新(严格要求 ResolvedPlugin 已传入——否则没法装)\n if (!input.resolvedPlugin) {\n // 没有 ResolvedPlugin 无法继续——记 failure,让上层知道问题\n const reason = 'resolvedPlugin not provided to maybeRunMigration';\n await appendMigrationFailure(reason, { home, now });\n const saveErr = await safeRecordMigration(\n registry,\n 'failed',\n { now: now(), fromVersion, notes: reason },\n { home, now }\n );\n reporter.warn?.(`⚠ Migration failed: ${reason}`);\n if (saveErr) {\n return { kind: 'failure', reason: saveErr, stage: 'registry-write' };\n }\n return { kind: 'failure', reason, stage: 'install-new' };\n }\n\n const installerFactory =\n input.installerFactory ?? ((h) => new ClaudeCodeInstaller({ home: h }));\n const installer = installerFactory(home);\n\n try {\n const installResult = await installer.install(input.resolvedPlugin, {\n force: false,\n dryRun: false,\n });\n if (installResult.status === 'failed') {\n const reason =\n installResult.errors?.[0] ??\n 'Claude Code installer returned failed without error details';\n await appendMigrationFailure(`install new format failed: ${reason}`, {\n home,\n now,\n detail: installResult.errors?.slice(1).join('\\n'),\n });\n const saveErr = await safeRecordMigration(\n registry,\n 'failed',\n { now: now(), fromVersion, notes: `install-new: ${reason}` },\n { home, now }\n );\n printFailureGuidance(reporter, reason);\n if (saveErr) {\n return { kind: 'failure', reason: saveErr, stage: 'registry-write' };\n }\n return { kind: 'failure', reason, stage: 'install-new' };\n }\n // `partial` 在 v1 不允许(spec §1.4 \"Installer 实现不得构造 partial\")——\n // 但防御性:若 Installer 违反契约返回 partial,我们**不**当作成功继续 cleanup,\n // 否则可能在新格式未完整安装时清掉旧格式(违反 plan §4.3 安全铁律)。\n // 统一降级为 install-new 失败,保留双份 + 写 log + registry failed。\n if (installResult.status === 'partial') {\n const reason =\n \"Claude Code installer returned 'partial' which is not permitted in v1 (spec §1.4); treating as install-new failure to preserve legacy\";\n await appendMigrationFailure(`install new format partial: ${reason}`, {\n home,\n now,\n detail: installResult.errors?.join('\\n'),\n });\n const saveErr = await safeRecordMigration(\n registry,\n 'failed',\n { now: now(), fromVersion, notes: `install-new partial: ${reason}` },\n { home, now }\n );\n printFailureGuidance(reporter, reason);\n if (saveErr) {\n return { kind: 'failure', reason: saveErr, stage: 'registry-write' };\n }\n return { kind: 'failure', reason, stage: 'install-new' };\n }\n // 'skipped' 代表 ~/.claude 目录不存在(非 Claude Code 用户)——\n // 迁移本来就不适用;记 failed 带清晰 notes 让 registry 能看到\n if (installResult.status === 'skipped') {\n const reason =\n 'Claude Code not detected (~/.claude missing); nothing to migrate';\n const saveErr = await safeRecordMigration(\n registry,\n 'failed',\n { now: now(), fromVersion, notes: `install-new skipped: ${reason}` },\n { home, now }\n );\n // 不写 migration-failed.log——这不是\"失败\",是环境不支持\n reporter.info?.(`ⓘ Migration skipped: ${reason}`);\n if (saveErr) {\n return { kind: 'failure', reason: saveErr, stage: 'registry-write' };\n }\n return { kind: 'failure', reason, stage: 'install-new' };\n }\n // 仅 'ok' 才进入后续 verify 阶段\n reporter.info?.(\n ` ✓ Copied skills to ~/.claude/plugins/cache/${input.ref.marketplace}/${input.ref.name}/${input.resolvedPlugin.version}/`\n );\n reporter.info?.(' ✓ Registered in installed_plugins.json');\n } catch (err) {\n // Installer 抛异常(bug 级)——同样走 failed 分支,不复制到 install 命令\n const reason = errorMessage(err);\n await appendMigrationFailure(`install new format threw: ${reason}`, {\n home,\n now,\n });\n const saveErr = await safeRecordMigration(\n registry,\n 'failed',\n { now: now(), fromVersion, notes: `install-new threw: ${reason}` },\n { home, now }\n );\n printFailureGuidance(reporter, reason);\n if (saveErr) {\n return { kind: 'failure', reason: saveErr, stage: 'registry-write' };\n }\n return { kind: 'failure', reason, stage: 'install-new' };\n }\n\n // 5. 验证新格式能加载(快速 dry-run:读回 plugin.json)\n const resolver =\n input.newManifestPathResolver ??\n ((h, r, v) => {\n const paths = new ClaudeCodePaths(h);\n return paths.pluginManifestPath(r, v);\n });\n const manifestPath = resolver(home, input.ref, input.resolvedPlugin.version);\n\n const verifyResult = await verifyNewFormat(\n manifestPath,\n input.ref.name,\n input.resolvedPlugin.version\n );\n if (!verifyResult.ok) {\n const reason = verifyResult.error;\n await appendMigrationFailure(`verify new format failed: ${reason}`, {\n home,\n now,\n detail: `expected manifest at ${manifestPath}`,\n });\n const saveErr = await safeRecordMigration(\n registry,\n 'failed',\n { now: now(), fromVersion, notes: `verify-new: ${reason}` },\n { home, now }\n );\n printFailureGuidance(reporter, reason);\n if (saveErr) {\n return { kind: 'failure', reason: saveErr, stage: 'registry-write' };\n }\n return { kind: 'failure', reason, stage: 'verify-new' };\n }\n reporter.info?.(' ✓ Verified new format loads correctly');\n\n // 6. 验证通过 → 清旧\n const cleanupResult = await cleanupLegacyInstall({ home });\n const cleanupSummary = summarizeCleanup(cleanupResult);\n if (cleanupResult.legacyDir.ok) {\n reporter.info?.(' ✓ Cleaned up legacy ~/.rush/plugins/claude-code/');\n }\n if (cleanupResult.skillSymlink.ok) {\n reporter.info?.(' ✓ Removed legacy symlink ~/.claude/skills/rush-task');\n }\n if (cleanupResult.settingsMcp.ok) {\n reporter.info?.(\" ✓ Removed 'rush' from settings.json.mcpServers\");\n }\n if (cleanupSummary.failed) {\n // 清理 best-effort 失败——整体仍视为迁移成功(新格式装好了),但记 notes\n // 让 registry 能看到残留,下次再 install 时会重新走 registry.getMigration\n // → ok 分支跳过,不会重试清理。\n await appendMigrationFailure(\n `cleanup partial failure after successful install: ${cleanupSummary.reasons.join(\n '; '\n )}`,\n { home, now }\n );\n }\n\n // 7. registry 记 ok\n const okSaveErr = await safeRecordMigration(\n registry,\n 'ok',\n {\n now: now(),\n fromVersion,\n ...(cleanupSummary.failed\n ? { notes: `cleanup issues: ${cleanupSummary.reasons.join('; ')}` }\n : {}),\n },\n { home, now }\n );\n if (okSaveErr) {\n // registry 写失败——不阻断用户(新格式已装好 + 老的已清),但需要上层知道\n // 下次 install 不会走 `already-done` 短路(registry 没标成 ok),会再跑一次\n // 迁移,但 legacy 已经清掉,detection 应返回 no-legacy-traces。\n reporter.warn?.(\n `⚠ Migration completed but rush-ai registry write failed: ${okSaveErr}`\n );\n return { kind: 'failure', reason: okSaveErr, stage: 'registry-write' };\n }\n\n return {\n kind: 'success',\n fromVersion,\n ...(cleanupSummary.failed ? { cleanupIssues: cleanupSummary.reasons } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\n/** 检查环境变量值是否代表 \"truthy\"(\"1\" / \"true\" / \"yes\" / \"on\" 都算)。 */\nfunction isTruthyEnvValue(value: string | undefined): boolean {\n if (value === undefined || value === '') return false;\n const normalized = value.trim().toLowerCase();\n if (normalized === '' || normalized === '0' || normalized === 'false') {\n return false;\n }\n if (normalized === 'no' || normalized === 'off') return false;\n return true;\n}\n\ninterface VerifyResult {\n ok: boolean;\n error: string;\n}\n\n/**\n * 验证新格式 plugin.json 能正常读回 + 匹配期望的 name。\n *\n * 宽松:version 允许 mismatch(Installer 已按请求 version 写入,若 manifest 里\n * 是空字符串或别的也视为文件完整性 ok——Installer 侧已有 schema 校验)。\n *\n * 不验证目录完整性(cache dir 结构)——Installer 的写入保证了这点;dry-run\n * 目的是\"新格式磁盘可见\"即可。\n */\nasync function verifyNewFormat(\n manifestPath: string,\n expectedName: string,\n _expectedVersion: string\n): Promise<VerifyResult> {\n try {\n await access(manifestPath);\n } catch (err) {\n return {\n ok: false,\n error: `manifest not found at ${manifestPath}: ${errorMessage(err)}`,\n };\n }\n\n let raw: string;\n try {\n raw = await readFile(manifestPath, 'utf8');\n } catch (err) {\n return {\n ok: false,\n error: `manifest read failed: ${errorMessage(err)}`,\n };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n return {\n ok: false,\n error: `manifest JSON parse failed: ${errorMessage(err)}`,\n };\n }\n\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return {\n ok: false,\n error: 'manifest top-level is not an object',\n };\n }\n const obj = parsed as Record<string, unknown>;\n if (obj.name !== expectedName) {\n return {\n ok: false,\n error: `manifest name mismatch: expected \"${expectedName}\", got ${JSON.stringify(\n obj.name\n )}`,\n };\n }\n\n return { ok: true, error: '' };\n}\n\n/**\n * 读-改-写 registry.migrations 记录。\n *\n * 并发冲突由 RushRegistryStore 内置 mtime 保护 + 一次重试处理。\n *\n * 可能抛错(JSON 损坏 / mtime 冲突失败 / schema mismatch / 磁盘 IO)。\n * 失败分支路径里应改用 `safeRecordMigration` —— 不抛错,让 outcome 降级为\n * `registry-write` 失败。\n */\nasync function recordMigration(\n registry: RushRegistryStore,\n status: MigrationRecord['status'],\n fields: { now: string; fromVersion?: string; notes?: string }\n): Promise<void> {\n const record: MigrationRecord = {\n migratedAt: fields.now,\n status,\n ...(fields.fromVersion !== undefined\n ? { fromVersion: fields.fromVersion }\n : {}),\n ...(fields.notes !== undefined ? { notes: fields.notes } : {}),\n };\n registry.setMigration(LEGACY_MIGRATION_KEY, record);\n await registry.save();\n}\n\n/**\n * 不抛错版本——在失败分支里记 status=failed 时使用。\n *\n * 契约:`maybeRunMigration` 永不抛错;失败分支里若连 registry 都写不动(磁盘满、\n * 并发冲突、schema mismatch 等),只能写 log + 返回非 null error——由调用方决定\n * 是继续返回原阶段(install-new/verify-new)还是升级为 registry-write 失败。\n *\n * 返回 `undefined` 代表写入成功,否则返回失败原因字符串。\n */\nasync function safeRecordMigration(\n registry: RushRegistryStore,\n status: MigrationRecord['status'],\n fields: { now: string; fromVersion?: string; notes?: string },\n logOpts: { home: string; now: () => string }\n): Promise<string | undefined> {\n try {\n await recordMigration(registry, status, fields);\n return undefined;\n } catch (err) {\n const reason = errorMessage(err);\n await appendMigrationFailure(\n `registry save failed while recording status=${status}: ${reason}`,\n { home: logOpts.home, now: logOpts.now }\n );\n return reason;\n }\n}\n\nfunction printFailureGuidance(\n reporter: MigrationReporter,\n reason: string\n): void {\n reporter.warn?.(`⚠ Migration failed: ${reason}`);\n reporter.warn?.(' → New format installed to ~/.claude/plugins/cache/...');\n reporter.warn?.(\n ' → Legacy at ~/.rush/plugins/claude-code/ preserved (still functional)'\n );\n reporter.warn?.(' → See ~/.rush/plugins/migration-failed.log for details');\n reporter.warn?.(' → Continuing with install...');\n}\n\nfunction errorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n\n// 避免 PLUGIN_MANIFEST_RELATIVE unused-import warning——\n// 便于后续若 resolver 需要直接参考 plugin.json 相对路径时可用。\nexport const __PLUGIN_MANIFEST_RELATIVE = PLUGIN_MANIFEST_RELATIVE;\n","/**\n * Plugin resolver:从 marketplace 解析出 ResolvedPlugin(task-4 产物)。\n *\n * Source of truth:\n * - `specs/installer-interface.md` §1.3 ResolvedPlugin / PluginManifest\n * - `specs/plugin-schemas.md` §1.5 plugin.json schema\n *\n * 流程(对齐 task-4 prompt \"实现步骤\"):\n * 1. 在 marketplace 的本地 cache(`ResolvedMarketplace.rootDir`)里按\n * `marketplace.json.plugins[].source.path`(或字符串 source)定位 plugin 子目录\n * 2. 读 `<plugin-dir>/.claude-plugin/plugin.json`(缺失 → `PluginManifestNotFoundError`)\n * 3. 校验 `plugin.json.version` 非空 + 非 \"unknown\"(`InvalidPluginVersionError`)\n * 4. 扫描 capabilities(commands / skills / rules / hooks / mcp)\n * 5. MCP command 规范化:**仅**对 rush-ai 自己的插件\n * (`ref.name === 'rush' && ref.marketplace === 'rush-marketplace'`)做绝对路径解析,\n * 其他所有 plugin **透传**不改(见 plan §7.1)\n * 6. 返回 `ResolvedPlugin`\n *\n * 接口设计:接受 `ResolvedMarketplace`(task-3 `MarketplaceCache.get()` 产物)而非\n * MarketplaceCache 本身——职责单一 + 便于 CLI 层手工构造 / 测试里直接 fixture。\n */\n\nimport {\n isAbsolute,\n relative as pathRelative,\n resolve as pathResolve,\n sep as pathSep,\n} from 'node:path';\nimport type {\n PluginCapability,\n PluginManifest,\n PluginRef,\n ResolvedPlugin,\n} from '../installers/types.js';\nimport type {\n MarketplacePluginEntry,\n ResolvedMarketplace,\n} from '../marketplaces/types.js';\nimport { scanCapabilities } from './capabilities.js';\nimport {\n PluginNotFoundInMarketplaceError,\n PluginSourceUnresolvableError,\n} from './errors.js';\nimport { readPluginManifest } from './manifest.js';\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/**\n * resolver 的可注入选项。\n */\nexport interface ResolvePluginOptions {\n /**\n * 解析 `rush-ai` binary 的绝对路径。仅在 ref 为 rush-ai 自己的插件\n * (`rush@rush-marketplace`)且 plugin.json.mcpServers.rush.command 非绝对路径时调用。\n *\n * 默认实现 = 当前 node 进程的 `process.argv[1]`(rush-ai CLI 入口),失败时\n * 回退到原值(保持兼容,不让 resolver 因为 rush-ai 还没发布就挂掉)。\n *\n * 测试里注入固定字符串更稳定。\n */\n readonly rushAiBinaryResolver?: () => string | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// 常量:rush-ai 自己的插件 identifier(MCP command 绝对路径规则触发条件)\n// ---------------------------------------------------------------------------\n\n/**\n * rush-ai 自己发布的插件的 PluginRef identifier。\n *\n * plan §7.1 约定:仅对本 ref 做 MCP command 绝对路径规范化,其他所有 plugin 透传。\n */\nexport const RUSH_AI_PLUGIN_NAME = 'rush' as const;\nexport const RUSH_AI_MARKETPLACE_NAME = 'rush-marketplace' as const;\n\n/**\n * rush 插件内 MCP server 的 key(plan §7 示例 / spec §1.3)。\n *\n * 只对该 key 的 command 做绝对路径规范化;其他 key(即使 rush plugin 自己声明了多个\n * MCP server)保持透传。\n */\nexport const RUSH_MCP_SERVER_KEY = 'rush' as const;\n\n// ---------------------------------------------------------------------------\n// 主 API\n// ---------------------------------------------------------------------------\n\n/**\n * 从 marketplace 解析 plugin,产出 Installer 可直接消费的 ResolvedPlugin。\n *\n * @param ref plugin 引用(含 name + marketplace name)\n * @param marketplace 已 resolve 的 marketplace(task-3 `MarketplaceCache.get()` 产物)\n * @param options 可选注入项(见 `ResolvePluginOptions`)\n *\n * @throws {PluginNotFoundInMarketplaceError} marketplace.json 里没有该 plugin\n * @throws {PluginSourceUnresolvableError} plugin 条目的 source 字段不可解析为本地路径\n * @throws {PluginManifestNotFoundError} plugin source 目录缺 `.claude-plugin/plugin.json`\n * @throws {PluginManifestCorruptError} plugin.json JSON 损坏 / 顶层 shape 错\n * @throws {InvalidPluginVersionError} plugin.json.version 缺失 / 空串 / \"unknown\"\n */\nexport async function resolvePlugin(\n ref: PluginRef,\n marketplace: ResolvedMarketplace,\n options: ResolvePluginOptions = {}\n): Promise<ResolvedPlugin> {\n // 1. 在 marketplace.json 里找 plugin 条目\n const entry = findPluginEntry(ref, marketplace);\n\n // 2. 定位 plugin 源目录(marketplace rootDir 下的子目录)\n const sourceDir = resolvePluginSourceDir(ref, marketplace, entry);\n\n // 3. 读 plugin.json + 校验 version\n const manifest = await readPluginManifest(ref.name, sourceDir);\n\n // 4. 扫描 capabilities\n const capabilities = await scanCapabilities(sourceDir, manifest);\n\n // 5. MCP command 绝对路径规范化(仅 rush-ai 自己的插件)\n const normalizedManifest = normalizeRushMcpCommand(\n ref,\n manifest,\n options.rushAiBinaryResolver\n );\n\n // 6. 组装 ResolvedPlugin\n return {\n ref,\n version: normalizedManifest.version,\n sourceDir,\n manifest: normalizedManifest,\n capabilities,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\n/**\n * 在 marketplace.manifest.plugins 数组里找 `ref.name` 对应的条目。\n */\nfunction findPluginEntry(\n ref: PluginRef,\n marketplace: ResolvedMarketplace\n): MarketplacePluginEntry {\n const entry = marketplace.manifest.plugins.find((p) => p.name === ref.name);\n if (!entry) {\n const available = marketplace.manifest.plugins.map((p) => p.name);\n throw new PluginNotFoundInMarketplaceError(\n ref.name,\n marketplace.name,\n available\n );\n }\n return entry;\n}\n\n/**\n * 由 marketplace plugin 条目的 `source` 字段定位 plugin 子目录的绝对路径。\n *\n * 支持的 source 形态:\n * - 缺失 `source`:默认相对路径 `plugins/<name>/`(对齐 phase0-spike fixture 约定)\n * - 字符串:作为相对 marketplace.rootDir 的路径使用(允许 `./foo` / `foo/bar`)\n * - 对象形态:\n * - `{ source: 'local', path: <相对路径> }` → 用 path\n * - 含 `path` 字段但没 `source` → 用 path(宽容)\n * - 含 `url` 但无 `path`(如 git-subdir 指向外部仓库的 url 但未声明子目录)\n * → 抛 `PluginSourceUnresolvableError`(Phase 1 不支持远程拉取子目录)\n *\n * 所有返回路径都是**绝对路径**,且必须 resolve 到 marketplace.rootDir 下(防止 `../`\n * 逃逸——目录逃逸在 Phase 1 不支持,避免意外读取 marketplace 以外的目录)。\n */\nfunction resolvePluginSourceDir(\n ref: PluginRef,\n marketplace: ResolvedMarketplace,\n entry: MarketplacePluginEntry\n): string {\n const relPath = extractRelativeSourcePath(ref, marketplace, entry);\n if (isAbsolute(relPath)) {\n throw new PluginSourceUnresolvableError(\n ref.name,\n marketplace.name,\n `source path must be relative to marketplace root; got absolute '${relPath}'`\n );\n }\n const rootAbs = pathResolve(marketplace.rootDir);\n const abs = pathResolve(rootAbs, relPath);\n // 防止 `../` 逃逸到 marketplace 以外。\n //\n // 用 `path.relative(root, abs)` 判定,下列三种结果都视为逃逸:\n // 1. rel === '..' → 恰好指向 root 的上一级\n // 2. rel.startsWith(`..${path.sep}`) → `../foo` / `..\\foo`(跨平台分隔符)\n // 3. isAbsolute(rel) → Windows 跨盘符时 `path.relative` 会直接\n // 返回 `to` 的绝对路径(Node 文档行为)\n // 比老写法 `startsWith(root + '/')` 更稳,后者在 Windows 下会因字面 '/' 与\n // `path.resolve` 产出的反斜杠不匹配导致误判/漏判。\n const rel = pathRelative(rootAbs, abs);\n const isEscaping =\n rel === '..' || rel.startsWith(`..${pathSep}`) || isAbsolute(rel);\n if (isEscaping) {\n throw new PluginSourceUnresolvableError(\n ref.name,\n marketplace.name,\n `resolved plugin source dir '${abs}' escapes marketplace root '${rootAbs}'`\n );\n }\n return abs;\n}\n\n/**\n * 从 plugin 条目的 `source` 字段抽出\"相对 marketplace rootDir 的路径\"。\n *\n * 不碰磁盘——返回的路径可能指向不存在的目录,后续 `readPluginManifest` 会发现。\n */\nfunction extractRelativeSourcePath(\n ref: PluginRef,\n marketplace: ResolvedMarketplace,\n entry: MarketplacePluginEntry\n): string {\n if (entry.source === undefined || entry.source === null) {\n // 默认约定:`plugins/<name>/`(对齐 phase0-spike fixture)\n return `plugins/${entry.name}`;\n }\n\n if (typeof entry.source === 'string') {\n const s = entry.source.trim();\n if (s.length === 0) {\n throw new PluginSourceUnresolvableError(\n ref.name,\n marketplace.name,\n 'source string is empty'\n );\n }\n return s;\n }\n\n if (typeof entry.source === 'object' && !Array.isArray(entry.source)) {\n const src = entry.source as {\n source?: unknown;\n type?: unknown;\n path?: unknown;\n url?: unknown;\n };\n if (typeof src.path === 'string' && src.path.trim().length > 0) {\n return src.path.trim();\n }\n const kind =\n typeof src.source === 'string'\n ? src.source\n : typeof src.type === 'string'\n ? src.type\n : 'unknown';\n throw new PluginSourceUnresolvableError(\n ref.name,\n marketplace.name,\n `source object (kind='${kind}') has no 'path' field; Phase 1 resolver only supports local paths relative to marketplace root`\n );\n }\n\n throw new PluginSourceUnresolvableError(\n ref.name,\n marketplace.name,\n `source field has unexpected type '${typeof entry.source}'`\n );\n}\n\n/**\n * 仅对 rush-ai 自己的插件(`rush@rush-marketplace`)做 MCP command 绝对路径规范化。\n *\n * 其他 plugin 透传——即使 command 写的是相对路径,也不替换(由各家 Installer 按\n * 自己的规则处理,如 Codex `.mcp.json` 的 cwd 相对路径)。\n *\n * 触发条件:\n * - `ref.name === 'rush'` 且 `ref.marketplace === 'rush-marketplace'`\n * - `manifest.mcpServers` 是对象形态(不是 Codex 的字符串外部文件引用)\n * - 存在 `mcpServers.rush` 且其 `command` 非绝对路径\n *\n * 返回**新对象**(不 mutate 原 manifest),避免副作用。\n */\nfunction normalizeRushMcpCommand(\n ref: PluginRef,\n manifest: PluginManifest,\n rushAiBinaryResolver: (() => string | undefined) | undefined\n): PluginManifest {\n if (\n ref.name !== RUSH_AI_PLUGIN_NAME ||\n ref.marketplace !== RUSH_AI_MARKETPLACE_NAME\n ) {\n return manifest;\n }\n const servers = manifest.mcpServers;\n if (servers === undefined || typeof servers === 'string') {\n return manifest;\n }\n const rushServer = servers[RUSH_MCP_SERVER_KEY];\n if (!rushServer || typeof rushServer !== 'object') {\n return manifest;\n }\n const currentCommand = rushServer.command;\n if (typeof currentCommand !== 'string' || currentCommand.length === 0) {\n return manifest;\n }\n if (isAbsolute(currentCommand)) {\n return manifest;\n }\n\n const resolved = (rushAiBinaryResolver ?? defaultRushAiBinaryResolver)();\n if (!resolved || !isAbsolute(resolved)) {\n // 兜底:resolve 不出绝对路径就保持原 command(不让 resolver 阶段挂掉)。\n // 下游 Installer 若需要强制绝对路径,会在 install 阶段报错。\n return manifest;\n }\n\n const nextServers = {\n ...servers,\n [RUSH_MCP_SERVER_KEY]: { ...rushServer, command: resolved },\n };\n return { ...manifest, mcpServers: nextServers };\n}\n\n/**\n * 默认 rush-ai binary 解析:取当前 node 进程的 argv[1]。\n *\n * 实际场景里 rush-ai CLI 入口会填到 argv[1];测试里应注入自定义 resolver。\n */\nfunction defaultRushAiBinaryResolver(): string | undefined {\n const argv1 = process.argv[1];\n if (typeof argv1 === 'string' && isAbsolute(argv1)) {\n return argv1;\n }\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// 便于下游引用的类型再导出\n// ---------------------------------------------------------------------------\n\nexport type { PluginRef, ResolvedPlugin, PluginCapability };\n","/**\n * 扫描 plugin 源目录产出 capability 清单(task-4 产物)。\n *\n * Source of truth: `specs/installer-interface.md` §1.2 PluginCapability + plan §7.1。\n *\n * - `commands/` 目录下任一 `.md` 文件 → `commands`\n * - `skills/` 目录下任一子目录含 `SKILL.md`,或 Claude-style `.skills`\n * 目录里的 markdown → `skills`\n * - `rules/` 目录下任一 `.md` 文件 → `rules`\n * - `hooks/` 目录下任一 `.json` / `.sh` / `.js` 文件 → `hooks`\n * - plugin.json 顶层有 `mcpServers` 字段(对象或字符串) → `mcp`\n *\n * 设计原则:\n * - 宽容扫描:目录不存在视为\"没有该能力\"(不抛错)\n * - 只扫一层(commands/rules/hooks)或两层(skills/<name>/SKILL.md);\n * `.skills` 允许递归找 markdown,因为 Claude plugin 常按主题分组\n * - 结果顺序**固定**:按 `CAPABILITY_ORDER` 产出,便于测试断言 + 人眼 diff\n */\n\nimport { type Dirent, constants as fsConstants } from 'node:fs';\nimport { access, readdir, stat } from 'node:fs/promises';\nimport { extname, resolve } from 'node:path';\nimport type { PluginCapability, PluginManifest } from '../installers/types.js';\n\n/**\n * Capability 枚举的规范顺序(测试断言 + 格式化输出用)。\n */\nexport const CAPABILITY_ORDER: readonly PluginCapability[] = [\n 'commands',\n 'skills',\n 'rules',\n 'hooks',\n 'mcp',\n] as const;\n\nconst HOOK_EXTENSIONS = new Set(['.json', '.sh', '.js']);\n\n/**\n * 扫描 plugin 源目录 + 检查 manifest,返回实际发现的 capability 清单。\n *\n * @param sourceDir plugin 源目录绝对路径\n * @param manifest 已解析的 plugin.json(用于检查 `mcpServers`)\n * @returns capability 清单,按 `CAPABILITY_ORDER` 排序\n */\nexport async function scanCapabilities(\n sourceDir: string,\n manifest: PluginManifest\n): Promise<PluginCapability[]> {\n const found = new Set<PluginCapability>();\n\n if (await hasCommands(sourceDir)) {\n found.add('commands');\n }\n if (await hasSkills(sourceDir)) {\n found.add('skills');\n }\n if (await hasRules(sourceDir)) {\n found.add('rules');\n }\n if (await hasHooks(sourceDir)) {\n found.add('hooks');\n }\n if (hasMcp(manifest)) {\n found.add('mcp');\n }\n\n return CAPABILITY_ORDER.filter((cap) => found.has(cap));\n}\n\n// ---------------------------------------------------------------------------\n// 单能力探测函数(导出便于独立测试)\n// ---------------------------------------------------------------------------\n\n/** `commands/` 下任一 `.md` 文件 → 有 commands 能力。 */\nexport async function hasCommands(sourceDir: string): Promise<boolean> {\n return dirHasFileWithExt(resolve(sourceDir, 'commands'), '.md');\n}\n\n/**\n * `skills/` 下任一子目录含 `SKILL.md`,或 `.skills` 目录里的 markdown → 有\n * skills 能力。\n *\n * 注意:skill 判定必须以 `SKILL.md` 为准(不是目录下随便有 `.md`)——对齐\n * Claude Code / Codex 原生扫描行为,也避免误报。\n */\nexport async function hasSkills(sourceDir: string): Promise<boolean> {\n const skillsDir = resolve(sourceDir, 'skills');\n if (await dirExists(skillsDir)) {\n let entries: Dirent[];\n try {\n entries = await readdir(skillsDir, { withFileTypes: true });\n } catch {\n return false;\n }\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const skillMdPath = resolve(skillsDir, entry.name, 'SKILL.md');\n if (await fileExists(skillMdPath)) {\n return true;\n }\n }\n }\n return hasClaudeStyleSkills(sourceDir);\n}\n\n/** `rules/` 下任一 `.md` 文件 → 有 rules 能力。 */\nexport async function hasRules(sourceDir: string): Promise<boolean> {\n return dirHasFileWithExt(resolve(sourceDir, 'rules'), '.md');\n}\n\n/** `hooks/` 下任一 `.json` / `.sh` / `.js` 文件 → 有 hooks 能力。 */\nexport async function hasHooks(sourceDir: string): Promise<boolean> {\n const hooksDir = resolve(sourceDir, 'hooks');\n if (!(await dirExists(hooksDir))) {\n return false;\n }\n let entries: Dirent[];\n try {\n entries = await readdir(hooksDir, { withFileTypes: true });\n } catch {\n return false;\n }\n for (const entry of entries) {\n if (!entry.isFile()) continue;\n if (HOOK_EXTENSIONS.has(extname(entry.name).toLowerCase())) {\n return true;\n }\n }\n return false;\n}\n\n/** plugin.json 顶层有非空 `mcpServers` → 有 mcp 能力。 */\nexport function hasMcp(manifest: PluginManifest): boolean {\n const m = manifest.mcpServers;\n if (m === undefined || m === null) return false;\n if (typeof m === 'string') {\n return m.length > 0;\n }\n // object: 必须至少有一个 server key\n return Object.keys(m).length > 0;\n}\n\n// ---------------------------------------------------------------------------\n// 小工具\n// ---------------------------------------------------------------------------\n\nasync function dirHasFileWithExt(\n dirPath: string,\n ext: string\n): Promise<boolean> {\n if (!(await dirExists(dirPath))) {\n return false;\n }\n let entries: Dirent[];\n try {\n entries = await readdir(dirPath, { withFileTypes: true });\n } catch {\n return false;\n }\n const target = ext.toLowerCase();\n return entries.some(\n (e) => e.isFile() && extname(e.name).toLowerCase() === target\n );\n}\n\nasync function hasClaudeStyleSkills(sourceDir: string): Promise<boolean> {\n const dotSkillsDir = resolve(sourceDir, '.skills');\n if (!(await dirExists(dotSkillsDir))) {\n return false;\n }\n async function walk(dirPath: string): Promise<boolean> {\n let entries: Dirent[];\n try {\n entries = await readdir(dirPath, { withFileTypes: true });\n } catch {\n return false;\n }\n for (const entry of entries) {\n const abs = resolve(dirPath, entry.name);\n if (entry.isDirectory()) {\n if (await walk(abs)) return true;\n continue;\n }\n if (entry.isFile() && extname(entry.name).toLowerCase() === '.md') {\n return true;\n }\n }\n return false;\n }\n return walk(dotSkillsDir);\n}\n\nasync function dirExists(p: string): Promise<boolean> {\n try {\n const s = await stat(p);\n return s.isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function fileExists(p: string): Promise<boolean> {\n try {\n await access(p, fsConstants.R_OK);\n const s = await stat(p);\n return s.isFile();\n } catch {\n return false;\n }\n}\n","/**\n * Plugin resolver 错误类型(task-4 产物)。\n *\n * Source of truth: `specs/installer-interface.md` §1.3 / `specs/plugin-schemas.md` §1.5。\n *\n * 设计原则:\n * - 错误类型**细分**,便于 CLI 层按 `instanceof` 给出定向用户提示\n * - 所有错误保留关键字段(pluginName / marketplaceName / manifestPath 等),方便排障\n * - 每个子类通过 `Error.name` 区分(如 `'InvalidPluginVersionError'`)——未显式暴露\n * `code` 字段;CLI 层用 `instanceof` + `Error.name` 分派,而不是按 code 字符串\n */\n\n/** 基类 —— CLI 层统一 `instanceof PluginResolverError` catch。 */\nexport class PluginResolverError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'PluginResolverError';\n }\n}\n\n/**\n * marketplace.json 里找不到请求的 plugin 条目。\n *\n * 触发:`ref.name` 在 marketplace.manifest.plugins 数组里不存在。\n */\nexport class PluginNotFoundInMarketplaceError extends PluginResolverError {\n constructor(\n public readonly pluginName: string,\n public readonly marketplaceName: string,\n public readonly availableNames: readonly string[]\n ) {\n super(\n `Plugin '${pluginName}' not found in marketplace '${marketplaceName}'. ` +\n `Available plugins: ${availableNames.length > 0 ? availableNames.map((n) => `'${n}'`).join(', ') : '(none)'}.`\n );\n this.name = 'PluginNotFoundInMarketplaceError';\n }\n}\n\n/**\n * marketplace.json 里 plugin 条目的 `source` 字段无法落到本地路径。\n *\n * Phase 1 支持的形态:\n * - 字符串:相对 marketplace rootDir 的路径(直接使用)\n * - `{ source: 'local' | ..., path: <相对路径> }`:取 path\n *\n * 不支持:缺 path 的对象(如纯 `{ source: 'git-subdir', url: '...' }`)—— 抛本错误。\n */\nexport class PluginSourceUnresolvableError extends PluginResolverError {\n constructor(\n public readonly pluginName: string,\n public readonly marketplaceName: string,\n public readonly reason: string\n ) {\n super(\n `Cannot resolve local source dir for plugin '${pluginName}' in marketplace '${marketplaceName}': ${reason}`\n );\n this.name = 'PluginSourceUnresolvableError';\n }\n}\n\n/**\n * marketplace cache 里缺 `.claude-plugin/plugin.json` 文件。\n *\n * 触发:`source.path` 指向的目录不存在,或该目录下没有 plugin.json。\n */\nexport class PluginManifestNotFoundError extends PluginResolverError {\n constructor(\n public readonly pluginName: string,\n public readonly expectedManifestPath: string\n ) {\n super(\n `plugin.json not found for plugin '${pluginName}' at '${expectedManifestPath}'. ` +\n `Either the plugin source path is wrong, or the marketplace cache is stale (try 'rush-ai marketplace update').`\n );\n this.name = 'PluginManifestNotFoundError';\n }\n}\n\n/**\n * `.claude-plugin/plugin.json` 文件存在但无法解析(JSON 损坏 / shape 错)。\n */\nexport class PluginManifestCorruptError extends PluginResolverError {\n constructor(\n public readonly manifestPath: string,\n public readonly detail: string\n ) {\n super(\n `plugin.json at '${manifestPath}' is corrupt: ${detail}. Please fix the file or contact the plugin author.`\n );\n this.name = 'PluginManifestCorruptError';\n }\n}\n\n/**\n * `plugin.json.version` 字段缺失/为空/为 \"unknown\"。\n *\n * 修复 #906 P1:Claude Code 默认会把缺 version 的 plugin 落到\n * `cache/<mkt>/<plugin>/unknown/` 坏目录,必须在 resolver 阶段拦下。\n */\nexport class InvalidPluginVersionError extends PluginResolverError {\n constructor(\n public readonly pluginName: string,\n public readonly manifestPath: string,\n public readonly actualValue: string | undefined\n ) {\n super(\n `plugin.json at '${manifestPath}' has invalid version field (got ${\n actualValue === undefined ? 'undefined' : `'${actualValue}'`\n }). ` +\n `Plugin '${pluginName}' must declare a non-empty version (e.g. \"0.1.0\"); ` +\n `'unknown' is not allowed (see #906).`\n );\n this.name = 'InvalidPluginVersionError';\n }\n}\n","/**\n * `.claude-plugin/plugin.json` 读取 + 校验(task-4 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §1.5 / `specs/installer-interface.md` §1.3。\n *\n * 读取策略(对齐 marketplace manifest 的\"最小必要字段\"原则):\n * - 必需字段:`name`(非空 string)+ `version`(非空 string,不得为 \"unknown\")\n * - 其他字段原样透传(`description` / `author` / `mcpServers` / `skills` / `interface` ...)\n * - JSON 损坏 / 顶层非 object → `PluginManifestCorruptError`\n * - 缺 / 非法 version → `InvalidPluginVersionError`(修复 #906 P1,与 plan §7.1 一致)\n *\n * **不做语义校验**:mcpServers 的 command 合法性、interface 子字段类型等都不在此 gate;\n * 那些校验属于各家 Installer 的职责或运行时。\n */\n\nimport { constants as fsConstants } from 'node:fs';\nimport { access, readFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport type { PluginManifest } from '../installers/types.js';\nimport {\n InvalidPluginVersionError,\n PluginManifestCorruptError,\n PluginManifestNotFoundError,\n} from './errors.js';\n\n/**\n * plugin.json 相对 plugin source 目录的路径(Claude Code / Codex 共用此位置;\n * Codex 官方样本里也可能用 `.codex-plugin/plugin.json`,但本 resolver 只认\n * `.claude-plugin/plugin.json`——rush-ai marketplace 生成时统一写入此路径)。\n */\nexport const PLUGIN_MANIFEST_RELATIVE_PATH =\n '.claude-plugin/plugin.json' as const;\n\n/**\n * 从 plugin source 目录读取 + 校验 plugin.json。\n *\n * @param pluginName 用于错误消息(来自 PluginRef.name 或 marketplace entry.name)\n * @param sourceDir plugin 源目录绝对路径(marketplace rootDir 下某子目录)\n *\n * @throws {PluginManifestNotFoundError} 文件不存在或不可读\n * @throws {PluginManifestCorruptError} JSON 损坏 / 顶层非 object / name 缺失\n * @throws {InvalidPluginVersionError} version 缺失 / 空串 / \"unknown\"\n */\nexport async function readPluginManifest(\n pluginName: string,\n sourceDir: string\n): Promise<PluginManifest> {\n const manifestPath = resolve(sourceDir, PLUGIN_MANIFEST_RELATIVE_PATH);\n\n try {\n await access(manifestPath, fsConstants.R_OK);\n } catch {\n throw new PluginManifestNotFoundError(pluginName, manifestPath);\n }\n\n const raw = await readFile(manifestPath, 'utf8');\n return parsePluginManifest(pluginName, raw, manifestPath);\n}\n\n/**\n * 从字符串解析 + 校验 plugin.json。\n *\n * 抽出此函数便于测试里直接传字符串,不依赖磁盘。\n */\nexport function parsePluginManifest(\n pluginName: string,\n raw: string,\n sourcePathForError: string\n): PluginManifest {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new PluginManifestCorruptError(\n sourcePathForError,\n (err as Error).message\n );\n }\n\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new PluginManifestCorruptError(\n sourcePathForError,\n 'root must be a JSON object'\n );\n }\n\n const obj = parsed as Record<string, unknown>;\n\n if (typeof obj.name !== 'string' || obj.name.length === 0) {\n throw new PluginManifestCorruptError(\n sourcePathForError,\n \"'name' must be a non-empty string\"\n );\n }\n\n // version 校验 —— 修复 #906 P1\n const version = obj.version;\n if (\n typeof version !== 'string' ||\n version.length === 0 ||\n version.toLowerCase() === 'unknown'\n ) {\n throw new InvalidPluginVersionError(\n pluginName,\n sourcePathForError,\n typeof version === 'string' ? version : undefined\n );\n }\n\n // `mcpServers` 允许 `Record<string, McpServerConfig>` 或 `string`(Codex 外部文件引用)。\n // 只做顶层类型 gate,不展开校验——那是 Installer 的事。\n if (obj.mcpServers !== undefined) {\n const m = obj.mcpServers;\n const isObject = typeof m === 'object' && m !== null && !Array.isArray(m);\n if (typeof m !== 'string' && !isObject) {\n throw new PluginManifestCorruptError(\n sourcePathForError,\n \"'mcpServers' must be an object (Claude Code inline) or string (Codex external file path)\"\n );\n }\n }\n\n return obj as unknown as PluginManifest;\n}\n","/**\n * `rush-ai plugin *` 公用工具(task-10)。\n *\n * 本文件只负责跨命令共享的纯函数:\n * - `parsePluginRef`:解析 `name[@marketplace]` → `PluginRef`(含自动推断 marketplace)\n * - `parseTargets`:解析 `--target claude-code,codex,cursor` → `InstallTarget[]`\n * - `defaultInstallerFactory`:CLI 默认三家 Installer 构造器(`{ home }` 透传)\n * - `buildRegistryEntry` / `applyRegistry`:CLI 层统一把 InstallResult 写入 rush-ai registry\n *\n * 设计原则(对齐 `specs/installer-interface.md` §4 + `specs/cli-commands.md` §2):\n * - Installer 层不写 registry——本文件提供的 `applyRegistry` 是 **唯一** 写入点\n * - Installer 调用次序 = 用户传入的 `--target` 顺序(CLI 不预过滤,透传)\n * - 所有 HOME 注入通过 option 对象传入,测试强制 tmp HOME\n */\n\nimport {\n ClaudeCodeInstaller,\n type ClaudeCodeInstallerOptions,\n CodexInstaller,\n type CodexInstallerOptions,\n CursorInstaller,\n type CursorInstallerOptions,\n type InstallResult,\n type InstallTarget,\n type PluginCapability,\n type PluginInstaller,\n type PluginRef,\n type ResolvedPlugin,\n type RushRegistryEntry,\n type RushRegistryStore,\n} from '../../installers/index.js';\nimport type {\n MarketplaceCache,\n MarketplaceSource,\n ResolvedMarketplace,\n} from '../../marketplaces/index.js';\nimport { RushError } from '../../util/errors.js';\n\n// ---------------------------------------------------------------------------\n// Targets\n// ---------------------------------------------------------------------------\n\n/**\n * 默认 target 集合(与 spec §2.1 \"默认 claude-code,codex,cursor\" 对齐)。\n *\n * CLI 不预过滤——即便某 target 的 IDE 没装,也调用该 Installer 让它自己返回\n * `status: 'skipped'`(spec §3.1)。\n */\nexport const DEFAULT_TARGETS: readonly InstallTarget[] = [\n 'claude-code',\n 'codex',\n 'cursor',\n] as const;\n\nconst VALID_TARGETS = new Set<InstallTarget>(DEFAULT_TARGETS);\n\n/**\n * 解析 `--target` 参数值。\n *\n * 规则:\n * - `undefined` / 空字符串 → 返回 DEFAULT_TARGETS 拷贝\n * - 逗号分隔,`trim` 每段,忽略空段\n * - 去重(保留首次出现顺序)\n * - 任意段不在合法集合 → 抛 `RushError(code='INVALID_TARGET', exitCode=2)`\n *\n * 不再做大小写规范——spec 要求精确匹配 `claude-code` / `codex` / `cursor`。\n */\nexport function parseTargets(raw?: string): InstallTarget[] {\n if (raw === undefined || raw.trim().length === 0) {\n return [...DEFAULT_TARGETS];\n }\n const segments = raw\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n const seen = new Set<InstallTarget>();\n const out: InstallTarget[] = [];\n for (const seg of segments) {\n if (!VALID_TARGETS.has(seg as InstallTarget)) {\n throw new RushError(\n `Invalid --target '${seg}'. Expected one of: ${[...DEFAULT_TARGETS].join(', ')}`,\n { raw: seg },\n 'INVALID_TARGET',\n 2\n );\n }\n const t = seg as InstallTarget;\n if (!seen.has(t)) {\n seen.add(t);\n out.push(t);\n }\n }\n if (out.length === 0) {\n return [...DEFAULT_TARGETS];\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// PluginRef parsing\n// ---------------------------------------------------------------------------\n\n/**\n * 解析 `<name>[@<marketplace>]`。\n *\n * 规则(spec §1.1 + §2.1 自动推断):\n * - `name` 必须非空,不得含 `@` / 空白\n * - `marketplace` 若省略:从 `cache.list()` 聚合后,按 plugin name 反查 marketplace\n * - 仅 1 个命中 → 自动选\n * - ≥ 2 个命中 → 抛 `RushError(code='AMBIGUOUS_PLUGIN', exitCode=2)`\n * - 0 个命中 → 抛 `RushError(code='PLUGIN_NOT_FOUND', exitCode=2)`\n *\n * `cache` 参数允许测试注入 mock。CLI 生产路径调用方自己 new `MarketplaceCache({ home })`。\n */\nexport async function parsePluginRef(\n raw: string,\n cache: MarketplaceCache\n): Promise<PluginRef> {\n const trimmed = raw.trim();\n if (trimmed.length === 0) {\n throw new RushError(\n 'Plugin name must not be empty',\n { raw },\n 'INVALID_PLUGIN_REF',\n 2\n );\n }\n\n const at = trimmed.indexOf('@');\n if (at === 0) {\n throw new RushError(\n `Invalid plugin ref '${trimmed}': name part is empty`,\n { raw },\n 'INVALID_PLUGIN_REF',\n 2\n );\n }\n\n if (at > 0) {\n const name = trimmed.slice(0, at);\n const marketplace = trimmed.slice(at + 1);\n if (marketplace.length === 0) {\n throw new RushError(\n `Invalid plugin ref '${trimmed}': marketplace part is empty after '@'`,\n { raw },\n 'INVALID_PLUGIN_REF',\n 2\n );\n }\n if (marketplace.includes('@')) {\n throw new RushError(\n `Invalid plugin ref '${trimmed}': marketplace must not contain '@'`,\n { raw },\n 'INVALID_PLUGIN_REF',\n 2\n );\n }\n return { name, marketplace };\n }\n\n // 无 @mkt —— 从 cache 反查\n const matches = await findMarketplacesByPluginName(trimmed, cache);\n if (matches.length === 0) {\n throw new RushError(\n `Plugin '${trimmed}' not found in any registered marketplace. ` +\n `Run 'rush-ai marketplace list' to see known marketplaces.`,\n { name: trimmed },\n 'PLUGIN_NOT_FOUND',\n 2\n );\n }\n if (matches.length > 1) {\n throw new RushError(\n `Plugin '${trimmed}' is ambiguous: found in ${matches.length} marketplaces ` +\n `(${matches.join(', ')}). Specify '${trimmed}@<marketplace>' explicitly.`,\n { name: trimmed, candidates: matches },\n 'AMBIGUOUS_PLUGIN',\n 2\n );\n }\n const only = matches[0];\n if (only === undefined) {\n // Node 的 TS 细粒度类型检查兜底(理论上 length===1 时 [0] 必存在)\n throw new RushError(\n `Internal: expected 1 marketplace match for '${trimmed}'`,\n { name: trimmed },\n 'INTERNAL_ERROR',\n 2\n );\n }\n return { name: trimmed, marketplace: only };\n}\n\n/**\n * 扫描所有 cache 里的 marketplace.json,找哪些 marketplace 提供 `pluginName`。\n *\n * 只读不改——并发安全。marketplace.json 损坏时跳过(宽容策略,免让一个坏 marketplace\n * 阻塞整个 install 命令)。\n */\nexport async function findMarketplacesByPluginName(\n pluginName: string,\n cache: MarketplaceCache\n): Promise<string[]> {\n const cached = await cache.list();\n const hits: string[] = [];\n for (const entry of cached) {\n try {\n const resolved = await cache.get(entry.name);\n const found = resolved.manifest.plugins.some(\n (p) => p.name === pluginName\n );\n if (found) hits.push(entry.name);\n } catch {\n // 单个 marketplace 损坏不阻塞扫描——其他 marketplace 仍可命中\n }\n }\n hits.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));\n return hits;\n}\n\n// ---------------------------------------------------------------------------\n// Installer factory\n// ---------------------------------------------------------------------------\n\n/**\n * 生产 Installer 实例的工厂方法。每个 target 一个,按需 `new`。\n *\n * `home` 注入:透传给三家 Installer 的 `{ home }` option(测试铁律)。\n *\n * Codex 需要额外 `marketplaceSource` 来写 `[marketplaces.<mkt>]` section——由\n * 调用方(install.ts)在拿到 ResolvedMarketplace 后用 `codexMarketplaceSource` 适配。\n */\nexport interface InstallerFactoryOptions {\n /** 测试用 HOME 注入。生产默认走 `os.homedir()`(由 Installer 自己解析)。 */\n home?: string;\n /** Codex:marketplace 在 config.toml 里的 source 描述(install 专用) */\n codexMarketplaceSource?: CodexInstallerOptions['marketplaceSource'];\n /**\n * Claude Code:marketplace 源描述(bug #4 修复)。\n * `install` 时 Installer 据此布置 `plugins/marketplaces/<mkt>/` 并写入\n * `known_marketplaces.json.installLocation`。\n */\n claudeMarketplaceSource?: ClaudeCodeInstallerOptions['marketplaceSource'];\n}\n\n/**\n * 默认 Installer 工厂:按 target 返回对应实例。\n *\n * 每次调用 new 一个新实例——无状态 + 便于 CLI 层显式控制注入参数。\n */\nexport function defaultInstallerFactory(\n target: InstallTarget,\n opts: InstallerFactoryOptions = {}\n): PluginInstaller {\n switch (target) {\n case 'claude-code': {\n const ccOpts: ClaudeCodeInstallerOptions = {};\n if (opts.home !== undefined) ccOpts.home = opts.home;\n if (opts.claudeMarketplaceSource !== undefined) {\n ccOpts.marketplaceSource = opts.claudeMarketplaceSource;\n }\n return new ClaudeCodeInstaller(ccOpts);\n }\n case 'cursor': {\n const cuOpts: CursorInstallerOptions = {};\n if (opts.home !== undefined) cuOpts.home = opts.home;\n return new CursorInstaller(cuOpts);\n }\n case 'codex': {\n const cxOpts: CodexInstallerOptions = {\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n ...(opts.codexMarketplaceSource !== undefined\n ? { marketplaceSource: opts.codexMarketplaceSource }\n : {}),\n };\n return new CodexInstaller(cxOpts);\n }\n default: {\n // 穷举检查——新 target 会在此抛 TS 错\n const _exhaustive: never = target;\n void _exhaustive;\n throw new RushError(\n `Unknown install target: ${String(target)}`,\n {},\n 'UNKNOWN_TARGET',\n 2\n );\n }\n }\n}\n\n/**\n * 把 `MarketplaceSource` 转成 Codex Installer 需要的 `marketplaceSource` 形态。\n *\n * 映射规则:\n * - directory → `{ sourceType: 'local', source: <abs path> }`\n * - github → `{ sourceType: 'git', source: 'https://github.com/<owner>/<repo>.git' }`\n * (通用 Codex 只接受 local/git;github shorthand 必须展开为 https URL,\n * 否则 reload 会报 `unknown variant 'github', expected 'git' or 'local'`。\n * ref 无法通过 source 字段表达,由 marketplace cache 层 shallow clone 处理)\n * - git → `{ sourceType: 'git', source: <url> }`\n * - 其他(rush / npm)→ undefined(Codex Installer 自己走 fallback)\n */\nexport function codexMarketplaceSourceFrom(\n source: MarketplaceSource\n): CodexInstallerOptions['marketplaceSource'] {\n switch (source.kind) {\n case 'directory':\n return { sourceType: 'local', source: source.path };\n case 'github': {\n // Codex CLI 只接受 source_type ∈ { \"git\", \"local\" }。\n // 早期本项目假设 Codex 接受 \"github\" shorthand(phase0-spike 观察到\n // ~/.codex 里 openai-curated 写的是 `source_type = \"github\"`),但实测\n // 那是 OpenAI internal fork 的特殊支持。通用 Codex 会拒绝,启动时\n // 报 \"unknown variant `github`, expected `git` or `local`\" 导致 reload 失败。\n // 因此 github source 必须展开为完整 https git URL。\n // ref 目前无法通过 source 字段表达;marketplace cache 层已经通过 git\n // `--branch <ref>` 处理 shallow clone,Codex 侧仅需要可 clone 的 URL。\n const url = `https://github.com/${source.owner}/${source.repo}.git`;\n return { sourceType: 'git', source: url };\n }\n case 'git':\n return { sourceType: 'git', source: source.url };\n default:\n return undefined;\n }\n}\n\n/**\n * 把 `ResolvedMarketplace` 映射成 `ClaudeCodeInstaller` 的 `marketplaceSource` option。\n *\n * - `rootDir`:ResolvedMarketplace.rootDir(对 directory source 是用户给的路径;\n * 对 github: source 是 `~/.rush/marketplaces/<name>/`)\n * - `descriptor`:按 source.kind 映射到 Claude Code `known_marketplaces.json.source`\n * 的字段结构\n * - directory → `{ kind: 'directory', path: <abs path> }`\n * - github → `{ kind: 'github', repo: '<owner>/<repo>' }`(Claude Code 认 github shorthand)\n * - git → `{ kind: 'git', url: <url> }`\n * - 其他(rush / npm)→ undefined(Installer 退化到 directory fallback)\n */\nexport function claudeMarketplaceSourceFrom(\n marketplace: ResolvedMarketplace\n): ClaudeCodeInstallerOptions['marketplaceSource'] {\n const src = marketplace.source;\n switch (src.kind) {\n case 'directory':\n return {\n rootDir: marketplace.rootDir,\n descriptor: { kind: 'directory', path: src.path },\n };\n case 'github':\n return {\n rootDir: marketplace.rootDir,\n descriptor: {\n kind: 'github',\n repo: `${src.owner}/${src.repo}`,\n ...(src.ref !== undefined ? { ref: src.ref } : {}),\n },\n };\n case 'git':\n return {\n rootDir: marketplace.rootDir,\n descriptor: { kind: 'git', url: src.url },\n };\n default:\n return undefined;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registry writer\n// ---------------------------------------------------------------------------\n\n/**\n * 把一组 `InstallResult`(同一个 plugin,跨 target)折合成 `RushRegistryEntry`。\n *\n * 语义:\n * - `installedAt` / `sourceVersion`:用 CLI 层时钟 + 当前 plugin.version\n * - `targets[t]` 仅记录 status != skipped 的 target;skipped 的不写 registry\n * (spec §3.2 没明说,但 skipped 的 target 不属于\"装了\",写进去会让 list 输出噪声)\n * - targets[t].files / mcpKeys 直接来自 `InstallResult.artifacts`\n * - 若本次所有 target 都是 skipped,返回 undefined——调用方应 no-op(不写 registry)\n *\n * 幂等:同 ref 反复调 → 后面的覆盖前面的(registry 是 read-modify-write 语义)。\n */\nexport function buildRegistryEntry(\n plugin: ResolvedPlugin,\n results: readonly InstallResult[],\n nowIso: string\n): RushRegistryEntry | undefined {\n const nonSkipped = results.filter((r) => r.status !== 'skipped');\n if (nonSkipped.length === 0) {\n return undefined;\n }\n const targets: RushRegistryEntry['targets'] = {};\n for (const r of nonSkipped) {\n targets[r.target] = {\n status: r.status,\n version: plugin.version,\n files: [...r.artifacts.files],\n mcpKeys: [...r.artifacts.mcpKeys],\n skipped: [...r.skipped] as PluginCapability[],\n installedAt: nowIso,\n };\n }\n return {\n installedAt: nowIso,\n sourceVersion: plugin.version,\n targets,\n };\n}\n\n/**\n * 把 install 结果合并进 rush-ai registry + 落盘。\n *\n * 失败时抛 `RushError`——本函数只负责 registry 写入;Installer 的 disk 已经装好,\n * 所以即便 registry 写失败,用户也能看到 CLI 输出中\"三家装好\"的 `✓`,只是 rush-ai\n * 这份\"账本\"落空。CLI 层应该把 registry 失败报给用户,让他们手动 `rush-ai plugin list`\n * 观察偏差。\n *\n * 注:本函数不重试——`RushRegistryStore.save()` 自己做一次并发 retry,超过一次抛\n * `RushRegistryConflictError`。CLI 层 catch 后统一转 RushError。\n */\nexport async function applyInstallToRegistry(\n store: RushRegistryStore,\n ref: PluginRef,\n plugin: ResolvedPlugin,\n results: readonly InstallResult[],\n nowIso: string\n): Promise<void> {\n const built = buildRegistryEntry(plugin, results, nowIso);\n if (!built) {\n // 全部 skipped,不写 registry\n return;\n }\n\n // Merge 语义:`--target <subset>` 只装部分 IDE 时,本次 results 不含其他\n // target。必须保留 existing.targets 里未被本次覆盖的条目,否则 uninstall\n // 读不到其他 IDE 的 artifacts → 磁盘泄漏。\n //\n // 顶层 `installedAt` 策略:**首次安装时间保留**。首次 install 用本次的 nowIso;\n // 后续 --target subset / --force reinstall 不覆盖,方便用户追溯\"这个 plugin\n // 最早什么时候出现在我的系统\"。每个 target 的 `installedAt` 另记在\n // `targets.<ide>.installedAt`,反映本次写入时间。\n const existing = store.get(ref);\n const mergedTargets = {\n ...(existing?.targets ?? {}),\n ...built.targets,\n };\n store.set(ref, {\n installedAt: existing?.installedAt ?? built.installedAt,\n sourceVersion: built.sourceVersion,\n targets: mergedTargets,\n });\n await store.save();\n}\n\n/**\n * 卸载对 registry 的影响——把本次卸掉的 targets 从 registry 条目里移除。\n *\n * 如果清理后该 plugin 没有任何 target 了,整条删除。\n *\n * `uninstalledTargets` 是 \"本次真正走了 uninstall 流程(status != skipped)\" 的\n * target 列表——跳过的 target 不改 registry。\n */\nexport async function applyUninstallToRegistry(\n store: RushRegistryStore,\n ref: PluginRef,\n uninstalledTargets: readonly InstallTarget[]\n): Promise<void> {\n if (uninstalledTargets.length === 0) return;\n const existing = store.get(ref);\n if (!existing) {\n await store.save();\n return;\n }\n const nextTargets = { ...existing.targets };\n for (const t of uninstalledTargets) {\n delete nextTargets[t];\n }\n if (Object.keys(nextTargets).length === 0) {\n store.delete(ref);\n } else {\n store.set(ref, { ...existing, targets: nextTargets });\n }\n await store.save();\n}\n\n// ---------------------------------------------------------------------------\n// Typed re-exports(给 commands/plugin 子模块用)\n// ---------------------------------------------------------------------------\n\nexport type {\n InstallResult,\n InstallTarget,\n PluginInstaller,\n ResolvedMarketplace,\n};\n","/**\n * `rush-ai plugin install <name>[@<mkt>] [--target ...] [--force] [--dry-run]`(task-10)。\n *\n * Source of truth:\n * - `specs/cli-commands.md` §2.1\n * - `specs/installer-interface.md` §1-§4\n *\n * 流程(对齐 spec §4 \"CLI 顶层调用\"):\n * 1. 解析 plugin ref(`<name>[@<mkt>]`,含自动推断 marketplace)\n * 2. 用 MarketplaceCache 拿 ResolvedMarketplace + resolvePlugin → ResolvedPlugin\n * 3. (task-12 才接入 migration hook,task-10 **不**做)\n * 4. 按 targets 串行调用每个 Installer(不预过滤,让 Installer 自己返回 skipped)\n * - 每个 Installer 内部已做 detect()=false 短路\n * - 失败的 target 保留其他家继续跑(spec §4 \"默认策略:一家 failed 不拖累另一家\")\n * 5. 一次性把非 skipped 的结果写入 rush-ai registry(CLI 层唯一写入点)\n * 6. 返回 `InstallCommandResult`——commander wrapper 打印汇总\n *\n * 退出码(spec §2.1 v1 单一规则):\n * - 0 所有被调用 target 均为 ok/skipped\n * - 1 任何一个 target 出现 failed\n * - 2 插件解析失败(marketplace 里没 / 多义等)\n *\n * --dry-run:所有 Installer 调用 `opts.dryRun: true`,不写磁盘,不写 registry。\n */\n\nimport type { Command } from 'commander';\nimport {\n type InstallResult,\n type InstallTarget,\n type PluginInstaller,\n type PluginRef,\n type ResolvedPlugin,\n RushRegistryConflictError,\n RushRegistryCorruptError,\n RushRegistrySchemaMismatchError,\n RushRegistryStore,\n} from '../../installers/index.js';\nimport {\n MarketplaceCache,\n MarketplaceNotFoundError,\n type ResolvedMarketplace,\n} from '../../marketplaces/index.js';\nimport {\n type MaybeRunMigrationOutcome,\n type MigrationReporter,\n maybeRunMigration,\n} from '../../migration/index.js';\nimport { output } from '../../output/logger.js';\nimport {\n PluginManifestNotFoundError,\n PluginNotFoundInMarketplaceError,\n PluginResolverError,\n type ResolvePluginOptions,\n resolvePlugin,\n} from '../../plugins/index.js';\nimport { RushError } from '../../util/errors.js';\nimport {\n applyInstallToRegistry,\n claudeMarketplaceSourceFrom,\n codexMarketplaceSourceFrom,\n DEFAULT_TARGETS,\n defaultInstallerFactory,\n parsePluginRef,\n parseTargets,\n} from './common.js';\n\n// ---------------------------------------------------------------------------\n// Input / Output 数据结构\n// ---------------------------------------------------------------------------\n\nexport interface InstallCommandInput {\n /** 原始 `<name>[@<mkt>]` 参数 */\n ref: string;\n /** `--target` 原始值;undefined 走默认三家 */\n targetRaw?: string;\n /** `--force` */\n force?: boolean;\n /** `--dry-run` */\n dryRun?: boolean;\n /**\n * `--target` 是否被用户显式指定(用于汇总输出格式差异化——显式列出的\n * target 若 skipped 要给 \"ⓘ Skipped: <target> (IDE not installed)\" 提示,\n * 默认走的 target skipped 则沉默)。\n *\n * 测试不传时走 `targetRaw !== undefined` 自动推断。\n */\n targetExplicit?: boolean;\n\n /** 测试用 HOME 注入(生产默认 `os.homedir()`) */\n home?: string;\n /** 测试/advanced:直接注入 MarketplaceCache(如 mock GitRunner) */\n cache?: MarketplaceCache;\n /** 测试/advanced:直接注入 RushRegistryStore 实例 */\n registry?: RushRegistryStore;\n /** 测试/advanced:覆盖 resolvePlugin 行为(避免真实 IO) */\n resolvePluginFn?: (\n ref: PluginRef,\n marketplace: ResolvedMarketplace,\n options?: ResolvePluginOptions\n ) => Promise<ResolvedPlugin>;\n /** 测试/advanced:覆盖 Installer 工厂 */\n installerFactory?: (\n target: InstallTarget,\n opts: { home?: string; marketplace: ResolvedMarketplace }\n ) => PluginInstaller;\n /** 测试用:固定时间戳注入 */\n now?: () => string;\n /**\n * 测试/advanced:覆盖 pre-install migration hook。生产默认 `maybeRunMigration`。\n *\n * **铁律**:覆盖的 runner 内部**绝对不能**再调本 `runInstall`——否则会和\n * hook 形成递归。生产 `maybeRunMigration` 直接调 `ClaudeCodeInstaller.install()`\n * 绕过了这一层,测试里如果自定义也必须遵守。\n */\n migrationRunner?: typeof maybeRunMigration;\n /**\n * 迁移流程的进度/告警输出。默认沉默(v1 不强制打印 migration 输出——\n * commander wrapper 会传入一个写 stdout 的 reporter)。\n */\n migrationReporter?: MigrationReporter;\n}\n\n/**\n * 一次 install 命令的完整结果——commander wrapper 根据此对象打印汇总 + 决定退出码。\n */\nexport interface InstallCommandResult {\n ref: PluginRef;\n /** 最终解析到的 ResolvedPlugin(可用于测试断言 version / capabilities) */\n plugin: ResolvedPlugin;\n /** 按输入 targets 顺序排列的结果 */\n results: InstallResult[];\n /** 用户是否显式指定了 --target(影响汇总输出) */\n targetExplicit: boolean;\n /** --dry-run 模式标识 */\n dryRun: boolean;\n /** 聚合 exit code(0/1/2)。commander wrapper 直接使用。 */\n exitCode: 0 | 1 | 2;\n /** registry 写失败时的提示(非阻断;只在人看模式下展示) */\n registryError?: string;\n /**\n * 迁移 hook 的结果(仅装 `rush@rush-marketplace` 时非 undefined)。用于测试\n * 断言;commander wrapper 不直接使用此字段——迁移的 UX 已通过 reporter 打印。\n */\n migration?: MaybeRunMigrationOutcome;\n}\n\n// ---------------------------------------------------------------------------\n// Pure runner\n// ---------------------------------------------------------------------------\n\n/**\n * `rush-ai plugin install` 的纯业务实现。无 console I/O。\n *\n * 失败策略(见 spec §2.1 退出码):\n * - 插件解析失败 → 抛 `RushError(exitCode=2)`\n * - Installer 部分失败 → 不抛,返回结果里 exitCode=1\n * - registry 写失败 → 不抛,结果里填 registryError,exitCode 仍沿用 Installer 结果\n * (spec §10 风险矩阵:\"rush-ai 进程崩在安装中途\":registry 丢失不影响本次\n * Installer 的真实磁盘状态;用户可 `rush-ai plugin list` 手动观察差异)\n */\nexport async function runInstall(\n input: InstallCommandInput\n): Promise<InstallCommandResult> {\n const targets = parseTargets(input.targetRaw);\n const targetExplicit = input.targetExplicit ?? input.targetRaw !== undefined;\n const force = input.force === true;\n const dryRun = input.dryRun === true;\n const now = input.now ?? (() => new Date().toISOString());\n\n // 1. 解析 plugin ref\n const cache =\n input.cache ??\n new MarketplaceCache(input.home !== undefined ? { home: input.home } : {});\n const ref = await parsePluginRef(input.ref, cache);\n\n // 2. 拿 ResolvedMarketplace + resolvePlugin\n let marketplace: ResolvedMarketplace;\n try {\n marketplace = await cache.get(ref.marketplace);\n } catch (err) {\n if (err instanceof MarketplaceNotFoundError) {\n throw new RushError(\n `Marketplace '${ref.marketplace}' is not in the local cache. ` +\n `Run 'rush-ai marketplace add <source>' first.`,\n { marketplaceName: ref.marketplace },\n 'MARKETPLACE_NOT_FOUND',\n 2\n );\n }\n throw err;\n }\n\n const resolveFn = input.resolvePluginFn ?? resolvePlugin;\n let plugin: ResolvedPlugin;\n try {\n plugin = await resolveFn(ref, marketplace);\n } catch (err) {\n if (err instanceof PluginNotFoundInMarketplaceError) {\n throw new RushError(\n err.message,\n { pluginName: ref.name, marketplace: ref.marketplace },\n 'PLUGIN_NOT_FOUND_IN_MARKETPLACE',\n 2\n );\n }\n if (err instanceof PluginManifestNotFoundError) {\n throw new RushError(\n err.message,\n { pluginName: ref.name, marketplace: ref.marketplace },\n 'PLUGIN_MANIFEST_NOT_FOUND',\n 2\n );\n }\n if (err instanceof PluginResolverError) {\n throw new RushError(\n err.message,\n { pluginName: ref.name, marketplace: ref.marketplace },\n 'PLUGIN_RESOLVE_FAILED',\n 2\n );\n }\n throw err;\n }\n\n // 3. Pre-install migration hook(task-12)——迁移老版本 rush-ai 用户\n // 设计要点:\n // - 函数内部对非 `rush@rush-marketplace` 自动 no-op;这里无条件调即可\n // - `maybeRunMigration` 调 `ClaudeCodeInstaller.install()` 绕过本 runInstall,\n // 所以**不会**递归触发本 hook\n // - 迁移失败**不抛**——outcome.kind === 'failure' 已走 log + registry,install 主流程继续\n // - `--dry-run` 不触发迁移(不写磁盘的原则)\n let migrationOutcome: MaybeRunMigrationOutcome | undefined;\n if (!dryRun) {\n const runMigration = input.migrationRunner ?? maybeRunMigration;\n try {\n migrationOutcome = await runMigration({\n ref,\n resolvedPlugin: plugin,\n ...(input.home !== undefined ? { home: input.home } : {}),\n ...(input.migrationReporter !== undefined\n ? { reporter: input.migrationReporter }\n : {}),\n ...(input.now !== undefined ? { now: input.now } : {}),\n });\n } catch (err) {\n // `maybeRunMigration` 契约是\"永不抛错\"——这里只是防御性兜底,实际不应命中。\n // 真实阶段信息拿不到(因为违反契约时未能走正常返回),用 `registry-write`\n // 作为保守默认——它表达\"最顶层 wrapper 层面的失败\",优于把锅甩给具体阶段。\n migrationOutcome = {\n kind: 'failure',\n reason: `migration hook contract violation (runner threw): ${\n err instanceof Error ? err.message : String(err)\n }`,\n stage: 'registry-write',\n };\n }\n }\n\n // 4. 按 target 串行调用每个 Installer(不预过滤,让 Installer 自己决定 skipped)\n const results: InstallResult[] = [];\n for (const t of targets) {\n const factory =\n input.installerFactory ??\n ((target, opts) =>\n defaultInstallerFactory(target, {\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n ...(target === 'codex'\n ? {\n codexMarketplaceSource: codexMarketplaceSourceFrom(\n opts.marketplace.source\n ),\n }\n : {}),\n ...(target === 'claude-code'\n ? {\n claudeMarketplaceSource: claudeMarketplaceSourceFrom(\n opts.marketplace\n ),\n }\n : {}),\n }));\n const installer = factory(t, {\n ...(input.home !== undefined ? { home: input.home } : {}),\n marketplace,\n });\n try {\n const result = await installer.install(plugin, { force, dryRun });\n // v1 partial 防御性收敛(spec §1.4 \"Installer 实现不得构造 partial\"):\n // 若 Installer 违反契约返回 partial,CLI 层不透传——强制收敛为 failed,\n // 带上 note 让开发者知道是契约违反。\n if (result.status === 'partial') {\n results.push({\n ...result,\n status: 'failed',\n errors: [\n ...(result.errors ?? []),\n `Installer '${t}' returned 'partial' which is not permitted in v1 (spec §1.4). Treating as failed.`,\n ],\n });\n } else {\n results.push(result);\n }\n } catch (err) {\n // Installer 契约:bug 级异常不 swallow。但 CLI 层聚合视角下,让整个命令\n // 因为单一 IDE 的 bug 挂掉不合理——包装为 failed 记录,供 --debug 模式\n // 下用户追 cause。继续跑其他 target(spec §4 \"一家 failed 不拖累另一家\")。\n const message = err instanceof Error ? err.message : String(err);\n results.push({\n target: t,\n status: 'failed',\n included: [],\n skipped: [],\n artifacts: { files: [], mcpKeys: [] },\n errors: [message],\n });\n }\n }\n\n // 5. 把非 skipped 结果写入 rush-ai registry(CLI 层唯一写入点)\n // dry-run 或全部 skipped → 跳过 registry 写入\n let registryError: string | undefined;\n if (!dryRun) {\n try {\n const store =\n input.registry ??\n (await RushRegistryStore.load(\n input.home !== undefined ? { home: input.home } : {}\n ));\n await applyInstallToRegistry(store, ref, plugin, results, now());\n } catch (err) {\n registryError = formatRegistryError(err);\n }\n }\n\n // 6. 聚合 exit code\n const hasFailed = results.some((r) => r.status === 'failed');\n const exitCode: 0 | 1 | 2 = hasFailed ? 1 : 0;\n\n return {\n ref,\n plugin,\n results,\n targetExplicit,\n dryRun,\n exitCode,\n ...(registryError !== undefined ? { registryError } : {}),\n ...(migrationOutcome !== undefined ? { migration: migrationOutcome } : {}),\n };\n}\n\nfunction formatRegistryError(err: unknown): string {\n if (err instanceof RushRegistryConflictError) {\n return `rush-ai registry write conflict: ${err.message}`;\n }\n if (err instanceof RushRegistryCorruptError) {\n return `rush-ai registry is corrupt: ${err.message}`;\n }\n if (err instanceof RushRegistrySchemaMismatchError) {\n return `rush-ai registry schema mismatch: ${err.message}`;\n }\n if (err instanceof Error) {\n return `rush-ai registry write failed: ${err.message}`;\n }\n return `rush-ai registry write failed: ${String(err)}`;\n}\n\n// ---------------------------------------------------------------------------\n// Commander wrapper\n// ---------------------------------------------------------------------------\n\nexport function registerInstallCommand(group: Command, _root: Command): void {\n group\n .command('install')\n .description(\n 'Install a plugin to one or more IDEs (Claude Code, Codex, Cursor)'\n )\n .argument('<ref>', 'plugin reference: <name> or <name>@<marketplace>')\n .option(\n '--target <list>',\n `comma-separated targets (default: ${[...DEFAULT_TARGETS].join(',')})`\n )\n .option('--force', 'reinstall even if same version is already installed')\n .option('--dry-run', \"don't touch disk; print the plan\")\n .action(\n async (\n ref: string,\n opts: { target?: string; force?: boolean; dryRun?: boolean }\n ) => {\n const result = await runInstall({\n ref,\n ...(opts.target !== undefined ? { targetRaw: opts.target } : {}),\n targetExplicit: opts.target !== undefined,\n ...(opts.force ? { force: true } : {}),\n ...(opts.dryRun ? { dryRun: true } : {}),\n migrationReporter: {\n info: (line) => output.dim(line),\n warn: (line) => output.warn(line),\n },\n });\n\n printInstallSummary(result);\n\n if (result.exitCode !== 0) {\n // 交给 index.ts 顶层 handler 转成 process.exit;保持错误语义一致\n throw new RushError(\n `Install completed with failures (see output above)`,\n {},\n 'INSTALL_PARTIAL_FAILED',\n result.exitCode\n );\n }\n }\n );\n}\n\n/**\n * 人看模式汇总输出(spec §2.1 / plan §5 示例对齐)。\n *\n * 行格式:\n * ```\n * ✓ Installed to Claude Code (commands + skills + rules + MCP)\n * ✓ Installed to Codex (skills + MCP) [commands/rules/hooks 跳过: Codex 不支持]\n * ⓘ Skipped: Cursor (IDE not installed)\n * ✗ Failed to install to Codex: <reason>\n * ```\n *\n * 规则:\n * - 能力跳过(commands/rules/hooks 在不支持的 IDE)**不降低行级 `✓`**\n * - skipped + 用户显式 --target → 显示 \"ⓘ Skipped\"\n * - skipped + 默认 target → 沉默(不显示)\n * - failed → `✗` + 首条 error\n * - dry-run → 前缀 \"(dry-run)\";artifacts 列表按 verbose 原则简化呈现\n */\nexport function printInstallSummary(result: InstallCommandResult): void {\n const { plugin, results, targetExplicit, dryRun } = result;\n const tag = dryRun ? ' (dry-run)' : '';\n output.log(\n `Installing ${plugin.ref.name}@${plugin.ref.marketplace} v${plugin.version}...${tag}`\n );\n output.newline();\n\n for (const r of results) {\n const label = targetDisplayName(r.target);\n if (r.status === 'skipped') {\n if (targetExplicit) {\n output.dim(`ⓘ Skipped: ${label} (IDE not installed)`);\n }\n // 默认 target:沉默跳过\n continue;\n }\n if (r.status === 'failed') {\n const firstError = r.errors?.[0] ?? 'unknown error';\n output.error(`✗ Failed to install to ${label}: ${firstError}`);\n continue;\n }\n // ok —— v1 partial 已在 runner 里收敛为 failed,此分支只处理 ok 语义\n const includedFrag =\n r.included.length > 0 ? `(${formatCapabilityList(r.included)})` : '';\n const skippedFrag = formatSkippedFragment(r.skipped, r.target);\n const line = [`✓ Installed to ${label}`, includedFrag, skippedFrag]\n .filter((s) => s.length > 0)\n .join(' ');\n output.success(line);\n }\n\n // Next steps(仅人看模式;dry-run 跳过 + 至少有一家 ok 才展示)\n // v1 不支持 partial——runner 已把任何 partial 收敛为 failed,此处只看 ok\n const anyOk = results.some((r) => r.status === 'ok');\n if (anyOk && !dryRun) {\n output.newline();\n output.dim('Next steps:');\n for (const r of results) {\n if (r.status !== 'ok') continue;\n output.dim(\n ` - ${targetDisplayName(r.target)}: ${nextStepFor(r.target)}`\n );\n }\n }\n\n if (result.registryError) {\n output.newline();\n output.warn(result.registryError);\n output.dim(\n ' The plugin is still installed on disk; run `rush-ai plugin list` to inspect.'\n );\n }\n}\n\n/** 能力列表 \"commands + skills + rules + MCP\" 格式化。 */\nfunction formatCapabilityList(caps: readonly string[]): string {\n return caps.map((c) => (c === 'mcp' ? 'MCP' : c)).join(' + ');\n}\n\n/**\n * 生成尾部方括号跳过提示(如 `[commands/rules/hooks 跳过: Codex 不支持]`)。\n * 空 skipped 返回空串。\n */\nfunction formatSkippedFragment(\n skipped: readonly string[],\n target: InstallTarget\n): string {\n if (skipped.length === 0) return '';\n const joined = skipped.join('/');\n return `[${joined} 跳过: ${targetDisplayName(target)} 不支持]`;\n}\n\nfunction targetDisplayName(t: InstallTarget): string {\n switch (t) {\n case 'claude-code':\n return 'Claude Code';\n case 'codex':\n return 'Codex';\n case 'cursor':\n return 'Cursor';\n default: {\n const _exhaustive: never = t;\n void _exhaustive;\n return String(t);\n }\n }\n}\n\nfunction nextStepFor(t: InstallTarget): string {\n switch (t) {\n case 'claude-code':\n return \"restart Claude Code or run '/plugin list' to see the installed plugin\";\n case 'codex':\n return 'start a new Codex session';\n case 'cursor':\n return 'restart Cursor';\n default:\n return 'restart the IDE';\n }\n}\n","/**\n * `rush-ai plugin list [--available] [--target ...]`(task-10)。\n *\n * Source of truth: `specs/cli-commands.md` §2.3\n *\n * 两种模式:\n * - 默认:从 rush-ai registry 读已装插件,跨 target 聚合输出\n * ```\n * NAME@MARKETPLACE VERSION TARGETS\n * rush@rush-marketplace 0.2.0 claude-code ✓ codex ✓ cursor ✓\n * yuanli-infra@rush-marketplace 1.0.0 claude-code ✓ codex - cursor ✓\n * ```\n * - `✓` = status=ok\n * - `-` = status=skipped(IDE 没装)\n * - `✗` = status=failed(上次 install 出错,registry 仍保留)\n *\n * - `--available`:从所有 cache 里的 marketplace 读可装插件,标注已装/未装\n * ```\n * AVAILABLE PLUGINS\n * rush-marketplace:\n * - rush (v0.2.0) [installed]\n * - yuanli-infra (v1.0.0) [installed]\n * - octo (v0.5.0) [not installed]\n * ```\n *\n * `--target` 过滤显示列(默认显示三家;显式指定只展示对应列)。\n *\n * JSON 模式:`--json`(继承 root --output 选项)返回结构化数据。\n */\n\nimport type { Command } from 'commander';\nimport {\n type InstallStatus,\n type InstallTarget,\n type PluginRef,\n RushRegistryStore,\n} from '../../installers/index.js';\nimport { MarketplaceCache } from '../../marketplaces/index.js';\nimport { resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport { DEFAULT_TARGETS, parseTargets } from './common.js';\n\nexport interface ListCommandInput {\n available?: boolean;\n targetRaw?: string;\n home?: string;\n cache?: MarketplaceCache;\n registry?: RushRegistryStore;\n}\n\nexport interface InstalledPluginRow {\n ref: PluginRef;\n version: string;\n /** 每个 target 的 status;undefined = registry 里没条目(视为 \"-\") */\n targets: {\n [T in InstallTarget]?: InstallStatus;\n };\n}\n\nexport interface AvailablePluginRow {\n marketplace: string;\n name: string;\n version: string | undefined;\n description: string | undefined;\n installed: boolean;\n}\n\nexport interface ListCommandResult {\n mode: 'installed' | 'available';\n targets: InstallTarget[];\n installed?: InstalledPluginRow[];\n available?: AvailablePluginRow[];\n}\n\n/**\n * 纯函数实现。\n *\n * - 默认模式:列当前 registry 里的所有 plugin,过滤展示列\n * - `--available`:扫所有 marketplace cache,列 marketplace.json 里声明的 plugin\n */\nexport async function runList(\n input: ListCommandInput = {}\n): Promise<ListCommandResult> {\n const targets = parseTargets(input.targetRaw);\n\n const cache =\n input.cache ??\n new MarketplaceCache(input.home !== undefined ? { home: input.home } : {});\n const registry =\n input.registry ??\n (await RushRegistryStore.load(\n input.home !== undefined ? { home: input.home } : {}\n ));\n\n if (input.available) {\n return runAvailable(cache, registry, targets);\n }\n return runInstalled(registry, targets);\n}\n\nfunction runInstalled(\n registry: RushRegistryStore,\n targets: InstallTarget[]\n): ListCommandResult {\n const installed: InstalledPluginRow[] = [];\n for (const [ref, entry] of registry.list()) {\n const row: InstalledPluginRow = {\n ref,\n version: entry.sourceVersion,\n targets: {},\n };\n for (const t of targets) {\n const te = entry.targets[t];\n if (te) row.targets[t] = te.status;\n }\n installed.push(row);\n }\n return { mode: 'installed', targets, installed };\n}\n\nasync function runAvailable(\n cache: MarketplaceCache,\n registry: RushRegistryStore,\n targets: InstallTarget[]\n): Promise<ListCommandResult> {\n const installedKeys = new Set<string>();\n for (const [ref] of registry.list()) {\n installedKeys.add(`${ref.name}@${ref.marketplace}`);\n }\n\n const rows: AvailablePluginRow[] = [];\n const mkts = await cache.list();\n for (const mkt of mkts) {\n try {\n const resolved = await cache.get(mkt.name);\n for (const plugin of resolved.manifest.plugins) {\n const refKey = `${plugin.name}@${mkt.name}`;\n rows.push({\n marketplace: mkt.name,\n name: plugin.name,\n version: plugin.version,\n description: plugin.description,\n installed: installedKeys.has(refKey),\n });\n }\n } catch {\n // 单个 marketplace 解析失败 → 跳过(避免一家坏拖累全部)\n }\n }\n // 按 marketplace 名 + plugin 名字典序\n rows.sort((a, b) => {\n if (a.marketplace !== b.marketplace) {\n return a.marketplace < b.marketplace ? -1 : 1;\n }\n return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;\n });\n return { mode: 'available', targets, available: rows };\n}\n\n// ---------------------------------------------------------------------------\n// Commander wrapper\n// ---------------------------------------------------------------------------\n\nexport function registerListCommand(group: Command, root: Command): void {\n group\n .command('list')\n .alias('ls')\n .description(\n 'List installed plugins (default) or available plugins (--available)'\n )\n .option('--available', 'list plugins available in cached marketplaces')\n .option(\n '--target <list>',\n `comma-separated targets (default: ${[...DEFAULT_TARGETS].join(',')})`\n )\n .action(async (opts: { available?: boolean; target?: string }) => {\n const format = resolveFormat(root.opts());\n const result = await runList({\n ...(opts.available ? { available: true } : {}),\n ...(opts.target !== undefined ? { targetRaw: opts.target } : {}),\n });\n\n if (format === 'json') {\n output.log(JSON.stringify(result, null, 2));\n return;\n }\n printListSummary(result);\n });\n}\n\nexport function printListSummary(result: ListCommandResult): void {\n if (result.mode === 'available') {\n printAvailable(result);\n return;\n }\n printInstalled(result);\n}\n\nfunction printInstalled(result: ListCommandResult): void {\n const rows = result.installed ?? [];\n if (rows.length === 0) {\n output.log('(no plugins installed)');\n output.dim(\n \" Install one with 'rush-ai plugin install <name>[@<marketplace>]'\"\n );\n return;\n }\n\n // 对齐表头:NAME@MARKETPLACE VERSION TARGETS\n const refStrs = rows.map((r) => `${r.ref.name}@${r.ref.marketplace}`);\n const nameWidth = Math.max(\n 'NAME@MARKETPLACE'.length,\n ...refStrs.map((s) => s.length)\n );\n const versionWidth = Math.max(\n 'VERSION'.length,\n ...rows.map((r) => r.version.length)\n );\n\n const header =\n `${'NAME@MARKETPLACE'.padEnd(nameWidth)} ` +\n `${'VERSION'.padEnd(versionWidth)} ` +\n `TARGETS`;\n output.log(output.bold(header));\n\n for (let i = 0; i < rows.length; i++) {\n const r = rows[i];\n const refStr = refStrs[i];\n if (!r || refStr === undefined) continue;\n const targetFrags: string[] = [];\n for (const t of result.targets) {\n const status = r.targets[t];\n targetFrags.push(`${t} ${statusIcon(status)}`);\n }\n output.log(\n `${refStr.padEnd(nameWidth)} ` +\n `${r.version.padEnd(versionWidth)} ` +\n `${targetFrags.join(' ')}`\n );\n }\n}\n\nfunction printAvailable(result: ListCommandResult): void {\n const rows = result.available ?? [];\n if (rows.length === 0) {\n output.log('(no available plugins)');\n output.dim(\n \" Add a marketplace with 'rush-ai marketplace add <source>' to discover plugins.\"\n );\n return;\n }\n output.log(output.bold('AVAILABLE PLUGINS'));\n\n const byMkt = new Map<string, AvailablePluginRow[]>();\n for (const r of rows) {\n const arr = byMkt.get(r.marketplace) ?? [];\n arr.push(r);\n byMkt.set(r.marketplace, arr);\n }\n for (const [mkt, mktRows] of byMkt) {\n output.log(`${mkt}:`);\n for (const row of mktRows) {\n const versionFrag = row.version ? ` (v${row.version})` : '';\n const installedFrag = row.installed ? '[installed]' : '[not installed]';\n output.log(` - ${row.name}${versionFrag} ${installedFrag}`);\n }\n }\n}\n\nfunction statusIcon(status: InstallStatus | undefined): string {\n if (status === undefined || status === 'skipped') return '-';\n if (status === 'ok') return '✓';\n if (status === 'failed') return '✗';\n // partial v1 不会出现,兜底按 failed 处理(spec §2.3)\n return '✗';\n}\n","/**\n * `rush-ai plugin uninstall <name>[@<mkt>] [--target ...] [--dry-run]`(task-10)。\n *\n * Source of truth:\n * - `specs/cli-commands.md` §2.2\n * - `specs/installer-interface.md` §2\n *\n * 流程:\n * 1. 解析 plugin ref(`name@mkt` 必须明确——优先读 registry;不在 registry 里但 cache\n * 能唯一命中 marketplace 也接受;否则报 ref 解析错)\n * 2. 对每个 target 调 `installer.uninstall(ref, { dryRun })`\n * - Installer 未 detect 到 IDE → 自己返回 `status: 'ok' + artifacts 空`(按 spec 行为)\n * - Installer failed → 保留其他 target 继续\n * 3. `--dry-run`:不写 registry,展示 artifacts 清单\n * 4. 非 dry-run:把对应 target 从 registry 条目里清理(全清后整条删)\n *\n * 输出示例(spec §2.2):\n * ```\n * Uninstalling rush@rush-marketplace...\n *\n * ✓ Removed from Claude Code\n * ✓ Removed from Codex\n * ✓ Removed from Cursor (3 files, 1 MCP entry)\n * ```\n *\n * 退出码:\n * - 0 所有被调用 target 均为 ok / skipped\n * - 1 任何一个 target failed\n * - 2 ref 解析失败\n */\n\nimport type { Command } from 'commander';\nimport {\n type InstallTarget,\n type PluginInstaller,\n type PluginRef,\n RushRegistryConflictError,\n RushRegistryCorruptError,\n RushRegistrySchemaMismatchError,\n RushRegistryStore,\n type UninstallResult,\n} from '../../installers/index.js';\nimport { MarketplaceCache } from '../../marketplaces/index.js';\nimport { output } from '../../output/logger.js';\nimport { RushError } from '../../util/errors.js';\nimport {\n applyUninstallToRegistry,\n DEFAULT_TARGETS,\n defaultInstallerFactory,\n parsePluginRef,\n parseTargets,\n} from './common.js';\n\nexport interface UninstallCommandInput {\n ref: string;\n targetRaw?: string;\n dryRun?: boolean;\n targetExplicit?: boolean;\n\n home?: string;\n cache?: MarketplaceCache;\n registry?: RushRegistryStore;\n installerFactory?: (\n target: InstallTarget,\n opts: { home?: string }\n ) => PluginInstaller;\n}\n\nexport interface UninstallCommandResult {\n ref: PluginRef;\n results: UninstallResult[];\n targetExplicit: boolean;\n dryRun: boolean;\n exitCode: 0 | 1 | 2;\n registryError?: string;\n}\n\nexport async function runUninstall(\n input: UninstallCommandInput\n): Promise<UninstallCommandResult> {\n const targets = parseTargets(input.targetRaw);\n const targetExplicit = input.targetExplicit ?? input.targetRaw !== undefined;\n const dryRun = input.dryRun === true;\n\n // 1. 解析 plugin ref —— 先在 registry 找;找不到再走 cache 反查(与 install 对称)\n const cache =\n input.cache ??\n new MarketplaceCache(input.home !== undefined ? { home: input.home } : {});\n const ref = await parsePluginRef(input.ref, cache).catch(async (err) => {\n // uninstall 对\"marketplace 没 cache\"的场景更宽容——用户可能把 marketplace\n // 已经 remove 了但插件装在三家 IDE 里残留。只要 registry 能找到就接受。\n if (err instanceof RushError && err.code === 'PLUGIN_NOT_FOUND') {\n const reg =\n input.registry ??\n (await RushRegistryStore.load(\n input.home !== undefined ? { home: input.home } : {}\n ));\n const all = reg.list();\n const matches = all.filter(([r]) => r.name === input.ref.trim());\n if (matches.length === 1) {\n const only = matches[0];\n if (only) return only[0];\n }\n if (matches.length > 1) {\n const names = matches.map(([r]) => `${r.name}@${r.marketplace}`);\n throw new RushError(\n `Plugin '${input.ref}' is ambiguous across registry: ${names.join(', ')}. ` +\n `Specify '<name>@<marketplace>' explicitly.`,\n { candidates: names },\n 'AMBIGUOUS_PLUGIN',\n 2\n );\n }\n }\n throw err;\n });\n\n // 2. 每个 target 串行 uninstall\n const results: UninstallResult[] = [];\n for (const t of targets) {\n const factory =\n input.installerFactory ??\n ((target, opts) =>\n defaultInstallerFactory(target, {\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n }));\n const installer = factory(t, {\n ...(input.home !== undefined ? { home: input.home } : {}),\n });\n try {\n const result = await installer.uninstall(ref, { dryRun });\n // v1 partial 防御性收敛(同 install)——Installer 违反契约返回 partial\n // 时收敛为 failed,避免透传到 registry / 退出码聚合\n if (result.status === 'partial') {\n results.push({\n ...result,\n status: 'failed',\n errors: [\n ...(result.errors ?? []),\n `Installer '${t}' returned 'partial' which is not permitted in v1 (spec §1.4). Treating as failed.`,\n ],\n });\n } else {\n results.push(result);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n results.push({\n target: t,\n status: 'failed',\n artifacts: { files: [], mcpKeys: [] },\n errors: [message],\n });\n }\n }\n\n // 3. 非 dry-run → 把 ok 的 target 从 registry 条目清理\n let registryError: string | undefined;\n if (!dryRun) {\n try {\n const store =\n input.registry ??\n (await RushRegistryStore.load(\n input.home !== undefined ? { home: input.home } : {}\n ));\n const successTargets = results\n .filter((r) => r.status === 'ok')\n .map((r) => r.target);\n await applyUninstallToRegistry(store, ref, successTargets);\n } catch (err) {\n registryError = formatRegistryError(err);\n }\n }\n\n const hasFailed = results.some((r) => r.status === 'failed');\n const exitCode: 0 | 1 | 2 = hasFailed ? 1 : 0;\n\n return {\n ref,\n results,\n targetExplicit,\n dryRun,\n exitCode,\n ...(registryError !== undefined ? { registryError } : {}),\n };\n}\n\nfunction formatRegistryError(err: unknown): string {\n if (err instanceof RushRegistryConflictError) {\n return `rush-ai registry write conflict: ${err.message}`;\n }\n if (err instanceof RushRegistryCorruptError) {\n return `rush-ai registry is corrupt: ${err.message}`;\n }\n if (err instanceof RushRegistrySchemaMismatchError) {\n return `rush-ai registry schema mismatch: ${err.message}`;\n }\n if (err instanceof Error) {\n return `rush-ai registry write failed: ${err.message}`;\n }\n return `rush-ai registry write failed: ${String(err)}`;\n}\n\n// ---------------------------------------------------------------------------\n// Commander wrapper\n// ---------------------------------------------------------------------------\n\nexport function registerUninstallCommand(group: Command, _root: Command): void {\n group\n .command('uninstall')\n .alias('remove')\n .description('Uninstall a plugin from one or more IDEs')\n .argument('<ref>', 'plugin reference: <name> or <name>@<marketplace>')\n .option(\n '--target <list>',\n `comma-separated targets (default: ${[...DEFAULT_TARGETS].join(',')})`\n )\n .option(\n '--dry-run',\n \"don't touch disk; print the files that would be removed\"\n )\n .action(\n async (ref: string, opts: { target?: string; dryRun?: boolean }) => {\n const result = await runUninstall({\n ref,\n ...(opts.target !== undefined ? { targetRaw: opts.target } : {}),\n targetExplicit: opts.target !== undefined,\n ...(opts.dryRun ? { dryRun: true } : {}),\n });\n\n printUninstallSummary(result);\n\n if (result.exitCode !== 0) {\n throw new RushError(\n `Uninstall completed with failures (see output above)`,\n {},\n 'UNINSTALL_PARTIAL_FAILED',\n result.exitCode\n );\n }\n }\n );\n}\n\nexport function printUninstallSummary(result: UninstallCommandResult): void {\n const { ref, results, targetExplicit, dryRun } = result;\n const tag = dryRun ? ' (dry-run)' : '';\n output.log(`Uninstalling ${ref.name}@${ref.marketplace}...${tag}`);\n output.newline();\n\n for (const r of results) {\n const label = targetDisplayName(r.target);\n if (r.status === 'skipped') {\n if (targetExplicit) {\n output.dim(`ⓘ Skipped: ${label} (IDE not installed)`);\n }\n continue;\n }\n if (r.status === 'failed') {\n const firstError = r.errors?.[0] ?? 'unknown error';\n output.error(`✗ Failed to uninstall from ${label}: ${firstError}`);\n continue;\n }\n // ok\n const details = formatArtifactsDetail(r);\n const line = details\n ? `✓ Removed from ${label} (${details})`\n : `✓ Removed from ${label}`;\n output.success(line);\n\n if (dryRun && r.artifacts.files.length > 0) {\n for (const f of r.artifacts.files) {\n output.dim(` would remove: ${f}`);\n }\n }\n }\n\n if (result.registryError) {\n output.newline();\n output.warn(result.registryError);\n output.dim(\n ' The disk has been cleaned; rush-ai registry may be stale. Retry to reconcile.'\n );\n }\n}\n\nfunction formatArtifactsDetail(r: UninstallResult): string {\n const parts: string[] = [];\n if (r.artifacts.files.length > 0) {\n parts.push(\n `${r.artifacts.files.length} file${r.artifacts.files.length > 1 ? 's' : ''}`\n );\n }\n if (r.artifacts.mcpKeys.length > 0) {\n parts.push(\n `${r.artifacts.mcpKeys.length} MCP entr${r.artifacts.mcpKeys.length > 1 ? 'ies' : 'y'}`\n );\n }\n return parts.join(', ');\n}\n\nfunction targetDisplayName(t: InstallTarget): string {\n switch (t) {\n case 'claude-code':\n return 'Claude Code';\n case 'codex':\n return 'Codex';\n case 'cursor':\n return 'Cursor';\n default: {\n const _exhaustive: never = t;\n void _exhaustive;\n return String(t);\n }\n }\n}\n","/**\n * `rush-ai plugin update [<name>] [--target ...]`(task-10)。\n *\n * Source of truth: `specs/cli-commands.md` §2.4\n *\n * 语义:\n * - 省略 `<name>`:检查所有已装插件的 marketplace 是否有新版本,有则 `install --force`\n * - 带 `<name>`:只更这一个\n * - 等价于:对每个目标 plugin 先 `marketplace update <mkt>`(刷 cache),\n * 再 `resolvePlugin` 拿最新 version;若 registry 里的 version 与最新不同或 force\n * 模式 → 走 `install --force`(复用 `runInstall`)。同版本 → 跳过。\n *\n * 由于 update = \"marketplace refresh + plugin install --force\",实现上直接复用\n * `runInstall`:\n * - 先对需要刷新的 marketplaces 走 `cache.update(name)`(directory: source → no-op)\n * - 再逐个 plugin 调 `runInstall({ force: true })`\n *\n * 输出示例:\n * ```\n * Updating 3 plugin(s)...\n *\n * → rush@rush-marketplace: 0.2.0 → 0.2.1\n * ✓ Installed to Claude Code ...\n * → yuanli-infra@rush-marketplace: up to date (1.0.0)\n * → octo@rush-marketplace: 0.5.0 → 0.6.0\n * ✓ Installed to ...\n * ```\n *\n * 退出码:\n * - 0 全部成功或无需更新\n * - 1 任一 plugin 的 install 失败\n * - 2 ref 解析或 marketplace 缺失\n */\n\nimport type { Command } from 'commander';\nimport {\n type InstallTarget,\n type PluginRef,\n RushRegistryStore,\n} from '../../installers/index.js';\nimport {\n MarketplaceCache,\n MarketplaceNotFoundError,\n NotImplementedError,\n type ResolvedMarketplace,\n} from '../../marketplaces/index.js';\nimport { output } from '../../output/logger.js';\nimport {\n PluginResolverError,\n resolvePlugin as realResolvePlugin,\n} from '../../plugins/index.js';\nimport { RushError } from '../../util/errors.js';\nimport { DEFAULT_TARGETS, parsePluginRef, parseTargets } from './common.js';\nimport {\n type InstallCommandInput,\n type InstallCommandResult,\n printInstallSummary,\n runInstall,\n} from './install.js';\n\nexport interface UpdateCommandInput {\n /** 限定 plugin;undefined = 更全部 */\n name?: string;\n targetRaw?: string;\n targetExplicit?: boolean;\n /** `--dry-run`:不 install,只展示 \"哪些插件有新版本 + 会刷哪些 cache\" */\n dryRun?: boolean;\n\n home?: string;\n cache?: MarketplaceCache;\n registry?: RushRegistryStore;\n\n /** 测试/advanced:覆盖 install runner */\n installRunner?: (input: InstallCommandInput) => Promise<InstallCommandResult>;\n /** 测试/advanced:覆盖 marketplace cache update 行为 */\n cacheUpdate?: (name: string) => Promise<void>;\n /** 测试/advanced:覆盖 resolvePlugin */\n resolvePluginFn?: typeof realResolvePlugin;\n}\n\nexport interface UpdatePluginResult {\n ref: PluginRef;\n /** registry 里的旧版本 */\n fromVersion: string;\n /** marketplace 最新版本(resolvePlugin 产出) */\n toVersion: string;\n /** 无需更新(版本相同) */\n upToDate: boolean;\n /** 关联的 install 执行结果(undefined = up-to-date 或 dry-run 跳过) */\n installResult?: InstallCommandResult;\n /** 处理过程中捕获的错误 */\n error?: string;\n /**\n * 错误类别(用于聚合 exitCode):\n * - `resolve`:marketplace 不存在 / resolvePlugin 失败 → 贡献 exitCode=2\n * - `install`:install runner 返回非零 exitCode → 贡献 exitCode=1\n */\n errorKind?: 'resolve' | 'install';\n}\n\nexport interface UpdateCommandResult {\n targets: InstallTarget[];\n dryRun: boolean;\n plugins: UpdatePluginResult[];\n exitCode: 0 | 1 | 2;\n}\n\nexport async function runUpdate(\n input: UpdateCommandInput = {}\n): Promise<UpdateCommandResult> {\n const targets = parseTargets(input.targetRaw);\n const targetExplicit = input.targetExplicit ?? input.targetRaw !== undefined;\n const dryRun = input.dryRun === true;\n\n const cache =\n input.cache ??\n new MarketplaceCache(input.home !== undefined ? { home: input.home } : {});\n const registry =\n input.registry ??\n (await RushRegistryStore.load(\n input.home !== undefined ? { home: input.home } : {}\n ));\n\n // 1. 确定要更新的 plugin 列表\n const refs: PluginRef[] = await collectRefs(input, cache, registry);\n if (refs.length === 0) {\n return {\n targets,\n dryRun,\n plugins: [],\n exitCode: 0,\n };\n }\n\n // 2. 按 marketplace 分组,先刷 cache(同一个 marketplace 只刷一次)\n const uniqueMkts = new Set(refs.map((r) => r.marketplace));\n const cacheUpdate =\n input.cacheUpdate ??\n ((name: string) => cache.update(name).then(() => undefined));\n for (const mktName of uniqueMkts) {\n if (dryRun) continue;\n try {\n await cacheUpdate(mktName);\n } catch (err) {\n if (err instanceof NotImplementedError) continue; // Phase 1.5/2 source 忽略\n if (err instanceof MarketplaceNotFoundError) continue; // has() 已挡,兜底\n // 其他错误挂一个 plugin → 让后续 resolve 阶段报错,下面继续\n }\n }\n\n // 3. 逐个 plugin:resolve 拿最新 version → 对比 registry → 必要时 install --force\n const resolveFn = input.resolvePluginFn ?? realResolvePlugin;\n const installRunner = input.installRunner ?? runInstall;\n const plugins: UpdatePluginResult[] = [];\n\n for (const ref of refs) {\n const fromVersion = registry.get(ref)?.sourceVersion ?? 'unknown';\n let resolved: ResolvedMarketplace;\n try {\n resolved = await cache.get(ref.marketplace);\n } catch (err) {\n plugins.push({\n ref,\n fromVersion,\n toVersion: fromVersion,\n upToDate: false,\n error: err instanceof Error ? err.message : String(err),\n errorKind: 'resolve',\n });\n continue;\n }\n let toVersion: string;\n try {\n const plugin = await resolveFn(ref, resolved);\n toVersion = plugin.version;\n } catch (err) {\n if (err instanceof PluginResolverError) {\n plugins.push({\n ref,\n fromVersion,\n toVersion: fromVersion,\n upToDate: false,\n error: err.message,\n errorKind: 'resolve',\n });\n continue;\n }\n throw err;\n }\n\n if (toVersion === fromVersion) {\n plugins.push({ ref, fromVersion, toVersion, upToDate: true });\n continue;\n }\n\n if (dryRun) {\n plugins.push({ ref, fromVersion, toVersion, upToDate: false });\n continue;\n }\n\n const installInput: InstallCommandInput = {\n ref: `${ref.name}@${ref.marketplace}`,\n ...(input.targetRaw !== undefined ? { targetRaw: input.targetRaw } : {}),\n targetExplicit,\n force: true,\n ...(input.home !== undefined ? { home: input.home } : {}),\n cache,\n registry,\n };\n const result = await installRunner(installInput);\n plugins.push({\n ref,\n fromVersion,\n toVersion,\n upToDate: false,\n installResult: result,\n ...(result.exitCode === 1 ? { errorKind: 'install' as const } : {}),\n });\n }\n\n // 退出码聚合规则(对齐 spec §2.1 单一规则 + 尊重 update = resolve + install 组合):\n // - 任一 resolve 失败(含 marketplace 缺失 / resolvePlugin 失败)→ 2(参数/解析类)\n // - 否则任一 install 阶段 exitCode=1 → 1\n // - 全部干净 → 0\n // resolve + install 同时失败时 resolve 优先(2 > 1),原因:CLI 调用方通常更关心\n // 参数类错误(可修),install 失败往往是可重试的。\n const hasResolveFailure = plugins.some((p) => p.errorKind === 'resolve');\n const hasInstallFailure = plugins.some((p) => p.errorKind === 'install');\n const exitCode: 0 | 1 | 2 = hasResolveFailure ? 2 : hasInstallFailure ? 1 : 0;\n\n return { targets, dryRun, plugins, exitCode };\n}\n\nasync function collectRefs(\n input: UpdateCommandInput,\n cache: MarketplaceCache,\n registry: RushRegistryStore\n): Promise<PluginRef[]> {\n if (input.name !== undefined) {\n const ref = await parsePluginRef(input.name, cache);\n return [ref];\n }\n return registry.list().map(([ref]) => ref);\n}\n\n// ---------------------------------------------------------------------------\n// Commander wrapper\n// ---------------------------------------------------------------------------\n\nexport function registerUpdateCommand(group: Command, _root: Command): void {\n group\n .command('update')\n .description(\n 'Update installed plugin(s) to the latest version available in their marketplaces'\n )\n .argument(\n '[name]',\n 'plugin name or <name>@<marketplace>; omit to update all installed plugins'\n )\n .option(\n '--target <list>',\n `comma-separated targets (default: ${[...DEFAULT_TARGETS].join(',')})`\n )\n .option(\n '--dry-run',\n 'list which plugins would be updated without touching disk'\n )\n .action(\n async (\n name: string | undefined,\n opts: { target?: string; dryRun?: boolean }\n ) => {\n const result = await runUpdate({\n ...(name !== undefined ? { name } : {}),\n ...(opts.target !== undefined ? { targetRaw: opts.target } : {}),\n targetExplicit: opts.target !== undefined,\n ...(opts.dryRun ? { dryRun: true } : {}),\n });\n\n printUpdateSummary(result);\n\n if (result.exitCode !== 0) {\n throw new RushError(\n `Update completed with failures (see output above)`,\n {},\n 'UPDATE_PARTIAL_FAILED',\n result.exitCode\n );\n }\n }\n );\n}\n\nexport function printUpdateSummary(result: UpdateCommandResult): void {\n const { plugins, dryRun } = result;\n if (plugins.length === 0) {\n output.log('(no installed plugins to update)');\n output.dim(\n \" Install one with 'rush-ai plugin install <name>[@<marketplace>]' first.\"\n );\n return;\n }\n\n const header = dryRun\n ? `Checking ${plugins.length} plugin(s) for updates (dry-run)...`\n : `Updating ${plugins.length} plugin(s)...`;\n output.log(header);\n output.newline();\n\n for (const p of plugins) {\n const refStr = `${p.ref.name}@${p.ref.marketplace}`;\n if (p.error) {\n output.error(`→ ${refStr}: ${p.error}`);\n continue;\n }\n if (p.upToDate) {\n output.dim(`→ ${refStr}: up to date (${p.toVersion})`);\n continue;\n }\n if (dryRun) {\n output.log(`→ ${refStr}: ${p.fromVersion} → ${p.toVersion} (dry-run)`);\n continue;\n }\n output.log(`→ ${refStr}: ${p.fromVersion} → ${p.toVersion}`);\n if (p.installResult) {\n printInstallSummary(p.installResult);\n }\n }\n}\n","/**\n * `rush-ai plugin` 命令组入口(task-10 重写)。\n *\n * Source of truth: `specs/cli-commands.md` §2\n *\n * 子命令:\n * - `install <name>[@<mkt>] [--target ...] [--force] [--dry-run]` 见 install.ts\n * - `uninstall <name>[@<mkt>] [--target ...] [--dry-run]` 见 uninstall.ts\n * - `list [--available] [--target ...]` 见 list.ts\n * - `update [<name>] [--target ...]` 见 update.ts\n *\n * 设计原则(与 `commands/marketplace/index.ts` 一致):\n * - 命令 handler 只做参数解析 + 调用 pure runner + 打印汇总\n * - 业务逻辑全在 pure runner 函数里(`runInstall` / `runUninstall` / `runList` /\n * `runUpdate`),便于单测直接调用\n * - 所有磁盘操作通过 `home` 参数注入(测试强制 tmp dir)\n *\n * - 本文件完全替换老的 adapter-based install/uninstall/list/status/update/doctor\n * 实现(即旧 `registerPluginCommand` 里的 `adapter.*` 调用)\n * - 老的 `adapters/` / `assets.ts` / `migration.ts` 在 task-11 才删除;本 task\n * 不再引用它们,让 task-11 成为纯 `rm` 操作\n * - `doctor.ts` / `preflight.ts` / `verify.ts` 依赖 `IdeAdapter`,本 task 不再\n * 挂载对应子命令(`plugin doctor` 暂时消失)。task-11 / 后续 task 会决定它们\n * 是一起删除还是改写成基于新 Installer API\n * 的健康检查——不属于本 task 职责\n */\n\nimport type { Command } from 'commander';\nimport { registerInstallCommand } from './install.js';\nimport { registerListCommand } from './list.js';\nimport { registerUninstallCommand } from './uninstall.js';\nimport { registerUpdateCommand } from './update.js';\n\nexport function registerPluginCommand(program: Command): void {\n const plugin = program\n .command('plugin')\n .description(\n 'Install, uninstall, list, and update plugins across Claude Code, Codex, and Cursor'\n );\n\n registerInstallCommand(plugin, program);\n registerUninstallCommand(plugin, program);\n registerListCommand(plugin, program);\n registerUpdateCommand(plugin, program);\n}\n\n// -- Re-export pure runners for integration / advanced consumers ---------------\nexport type { InstallCommandInput, InstallCommandResult } from './install.js';\nexport { printInstallSummary, runInstall } from './install.js';\nexport type {\n AvailablePluginRow,\n InstalledPluginRow,\n ListCommandInput,\n ListCommandResult,\n} from './list.js';\nexport { printListSummary, runList } from './list.js';\nexport type {\n UninstallCommandInput,\n UninstallCommandResult,\n} from './uninstall.js';\nexport { printUninstallSummary, runUninstall } from './uninstall.js';\nexport type {\n UpdateCommandInput,\n UpdateCommandResult,\n UpdatePluginResult,\n} from './update.js';\nexport { printUpdateSummary, runUpdate } from './update.js';\n","import { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport { basename, resolve } from 'node:path';\nimport type { Command } from 'commander';\nimport { output } from '../../output/logger.js';\n\nconst SKILL_DESCRIPTION =\n 'Print agent usage skills (hand-off, agent-shelf, ...) as raw markdown.';\n\nconst SKILL_HELP_AFTER = `\nExamples:\n $ npx rush-ai skill Print the skills index\n $ npx rush-ai skill hand-off Print the hand-off playbook\n $ npx rush-ai skill agent-shelf Print the agent-shelf playbook\n $ npx rush-ai skill --list List available skills\n\nDesigned to be consumed by AI agents inside IDEs (Cursor, Claude Code,\netc). Output is raw markdown on stdout — pipe it, grep it, feed it to\nyour own context.\n`;\n\n/**\n * Resolve the directory that contains the shipped skills/ markdown.\n *\n * Priority, highest first:\n * 1. `<bundle-dir>/skills/` — after `tsup` builds, index.js sits in\n * `dist/` and tsup copies `skills/` to `dist/skills/`, so the\n * sibling path wins.\n * 2. `<bundle-dir>/../skills/` — when running from source (tests,\n * ts-node, linked monorepo), the caller is roughly\n * `packages/rush-ai/src/commands/skill/`, so walking up lands on\n * the package-root `skills/`.\n * 3. `<bundle-dir>/../../../../skills/` — safety net for deeper\n * nesting if the build layout ever changes.\n */\nfunction resolveSkillsDir(): string | null {\n const baseDir = import.meta.dirname ?? __dirname;\n if (!baseDir) return null;\n\n const candidates = [\n resolve(baseDir, 'skills'),\n resolve(baseDir, '..', 'skills'),\n resolve(baseDir, '..', '..', 'skills'),\n resolve(baseDir, '..', '..', '..', 'skills'),\n resolve(baseDir, '..', '..', '..', '..', 'skills'),\n ];\n\n for (const p of candidates) {\n if (existsSync(p)) return p;\n }\n return null;\n}\n\nfunction listSkills(dir: string): string[] {\n return readdirSync(dir)\n .filter((f) => f.endsWith('.md'))\n .map((f) => basename(f, '.md'))\n .sort();\n}\n\nexport function registerSkillCommand(program: Command): void {\n program\n .command('skill [name]')\n .description(SKILL_DESCRIPTION)\n .addHelpText('after', SKILL_HELP_AFTER)\n .option('--list', 'List available skill names and exit')\n .action((name: string | undefined, opts: { list?: boolean }) => {\n const dir = resolveSkillsDir();\n\n if (!dir) {\n output.error(\n 'Could not locate the skills/ directory. This is a packaging bug — please file an issue.'\n );\n process.exit(2);\n return;\n }\n\n const available = listSkills(dir);\n\n if (opts.list) {\n for (const s of available) output.log(s);\n return;\n }\n\n // Default target is the README (the index).\n const target = name ?? 'README';\n const file = resolve(dir, `${target}.md`);\n\n if (!existsSync(file)) {\n output.error(`Unknown skill \"${target}\".`);\n output.dim(`Available: ${available.join(', ') || '(none)'}`);\n process.exit(1);\n return;\n }\n\n // Raw markdown to stdout — the whole point is to be agent-consumable.\n process.stdout.write(readFileSync(file, 'utf-8'));\n });\n}\n","import { createWriteStream } from 'node:fs';\nimport { mkdir, readFile, stat } from 'node:fs/promises';\nimport path from 'node:path';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\nimport type { Command } from 'commander';\nimport { colorizeDiff, containsDiff } from '../../output/diff.js';\nimport { formatOutput, resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport { isCIMode } from '../../util/ci.js';\nimport { createClient } from '../../util/client.js';\nimport { RushError, TaskFailedError } from '../../util/errors.js';\nimport { requireAuth } from '../../util/require-auth.js';\nimport { consumeSSEStreamWithReconnect } from '../../util/sse.js';\nimport { readStdinIfPiped } from '../../util/stdin.js';\n\ninterface TaskCreateResponse {\n id: string;\n status: string;\n}\n\ninterface TaskResult {\n id: string;\n name?: string;\n status:\n | 'pending'\n | 'running'\n | 'waiting_input'\n | 'completed'\n | 'failed'\n | 'cancelled';\n agent: string;\n prompt: string;\n result?: unknown;\n error?: string;\n template?: string | null;\n podImageName?: string | null;\n previewUrl?: string | null;\n gitRepoUrl?: string | null;\n deployedProductionUrl?: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\ninterface TaskListResponse {\n tasks: TaskResult[];\n total: number;\n}\n\ninterface TaskFile {\n id: string;\n filePath: string;\n fileName: string;\n ossPath?: string;\n ossUrl?: string;\n category: string;\n source: string;\n timestamp?: number;\n url?: string;\n conversationId?: string;\n}\n\ninterface TaskFilesResponse {\n files: TaskFile[];\n totalCount: number;\n}\n\ninterface ConversationInfo {\n id: string;\n title: string | null;\n model: string;\n message_count: number;\n created_at: string;\n updated_at: string;\n created_by_name: string | null;\n}\n\ninterface ConversationsResponse {\n conversations: ConversationInfo[];\n}\n\ninterface UIMessagePart {\n type: string;\n text?: string;\n toolName?: string;\n state?: string;\n reasoning?: string;\n}\n\ninterface UIMessage {\n id: string;\n role: string;\n content: string;\n parts?: UIMessagePart[];\n createdAt?: string | number;\n metadata?: Record<string, unknown>;\n}\n\ninterface MessagesResponse {\n messages: UIMessage[];\n metadata?: {\n id: string;\n title?: string;\n createdAt?: string;\n updatedAt?: string;\n messageCount?: number;\n };\n}\n\nconst MAX_TEXT_LENGTH = 500;\n\n/**\n * Truncate text to MAX_TEXT_LENGTH characters, appending \"...\" if truncated.\n */\nfunction truncateText(text: string): string {\n if (text.length <= MAX_TEXT_LENGTH) return text;\n return text.slice(0, MAX_TEXT_LENGTH) + '...';\n}\n\n/**\n * Extract a short summary of tool invocations from message parts.\n * Returns something like: \"Write(src/App.tsx), Bash(npm run build)\"\n */\nfunction summarizeTools(parts: UIMessagePart[]): string {\n const toolParts = parts.filter(\n (p) => p.type === 'tool-invocation' || p.type.startsWith('tool-')\n );\n if (toolParts.length === 0) return '';\n return toolParts\n .map((p) => {\n if (p.toolName) return p.toolName;\n if (p.type.startsWith('tool-')) return p.type.slice(5);\n return 'unknown';\n })\n .join(', ');\n}\n\n/**\n * Format a timestamp for display.\n */\nfunction formatTimestamp(ts: string | number | undefined): string {\n if (!ts) return '';\n const d = new Date(ts);\n return d.toLocaleString();\n}\n\n/**\n * Render messages in human-readable format.\n */\nfunction renderMessages(\n messages: UIMessage[],\n conversationId: string,\n compact: boolean\n): void {\n output.log(\n output.bold(`Conversation ${conversationId} — ${messages.length} messages`)\n );\n output.newline();\n\n for (const msg of messages) {\n const ts = formatTimestamp(msg.createdAt);\n output.log(`[${msg.role}] ${ts}`);\n\n // Collect text content\n const textParts = (msg.parts ?? []).filter(\n (p) => p.type === 'text' && p.text\n );\n if (textParts.length > 0) {\n for (const tp of textParts) {\n output.log(` ${truncateText(tp.text!)}`);\n }\n } else if (msg.content) {\n output.log(` ${truncateText(msg.content)}`);\n }\n\n // Show tool summary for assistant messages (unless compact)\n if (!compact && msg.role === 'assistant' && msg.parts) {\n const toolSummary = summarizeTools(msg.parts);\n if (toolSummary) {\n output.dim(` Tools: ${toolSummary}`);\n }\n }\n\n output.newline();\n }\n}\n\nconst VALID_CATEGORIES = ['code', 'image', 'document', 'other'];\n\n/**\n * Sanitize a file path to prevent path traversal attacks.\n * Returns null if the path is unsafe.\n */\nfunction sanitizeFilePath(filePath: string, outputDir: string): string | null {\n // Strip leading slashes and drive letters\n let stripped = filePath.replace(/^[/\\\\]+/, '').replace(/^[a-zA-Z]:/, '');\n // Remove any .. components\n stripped = stripped\n .split('/')\n .filter((seg) => seg !== '..' && seg !== '.')\n .join('/');\n\n if (!stripped) return null;\n\n const resolvedOutput = path.resolve(outputDir);\n const target = path.resolve(resolvedOutput, stripped);\n const rel = path.relative(resolvedOutput, target);\n\n // Reject if relative path escapes the output directory\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\n return null;\n }\n\n return target;\n}\n\n/**\n * Download a file via the API proxy (handles private OSS buckets).\n * Falls back to direct URL fetch if ossPath is not available.\n */\nasync function downloadFile(\n file: { ossPath?: string; ossUrl?: string },\n destPath: string,\n client: ReturnType<typeof createClient>\n): Promise<void> {\n let response: Response | null = null;\n\n if (file.ossPath) {\n try {\n const proxyResponse = await client.fetchRaw(\n `/api/files/oss-preview?action=file&path=${encodeURIComponent(file.ossPath)}`\n );\n if (proxyResponse.ok) {\n response = proxyResponse;\n }\n } catch {\n // Proxy failed, will fallback to ossUrl\n }\n }\n\n if (!response && file.ossUrl) {\n response = await fetch(file.ossUrl);\n }\n\n if (!response) {\n throw new Error('No download path available');\n }\n\n if (!response.ok) {\n throw new Error(\n `Download failed: ${response.status} ${response.statusText}`\n );\n }\n if (!response.body) {\n throw new Error('No response body');\n }\n\n await mkdir(path.dirname(destPath), { recursive: true });\n\n const nodeStream = Readable.fromWeb(\n response.body as import('node:stream/web').ReadableStream\n );\n const fileStream = createWriteStream(destPath);\n await pipeline(nodeStream, fileStream);\n}\n\n/**\n * Build a file category summary string, e.g. \"8 code, 2 image, 2 document\"\n */\nfunction buildCategorySummary(files: TaskFile[]): string {\n const counts: Record<string, number> = {};\n for (const f of files) {\n counts[f.category] = (counts[f.category] || 0) + 1;\n }\n return Object.entries(counts)\n .map(([cat, count]) => `${count} ${cat}`)\n .join(', ');\n}\n\nimport { registerDeploySubcommand } from './deploy.js';\nimport { registerDomainSubcommand } from './domain.js';\nimport { registerLinkSubcommand } from './link.js';\nimport { registerPushSubcommand } from './push.js';\nimport { registerVersionsSubcommand } from './versions.js';\n\nconst TASK_HELP_AFTER = `\nWhen to use:\n Call \\`task create\\` whenever the next unit of work belongs on the Rush\n platform instead of your local machine — a specialist Rush agent runs\n it, and you get a task id to track / iterate on.\n\nTypical flows:\n # Hand-off from an IDE conversation → Rush agent builds, you iterate\n $ rush-ai task create -a web-builder -p \"<prompt synthesized from context>\" --json\n $ rush-ai task status <id> --json # includes previewUrl / gitRepoUrl\n $ rush-ai task send <id> -p \"<follow-up>\" # same task, keep iterating\n\n # Quick one-shot with the default agent (\\`rush\\`)\n $ rush-ai task create -a rush -p \"Summarize the latest release notes\"\n\nFor agents: run \\`rush-ai skill hand-off\\` for the full playbook.\n`;\n\nexport function registerTaskCommand(program: Command): void {\n const task = program\n .command('task')\n .description('Create and manage tasks')\n .addHelpText('after', TASK_HELP_AFTER);\n\n registerPushSubcommand(task, program);\n // task link — link a local project to Rush (pod + optional DB / OSS) (J-3, M3-3).\n // Formerly `task init`; the old name remains as a deprecated alias until 0.13.0 (issue #1150).\n registerLinkSubcommand(task, program);\n // task deploy <id> — web-builder publish flow (agent-A task-3)\n registerDeploySubcommand(task, program);\n // task versions <id> — list buildable versions (agent-A task-4)\n registerVersionsSubcommand(task, program);\n // task domain check <prefix> --task <id> — domain prefix preflight (agent-A task-5)\n registerDomainSubcommand(task, program);\n\n task\n .command('create')\n .description('Create a task asynchronously')\n .option(\n '-a, --agent <name>',\n 'Agent name to execute the task (defaults to `rush`)',\n 'rush'\n )\n .option('-p, --prompt <text>', 'Task prompt')\n .option('--skills <skills>', 'Comma-separated skills to add', commaSplit)\n .option('--mcp <servers>', 'Comma-separated MCP servers to add', commaSplit)\n .action(\n async (options: {\n agent: string;\n prompt?: string;\n skills?: string[];\n mcp?: string[];\n }) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n const prompt = options.prompt ?? (await readStdinIfPiped());\n if (!prompt) {\n throw new RushError(\n 'No prompt provided. Use --prompt or pipe via stdin.'\n );\n }\n\n const { data } = await client.post<TaskCreateResponse>('/api/tasks', {\n agent: options.agent,\n prompt,\n skills: options.skills,\n mcpServers: options.mcp,\n });\n\n if (format === 'json') {\n output.log(JSON.stringify(data, null, 2));\n } else {\n output.success(`Task created: ${data.id}`);\n output.dim(`Status: ${data.status}`);\n output.dim(\n `Run \\`rush-ai task status ${data.id}\\` to check progress.`\n );\n }\n }\n );\n\n task\n .command('send')\n .description('Send a message to an existing task')\n .argument('<id>', 'Task ID (project ID)')\n .option('-p, --prompt <text>', 'Message to send')\n .option('--skills <skills>', 'Comma-separated skills to add', commaSplit)\n .option('--mcp <servers>', 'Comma-separated MCP servers to add', commaSplit)\n .option(\n '--file <path>',\n 'Attach a local file as context for the agent (repeatable)',\n collectFile,\n []\n )\n .action(\n async (\n id: string,\n options: {\n prompt?: string;\n skills?: string[];\n mcp?: string[];\n file?: string[];\n }\n ) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n const prompt = options.prompt ?? (await readStdinIfPiped());\n if (!prompt) {\n throw new RushError(\n 'No prompt provided. Use --prompt or pipe via stdin.'\n );\n }\n\n // Upload attachments before the send request so the task starts with\n // file parts already in place. A small bounded concurrency keeps the\n // happy path fast for users attaching many small files while still\n // surfacing the first failure quickly.\n const files: UploadedAttachment[] = [];\n const pendingPaths = options.file ?? [];\n const UPLOAD_CONCURRENCY = 4;\n for (let i = 0; i < pendingPaths.length; i += UPLOAD_CONCURRENCY) {\n const batch = pendingPaths.slice(i, i + UPLOAD_CONCURRENCY);\n if (format !== 'json') {\n for (const p of batch) output.dim(`Uploading ${p}...`);\n }\n const uploaded = await Promise.all(\n batch.map((p) => uploadFileForTask(client, p))\n );\n files.push(...uploaded);\n }\n\n const { data } = await client.post<TaskCreateResponse>(\n `/api/tasks/${encodeURIComponent(id)}/send`,\n {\n prompt,\n skills: options.skills,\n mcpServers: options.mcp,\n ...(files.length > 0 ? { files } : {}),\n }\n );\n\n if (format === 'json') {\n output.log(JSON.stringify(data, null, 2));\n } else {\n output.success(`Message sent to task: ${data.id}`);\n output.dim(`Status: ${data.status}`);\n if (files.length > 0) {\n output.dim(`Attached ${files.length} file(s).`);\n }\n output.dim(\n `Run \\`rush-ai task watch ${data.id}\\` to follow progress.`\n );\n }\n }\n );\n\n task\n .command('status')\n .description('Check task status')\n .argument('<id>', 'Task ID')\n .action(async (id: string) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n const { data } = await client.get<TaskResult>(\n `/api/tasks/${encodeURIComponent(id)}`\n );\n\n if (format === 'json') {\n output.log(JSON.stringify(data, null, 2));\n } else {\n output.log(output.bold(`Task ${data.id}`));\n output.log(` Agent: ${data.agent}`);\n output.log(` Status: ${data.status}`);\n if (data.template) {\n output.log(` Template: ${data.template}`);\n }\n if (data.podImageName) {\n output.log(` Image: ${data.podImageName}`);\n }\n output.log(` Created: ${new Date(data.createdAt).toLocaleString()}`);\n output.log(` Updated: ${new Date(data.updatedAt).toLocaleString()}`);\n if (data.previewUrl) {\n output.log(` Preview: ${data.previewUrl}`);\n }\n if (data.gitRepoUrl) {\n output.log(` Git: ${data.gitRepoUrl}`);\n }\n if (data.deployedProductionUrl) {\n output.log(` Production: ${data.deployedProductionUrl}`);\n }\n if (data.error) {\n output.error(` Error: ${data.error}`);\n }\n }\n\n // In CI mode, a failed task should produce a non-zero exit code\n if (isCIMode(program.opts()) && data.status === 'failed') {\n throw new TaskFailedError(\n `Task ${id} failed: ${data.error ?? 'unknown error'}`,\n { taskId: id, status: data.status }\n );\n }\n });\n\n task\n .command('result')\n .description('Get task result')\n .argument('<id>', 'Task ID')\n .action(async (id: string) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n const { data } = await client.get<{ result: unknown }>(\n `/api/tasks/${encodeURIComponent(id)}/result`\n );\n\n // Try to fetch file summary (non-blocking)\n let filesSummary: { totalCount: number; summary: string } | null = null;\n try {\n const { data: filesData } = await client.get<TaskFilesResponse>(\n `/api/chat/${encodeURIComponent(id)}/files`\n );\n if (filesData.totalCount > 0) {\n filesSummary = {\n totalCount: filesData.totalCount,\n summary: buildCategorySummary(filesData.files),\n };\n }\n } catch {\n // Silently degrade - file summary is an enhancement\n }\n\n if (format === 'json') {\n output.log(\n JSON.stringify(\n { ...data, filesSummary: filesSummary ?? undefined },\n null,\n 2\n )\n );\n } else {\n if (typeof data.result === 'string') {\n output.log(\n containsDiff(data.result) ? colorizeDiff(data.result) : data.result\n );\n } else {\n output.log(JSON.stringify(data.result, null, 2));\n }\n\n if (filesSummary) {\n output.newline();\n output.dim(\n `Files: ${filesSummary.totalCount} files (${filesSummary.summary})`\n );\n output.dim(`Run \\`rush-ai task files ${id}\\` to view details.`);\n }\n }\n });\n\n task\n .command('files')\n .description('List and download task artifact files')\n .argument('<id>', 'Task ID')\n .option('--download <path>', 'Download a specific file by its path')\n .option('--download-all', 'Download all files')\n .option('-o, --output <dir>', 'Output directory for downloads')\n .option(\n '-c, --category <type>',\n 'Filter by category: code, image, document, other'\n )\n .action(\n async (\n id: string,\n options: {\n download?: string;\n downloadAll?: boolean;\n output?: string;\n category?: string;\n }\n ) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n const { data } = await client.get<TaskFilesResponse>(\n `/api/chat/${encodeURIComponent(id)}/files`\n );\n\n let files = data.files;\n\n // Filter by category\n if (options.category && VALID_CATEGORIES.includes(options.category)) {\n files = files.filter((f) => f.category === options.category);\n } else if (options.category) {\n output.warn(\n `Unknown category \"${options.category}\". Showing all files.`\n );\n }\n\n // Single file download\n if (options.download) {\n const file = files.find(\n (f) =>\n f.filePath === options.download || f.fileName === options.download\n );\n if (!file) {\n output.error(`File not found: ${options.download}`);\n process.exit(1);\n }\n if (!file.ossPath && !file.ossUrl) {\n output.error(`File \"${file.fileName}\" has no download path.`);\n process.exit(1);\n }\n\n const outputDir = options.output || '.';\n const destPath = sanitizeFilePath(file.filePath, outputDir);\n if (!destPath) {\n output.error(`Unsafe file path: ${file.filePath}`);\n process.exit(1);\n }\n\n await downloadFile(file, destPath, client);\n\n if (format === 'json') {\n output.log(\n JSON.stringify(\n { downloaded: file.filePath, dest: destPath },\n null,\n 2\n )\n );\n } else {\n output.success(`Downloaded: ${file.filePath} -> ${destPath}`);\n }\n return;\n }\n\n // Batch download\n if (options.downloadAll) {\n const outputDir = options.output || `task-files-${id.slice(0, 8)}`;\n let downloaded = 0;\n let skipped = 0;\n let failed = 0;\n\n for (const file of files) {\n if (!file.ossPath && !file.ossUrl) {\n skipped++;\n if (format !== 'json') {\n output.dim(` Skipped (no download path): ${file.filePath}`);\n }\n continue;\n }\n\n const destPath = sanitizeFilePath(file.filePath, outputDir);\n if (!destPath) {\n skipped++;\n if (format !== 'json') {\n output.warn(` Skipped (unsafe path): ${file.filePath}`);\n }\n continue;\n }\n\n try {\n await downloadFile(file, destPath, client);\n downloaded++;\n if (format !== 'json') {\n output.dim(` Downloaded: ${file.filePath}`);\n }\n } catch {\n failed++;\n if (format !== 'json') {\n output.warn(` Failed: ${file.filePath}`);\n }\n }\n }\n\n if (format === 'json') {\n output.log(\n JSON.stringify(\n { outputDir, downloaded, skipped, failed, total: files.length },\n null,\n 2\n )\n );\n } else {\n output.newline();\n output.success(\n `Downloaded ${downloaded}/${files.length} files to ${outputDir}`\n );\n if (skipped > 0) {\n output.dim(\n ` ${skipped} skipped (no download URL or unsafe path)`\n );\n }\n if (failed > 0) {\n output.warn(` ${failed} failed`);\n }\n }\n return;\n }\n\n // Default: list files\n if (format === 'json') {\n output.log(\n JSON.stringify({ files, totalCount: files.length }, null, 2)\n );\n return;\n }\n\n if (files.length === 0) {\n output.info('No files found.');\n return;\n }\n\n output.log(output.bold(`Files (${files.length} total):`));\n output.newline();\n\n const rows = files.map((f) => ({\n Name: f.fileName,\n Category: f.category,\n Source: f.source,\n Path: f.filePath,\n }));\n\n output.log(formatOutput(rows, format));\n }\n );\n\n task\n .command('messages')\n .description('View conversation messages for a task')\n .argument('<id>', 'Task ID (project ID)')\n .option('-c, --conversation <id>', 'Specific conversation ID')\n .option('--last <n>', 'Show only last N messages')\n .option('--role <role>', 'Filter by role (user, assistant, system)')\n .option('--compact', 'Text-only output, no tool details or reasoning')\n .action(\n async (\n id: string,\n options: {\n conversation?: string;\n last?: string;\n role?: string;\n compact?: boolean;\n }\n ) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n let conversationId = options.conversation;\n\n // If no conversation ID provided, fetch the latest one\n if (!conversationId) {\n const { data: convData } = await client.get<ConversationsResponse>(\n `/api/chat/${encodeURIComponent(id)}/conversations`\n );\n\n if (!convData.conversations || convData.conversations.length === 0) {\n if (format === 'json') {\n output.log(\n JSON.stringify({ messages: [], conversationId: null }, null, 2)\n );\n } else {\n output.info('No conversations found for this task.');\n }\n return;\n }\n\n conversationId = convData.conversations[0].id;\n }\n\n const { data } = await client.get<MessagesResponse>(\n `/api/chat/${encodeURIComponent(id)}/conversations/${encodeURIComponent(conversationId)}`\n );\n\n let messages = data.messages ?? [];\n\n if (messages.length === 0) {\n if (format === 'json') {\n output.log(\n JSON.stringify({ messages: [], conversationId }, null, 2)\n );\n } else {\n output.info('No messages in this conversation.');\n }\n return;\n }\n\n // Filter by role\n if (options.role) {\n messages = messages.filter((m) => m.role === options.role);\n }\n\n // Limit to last N messages\n if (options.last) {\n const n = parseInt(options.last, 10);\n if (!isNaN(n) && n > 0) {\n messages = messages.slice(-n);\n }\n }\n\n if (format === 'json') {\n output.log(\n JSON.stringify(\n { messages, conversationId, metadata: data.metadata },\n null,\n 2\n )\n );\n } else {\n renderMessages(messages, conversationId, !!options.compact);\n }\n }\n );\n\n task\n .command('list')\n .alias('ls')\n .description('List tasks')\n .option('-l, --limit <limit>', 'Maximum number of tasks', '50')\n .option('-s, --status <status>', 'Filter by status')\n .action(async (options: { limit?: string; status?: string }) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n const params = new URLSearchParams();\n if (options.limit) params.set('limit', options.limit);\n if (options.status) params.set('status', options.status);\n\n const { data } = await client.get<TaskListResponse>(\n `/api/tasks?${params.toString()}`\n );\n\n if (format === 'json') {\n output.log(JSON.stringify(data, null, 2));\n return;\n }\n\n if (data.tasks.length === 0) {\n output.info('No tasks found.');\n return;\n }\n\n output.log(output.bold(`Tasks (${data.total} total):`));\n output.newline();\n\n const rows = data.tasks.map((t) => ({\n ID: t.id,\n Name: t.name ?? '',\n Agent: t.agent,\n Status: t.status,\n Created: new Date(t.createdAt).toLocaleString(),\n }));\n\n output.log(formatOutput(rows, format));\n });\n\n task\n .command('watch')\n .description('Watch task execution in real-time')\n .argument('<id>', 'Task ID')\n .option(\n '--since <iso>',\n 'Replay events from this ISO timestamp (default: only new events after the latest user turn)'\n )\n .action(async (id: string, opts: { since?: string }) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n if (opts.since && Number.isNaN(new Date(opts.since).getTime())) {\n throw new RushError(\n `Invalid --since value: \"${opts.since}\" (expected ISO 8601 timestamp)`\n );\n }\n\n if (format !== 'json') {\n output.log(output.bold(`Watching task ${id}...`));\n output.newline();\n }\n\n // waiting_input isn't a terminal status on the server (the stream\n // stays open while the agent is blocked), but from the CLI's POV\n // there's nothing more to watch — the user must answer in the web UI\n // before new events will flow. Treat it as an exit point here.\n const TERMINAL_STATUSES = [\n 'completed',\n 'failed',\n 'cancelled',\n 'waiting_input',\n ];\n\n const eventsPath = opts.since\n ? `/api/tasks/${encodeURIComponent(id)}/events?since=${encodeURIComponent(opts.since)}`\n : `/api/tasks/${encodeURIComponent(id)}/events`;\n\n const connectFn = async () => {\n const response = await client.fetchRaw(eventsPath, { method: 'GET' });\n if (!response.body) {\n throw new Error('No response body for SSE');\n }\n return response.body;\n };\n\n let finalStatus = '';\n\n await consumeSSEStreamWithReconnect(\n connectFn,\n (event) => {\n if (format === 'json') {\n output.log(JSON.stringify(event));\n } else {\n renderTaskEvent(event);\n }\n if (event.type === 'status') {\n finalStatus = event.data;\n }\n },\n {\n isTerminal: (event) =>\n event.type === 'status' && TERMINAL_STATUSES.includes(event.data),\n onReconnect: (attempt) => {\n if (format !== 'json') {\n output.dim(\n `\\u27F3 SSE connection lost. Reconnecting (${attempt}/5)...`\n );\n }\n },\n }\n );\n\n if (format !== 'json') {\n output.newline();\n if (finalStatus === 'completed') {\n output.success('Task completed.');\n } else if (finalStatus === 'failed') {\n output.error('Task failed.');\n } else if (finalStatus === 'cancelled') {\n output.info('Task cancelled.');\n } else if (finalStatus === 'waiting_input') {\n // watch 已经提前退出(waiting_input 不是终态,但客户端超时/\n // 主动中断后最后看到的状态就是它)。提示用户去 UI 回答或\n // 重新 watch。\n output.info(\n 'Agent is waiting for user input. Answer in the web UI, then run `rush-ai task watch` again.'\n );\n }\n }\n });\n\n task\n .command('cancel')\n .description('Cancel a running task')\n .argument('<id>', 'Task ID')\n .action(async (id: string) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n await client.delete(`/api/tasks/${encodeURIComponent(id)}`);\n\n if (format === 'json') {\n output.log(JSON.stringify({ id, status: 'cancelled' }));\n } else {\n output.success(`Task ${id} cancelled.`);\n }\n });\n}\n\nfunction commaSplit(value: string): string[] {\n return value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n}\n\nfunction collectFile(value: string, previous: string[] = []): string[] {\n return [...previous, value];\n}\n\n/** Shape of the attachment list forwarded to the send API. */\ninterface UploadedAttachment {\n url: string;\n mediaType: string;\n filename: string;\n}\n\n/** Shape of /api/files/upload success response (from FileUploadResult). */\ninterface FileUploadResponse {\n success: true;\n url: string;\n ossUrl: string;\n filename: string;\n size: number;\n mediaType: string;\n}\n\nconst EXTENSION_TO_MIME: Record<string, string> = {\n // Images — kept in sync with packages/shared/src/const/file-limits.ts\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n gif: 'image/gif',\n webp: 'image/webp',\n bmp: 'image/bmp',\n svg: 'image/svg+xml',\n ico: 'image/x-icon',\n // Documents / text\n pdf: 'application/pdf',\n txt: 'text/plain',\n md: 'text/markdown',\n markdown: 'text/markdown',\n csv: 'text/csv',\n json: 'application/json',\n html: 'text/html',\n // Code (registered as octet-stream by default; server only uses the ext)\n};\n\nfunction guessMediaType(filePath: string): string {\n const ext = path.extname(filePath).slice(1).toLowerCase();\n return EXTENSION_TO_MIME[ext] ?? 'application/octet-stream';\n}\n\nasync function uploadFileForTask(\n client: ReturnType<typeof createClient>,\n localPath: string\n): Promise<UploadedAttachment> {\n const resolved = path.resolve(localPath);\n\n let fileStat: Awaited<ReturnType<typeof stat>>;\n try {\n fileStat = await stat(resolved);\n } catch {\n throw new RushError(`File not found: ${localPath}`);\n }\n if (!fileStat.isFile()) {\n throw new RushError(`Not a regular file: ${localPath}`);\n }\n\n if (typeof File === 'undefined') {\n throw new RushError(\n 'Node 20+ is required for --file attachments (global File is missing)'\n );\n }\n\n const filename = path.basename(resolved);\n const mediaType = guessMediaType(resolved);\n const buf = await readFile(resolved);\n // Node's undici File is sufficient for FormData; the backend reads\n // formData.get('file') as a File and passes it to OSS.\n const blob = new File([buf], filename, { type: mediaType });\n\n const formData = new FormData();\n formData.append('file', blob);\n\n // scope=owner tells the server to write the object at\n // {OSS_DIR}/users/<ldapId>/... where ldapId comes from the session.\n // /api/tasks/[id]/send later verifies the OSS URL path contains\n // /users/<currentUserLdap>/, so a caller cannot reference another user's\n // attachment even if they guess the URL.\n const { data } = await client.postFormData<FileUploadResponse>(\n '/api/files/upload?scope=owner',\n formData\n );\n\n return {\n url: data.ossUrl,\n mediaType: data.mediaType || mediaType,\n filename: data.filename ?? filename,\n };\n}\n\nfunction renderTaskEvent(event: { type: string; data: string }): void {\n switch (event.type) {\n case 'status':\n output.info(`Status: ${event.data}`);\n break;\n case 'content':\n process.stdout.write(event.data);\n break;\n case 'error':\n output.error(event.data || 'Unknown error');\n break;\n case 'progress':\n output.dim(`Progress: ${event.data}`);\n break;\n case 'raw':\n process.stdout.write(event.data);\n break;\n default:\n output.dim(JSON.stringify(event));\n }\n}\n","import chalk from 'chalk';\n\n/** Detect whether text contains unified diff content. */\nexport function containsDiff(text: string): boolean {\n return /^@@\\s+-\\d+/m.test(text) || /^---\\s+a\\//m.test(text);\n}\n\n/** Colorize unified diff lines. */\nexport function colorizeDiff(text: string): string {\n return text\n .split('\\n')\n .map((line) => {\n if (line.startsWith('+++') || line.startsWith('---'))\n return chalk.bold(line);\n if (line.startsWith('+')) return chalk.green(line);\n if (line.startsWith('-')) return chalk.red(line);\n if (line.startsWith('@@')) return chalk.cyan(line);\n return line;\n })\n .join('\\n');\n}\n","/**\n * Read stdin if data is being piped (non-TTY).\n * Returns null if stdin is a TTY (interactive mode).\n */\nexport async function readStdinIfPiped(): Promise<string | null> {\n if (process.stdin.isTTY) {\n return null;\n }\n\n return new Promise<string>((resolve, reject) => {\n const chunks: Buffer[] = [];\n\n process.stdin.on('data', (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n process.stdin.on('end', () => {\n resolve(Buffer.concat(chunks).toString('utf-8').trim());\n });\n\n process.stdin.on('error', reject);\n });\n}\n","import type { ApiResponse, RushClient } from './client.js';\nimport { ApiError, RushError } from './errors.js';\n\n/**\n * Mirror of `packages/shared/src/const/agent-builtins.ts` — rush-ai is a\n * standalone published npm package and cannot depend on the private\n * `@cortex/shared` workspace. Keep in sync manually.\n */\nexport const BUILTIN_AGENT_NAMES = {\n WEB_BUILDER: 'web-builder',\n SKILL_PUBLISHER: 'skill-publisher',\n RUSH: 'rush',\n} as const;\n\n/**\n * Response shape for `GET /api/tasks/:id`. Kept loose — only the fields\n * we actually consume in the deploy lifecycle are typed here.\n */\nexport interface ApiTaskResult {\n id: string;\n agent: string;\n template?: string | null;\n deployedProductionUrl?: string | null;\n [key: string]: unknown;\n}\n\nexport interface WebBuilderTask {\n /** Raw `/api/tasks/:id` response. */\n task: ApiTaskResult;\n /** Guaranteed non-empty template string. */\n template: string;\n /** `true` when template is the Next.js full-stack template (pod deploy). */\n isNextjs: boolean;\n}\n\n/**\n * HTTP client subset consumed by `resolveWebBuilderTask`. Typed as a\n * structural minimum so tests can pass a plain fake without pulling in the\n * whole `RushClient` surface.\n */\nexport interface ApiTaskClient {\n get: RushClient['get'];\n}\n\nconst NEXTJS_TEMPLATE = 'nextjs-fullstack';\n\n/**\n * Fetch a task and assert it is a deployable web-builder task.\n *\n * Single source of truth for the web-builder gate. All new deploy-lifecycle\n * commands (`task deploy`, `task versions`, `task domain check`) must go\n * through this helper; no command re-implements the four gating checks.\n *\n * Throws `RushError` on:\n * - 404 (task not found)\n * - `task.agent !== 'web-builder'`\n * - `task.template` null / empty / whitespace-only\n *\n * Other HTTP errors propagate as `ApiError` from the underlying client.\n */\nexport async function resolveWebBuilderTask(\n client: ApiTaskClient,\n id: string\n): Promise<WebBuilderTask> {\n let response: ApiResponse<ApiTaskResult>;\n try {\n response = await client.get<ApiTaskResult>(\n `/api/tasks/${encodeURIComponent(id)}`\n );\n } catch (err) {\n if (err instanceof ApiError && err.status === 404) {\n // Spec §\"Error-code convention\": no new error codes — keep the default\n // `RUSH_ERROR` and let callers branch on `meta.reason`.\n throw new RushError(`Task not found: ${id}`, {\n taskId: id,\n reason: 'task_not_found',\n });\n }\n throw err;\n }\n\n const task = response.data;\n\n if (task.agent !== BUILTIN_AGENT_NAMES.WEB_BUILDER) {\n throw new RushError(\n `Only web-builder tasks are deployable. Task uses agent \\`${task.agent}\\`.`,\n { taskId: id, agent: task.agent, reason: 'not_web_builder' }\n );\n }\n\n const template =\n typeof task.template === 'string' ? task.template.trim() : '';\n if (!template) {\n throw new RushError('Project not yet buildable — template is null.', {\n taskId: id,\n reason: 'template_missing',\n });\n }\n\n return {\n task,\n template,\n isNextjs: template === NEXTJS_TEMPLATE,\n };\n}\n\n/**\n * User-facing CLI env value. Exposed here because downstream commands\n * (`deploy`, `publish-pod-stream`) need a type to bind their `--env` option to.\n */\nexport type CliEnv = 'test' | 'production';\n\n/**\n * `POST /api/projects/:id/publish` body wire value for `environment`.\n *\n * The CLI says `production` to the user; the server expects `online`. This\n * divergence is **not a refactor** — `/publish` is one of two endpoints that\n * uses the `online` literal (the other being `/publish-pod`, which hardcodes\n * it server-side). Keep this function pure so call sites read as\n * `toPublishEnv(opts.env)` rather than re-inlining the ternary.\n */\nexport function toPublishEnv(env: CliEnv): 'test' | 'online' {\n return env === 'production' ? 'online' : 'test';\n}\n\n/**\n * `POST /api/projects/:id/deploy-version` body wire value for `environment`.\n *\n * Identity on the surface, but **intentionally exposed as a function** so\n * call sites document which endpoint they're targeting. The `publish` and\n * `deploy-version` endpoints use different vocabularies (`online` vs.\n * `production`) for the same user concept, and a silent drift here is a 400\n * in production.\n */\nexport function toDeployVersionEnv(env: CliEnv): 'test' | 'production' {\n return env;\n}\n","import type { RushClient } from './client.js';\nimport { RushError } from './errors.js';\n\n/**\n * Event shape emitted by `POST /api/projects/:id/publish-pod`.\n *\n * Note: **different key name from the generic SSE helper in `util/sse.ts`**.\n * The publish-pod endpoint sends `{event, message, data?}` where `event` is\n * the event type, not `type`. The shared SSE parser reads `parsed.type`\n * and would silently skip our events, so this module owns a dedicated\n * one-shot line parser.\n */\nexport type PublishPodEventName =\n | 'deploying'\n | 'ready'\n | 'error'\n | 'timeout'\n | string;\n\nexport interface PublishPodEvent {\n event: PublishPodEventName;\n message: string;\n data?: {\n domain?: string;\n url?: string;\n [key: string]: unknown;\n };\n}\n\nconst TERMINAL_EVENTS: ReadonlySet<string> = new Set([\n 'ready',\n 'error',\n 'timeout',\n]);\n\nexport function isTerminalPublishPodEvent(event: PublishPodEvent): boolean {\n return TERMINAL_EVENTS.has(event.event);\n}\n\n/**\n * Parse a single SSE frame line for publish-pod.\n *\n * Input: full frame text (newline-stripped) containing `data: {...}`.\n * Returns `null` for lines that aren't `data:` frames (blank lines, comment\n * frames starting with `:`, or keep-alives) so callers can just skip them.\n *\n * Throws `RushError` when a `data:` frame is present but the payload fails\n * to parse or doesn't match the expected shape — a malformed frame mid-stream\n * is fatal (we can't silently recover: the server might have sent a terminal\n * we couldn't read).\n */\nexport function parsePublishPodEvent(line: string): PublishPodEvent | null {\n const trimmed = line.trimEnd();\n if (trimmed === '' || trimmed.startsWith(':')) {\n return null;\n }\n if (!trimmed.startsWith('data:')) {\n return null;\n }\n\n // `data:` + optional single space (per SSE spec) before the payload\n const payload = trimmed.replace(/^data:\\s?/, '');\n if (payload === '' || payload === '[DONE]') {\n return null;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(payload);\n } catch {\n throw new RushError(\n `Malformed publish-pod SSE payload (not JSON): ${payload.slice(0, 120)}`,\n { reason: 'publish_pod_invalid_frame' }\n );\n }\n\n if (\n !parsed ||\n typeof parsed !== 'object' ||\n typeof (parsed as { event?: unknown }).event !== 'string'\n ) {\n throw new RushError(\n 'Malformed publish-pod SSE payload (missing `event` string)',\n { reason: 'publish_pod_invalid_frame' }\n );\n }\n\n const obj = parsed as Record<string, unknown>;\n const message = typeof obj.message === 'string' ? obj.message : '';\n const data =\n obj.data && typeof obj.data === 'object'\n ? (obj.data as PublishPodEvent['data'])\n : undefined;\n\n return {\n event: obj.event as string,\n message,\n data,\n };\n}\n\nexport interface StreamPublishPodOptions {\n commitId: string;\n customDomainPrefix?: string;\n /** Called for every non-terminal event so callers can render progress. */\n onEvent?: (event: PublishPodEvent) => void;\n}\n\nexport interface StreamPublishPodResult {\n /** The terminal `ready` event's `data.url`, if provided. */\n url?: string;\n /** The terminal `ready` event's `data.domain`, if provided. */\n domain?: string;\n /** Full last event (always a `ready`). Exposed for downstream formatting. */\n readyEvent: PublishPodEvent;\n}\n\n/**\n * One-shot consumption of `POST /api/projects/:id/publish-pod`.\n *\n * Contract (spec §\"SSE handling for publish-pod\"):\n * - **No reconnect.** POST has side effects; re-POSTing would re-trigger a\n * publish. If the stream drops before a terminal event, throw.\n * - **Dedicated parser.** The shared SSE helpers expect `{type, data}`;\n * our endpoint emits `{event, message, data?}`. Reusing them would\n * silently drop events. Hence the bespoke line parser below.\n * - **Terminal events**: `ready` resolves; `error` / `timeout` throws.\n */\nexport async function streamPublishPod(\n client: RushClient,\n projectId: string,\n opts: StreamPublishPodOptions\n): Promise<StreamPublishPodResult> {\n const body: Record<string, unknown> = { commitId: opts.commitId };\n if (opts.customDomainPrefix !== undefined) {\n body.customDomainPrefix = opts.customDomainPrefix;\n }\n\n const response = await client.fetchRaw(\n `/api/projects/${encodeURIComponent(projectId)}/publish-pod`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n },\n body: JSON.stringify(body),\n }\n );\n\n if (!response.body) {\n throw new RushError('publish-pod response has no body', {\n reason: 'publish_pod_no_body',\n });\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let terminal: PublishPodEvent | null = null;\n\n const processFrame = (frame: string): void => {\n // Each SSE \"message\" can have multiple `data:` lines; publish-pod only\n // emits one per message so we parse the first non-null.\n for (const rawLine of frame.split('\\n')) {\n const event = parsePublishPodEvent(rawLine);\n if (!event) continue;\n\n if (isTerminalPublishPodEvent(event)) {\n terminal = event;\n return;\n }\n opts.onEvent?.(event);\n }\n };\n\n try {\n while (!terminal) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n // SSE frames are separated by blank lines (\"\\n\\n\"); process every\n // complete frame we have so far.\n let sepIdx = buffer.indexOf('\\n\\n');\n while (sepIdx !== -1 && !terminal) {\n const frame = buffer.slice(0, sepIdx);\n buffer = buffer.slice(sepIdx + 2);\n processFrame(frame);\n sepIdx = buffer.indexOf('\\n\\n');\n }\n }\n\n // Flush any remaining buffered frame (server may end without trailing\n // blank line).\n if (!terminal && buffer.length > 0) {\n processFrame(buffer);\n buffer = '';\n }\n } finally {\n reader.cancel().catch(() => {});\n }\n\n if (!terminal) {\n throw new RushError('Deployment stream disconnected unexpectedly', {\n reason: 'publish_pod_stream_disconnected',\n });\n }\n\n const finalEvent: PublishPodEvent = terminal;\n if (finalEvent.event === 'error' || finalEvent.event === 'timeout') {\n throw new RushError(\n finalEvent.message || `Deployment ${finalEvent.event}`,\n {\n reason:\n finalEvent.event === 'timeout'\n ? 'publish_pod_timeout'\n : 'publish_pod_error',\n event: finalEvent.event,\n }\n );\n }\n\n return {\n url: finalEvent.data?.url,\n domain: finalEvent.data?.domain,\n readyEvent: finalEvent,\n };\n}\n","/**\n * Derive Rush deployment domain suffix from the configured API base URL.\n *\n * The CLI does not have `window.location`, so it cannot reuse the shared\n * `@cortex/shared/utils/url.getDomainSuffix()` helper (which reads\n * `window.location.hostname`). Instead we look at the configured API origin\n * and match the same convention:\n *\n * rush-test.zhenguanyu.com → rush-dev.zhenguanyu.com (test env)\n * rush.zhenguanyu.com → rush.zhenguanyu.com (production)\n *\n * Anything else (local dev, self-hosted, unknown hostname, unparseable URL)\n * falls back to `rush.zhenguanyu.com`. This mirrors the web UI's\n * `getDomainSuffix()` fallback in `packages/shared/src/utils/url.ts`. The\n * consequence is real: if you run Rush under a custom deployment suffix,\n * the URL the CLI prints may not match the served hostname — until the\n * availability / publish endpoints expose a concrete `deployedDomain`\n * field in their response, there is no safe detection path CLI-side.\n *\n * Kept separate from `config.ts` so tests can exercise the derivation in\n * isolation.\n */\nexport function deriveDomainSuffix(apiBaseUrl: string): string {\n let host: string;\n try {\n host = new URL(apiBaseUrl).hostname;\n } catch {\n return 'rush.zhenguanyu.com';\n }\n if (host.includes('rush-test') || host.includes('local')) {\n return 'rush-dev.zhenguanyu.com';\n }\n return 'rush.zhenguanyu.com';\n}\n\n/**\n * Build the full deploy domain for a given prefix, matching the web UI's\n * `buildDomain({ mode: DEPLOY })` behaviour:\n *\n * test env: `{prefix}-test.{suffix}`\n * production env: `{prefix}.{suffix}`\n */\nexport function buildDeployDomain(\n prefix: string,\n env: 'test' | 'production',\n apiBaseUrl: string\n): string {\n const suffix = deriveDomainSuffix(apiBaseUrl);\n return env === 'test' ? `${prefix}-test.${suffix}` : `${prefix}.${suffix}`;\n}\n\n/** Convenience: full URL (with protocol) for the deploy domain. */\nexport function buildDeployUrl(\n prefix: string,\n env: 'test' | 'production',\n apiBaseUrl: string\n): string {\n return `https://${buildDeployDomain(prefix, env, apiBaseUrl)}`;\n}\n","import { resolve } from 'node:path';\nimport chalk from 'chalk';\nimport type { Command } from 'commander';\nimport { resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport { createClient } from '../../util/client.js';\nimport { readEnvFile } from '../../util/env-file.js';\nimport { RushError } from '../../util/errors.js';\nimport {\n extractRushProjectId,\n getRemoteUrl,\n gitAddAndCommit,\n gitInit,\n gitPushUrl,\n hasUncommittedChanges,\n isGitRepo,\n} from '../../util/git.js';\nimport { requireAuth } from '../../util/require-auth.js';\nimport { runChecks } from '../check/index.js';\n\n/** Partial shape of `GET /api/tasks/:id`. The push flow only cares about\n * identity (`id`) and output (`previewUrl`); other fields are returned by\n * the server but we don't depend on them here. */\ninterface TaskStatusResponse {\n id: string;\n previewUrl?: string | null;\n [key: string]: unknown;\n}\n\n/** Response of `POST /api/repository/sync/pull`. */\ninterface SyncPullResponse {\n success: boolean;\n /**\n * - `pulled`: git pull produced new commits\n * - `up_to_date`: pod already at HEAD\n * - `conflict`: merge conflict → user must rebase locally\n * - `failed`: other server-side failure (body carries details)\n */\n state?: 'pulled' | 'up_to_date' | 'conflict' | 'failed';\n message?: string;\n remoteCommit?: string;\n [key: string]: unknown;\n}\n\n/**\n * Kept for backward-compat with callers that still import it; the new push\n * flow does not show git URLs to the user, so there is nothing to mask.\n */\nexport function maskToken(url: string): string {\n return url.replace(/:([^@]{4})[^@]*@/, ':$1****@');\n}\n\n/**\n * Guidance shown when no rush project can be identified for the working\n * directory. Matches issue #1149's UX spec verbatim: steer users to\n * `task create` (fresh prompt-driven project) or `task init` (adopt an\n * existing local directory). The command name stays `task init` here even\n * though #1150 may rename it — renaming the user-facing verb is out of\n * scope for this PR.\n */\nconst NO_PROJECT_HINT = [\n 'No rush project detected for this directory. Either:',\n ' rush-ai task create -a web-builder -p \"...\" (build a new site from a prompt)',\n ' rush-ai task init (link an existing local project)',\n].join('\\n');\n\nexport function registerPushSubcommand(task: Command, program: Command): void {\n task\n .command('push')\n .description(\n 'Push local changes to an existing Rush project (git push + pod sync/pull)'\n )\n .option('-n, --name <name>', 'Project name (unused; kept for compat)')\n .option('-p, --path <dir>', 'Project directory', '.')\n .action(async (opts: { name?: string; path: string }) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n const projectPath = resolve(opts.path);\n\n // ─── Step 1: Local readiness checks ─────────────────────────────\n if (format !== 'json') {\n output.info('Checking project readiness...');\n }\n\n const report = runChecks(projectPath);\n if (!report.deployable) {\n if (format === 'json') {\n output.log(\n JSON.stringify(\n { error: 'Project has errors that must be fixed', report },\n null,\n 2\n )\n );\n } else {\n output.error('Project has errors that must be fixed before pushing.');\n output.dim('Run `rush-ai check` for details.');\n }\n process.exit(1);\n }\n\n // ─── Step 2: Identify the rush project ──────────────────────────\n //\n // Resolution order (issue #1149):\n // a. Parse `.git/config` remote.origin.url → extract projectId.\n // b. Verify projectId with GET /api/tasks/:id.\n // c. Fallback: .rush/env.md `PROJECT_ID` (for working trees where\n // the git remote was renamed or missing — verify against the\n // same endpoint so we never trust env.md blindly).\n // d. All failed → refuse with a guidance message.\n //\n // `.rush/env.md` is explicitly demoted from \"reuse signal\" to\n // \"fallback identity\" here: the working tree's git remote is the\n // authoritative source of truth.\n const envFile = readEnvFile(projectPath);\n const remoteUrl = isGitRepo(projectPath)\n ? getRemoteUrl(projectPath)\n : null;\n const idFromRemote = remoteUrl ? extractRushProjectId(remoteUrl) : null;\n\n let projectId: string | null = null;\n let taskInfo: TaskStatusResponse | null = null;\n // Tracks how we learned the projectId — determines where we push\n // in step 4 so we never pair a Rush identity with a non-Rush remote\n // (issue #1149 Sparring review P0: \"origin=github + env.md\n // PROJECT_ID would blindly push to GitHub and still report\n // success\").\n let projectSource: 'git-remote' | 'env-md' | null = null;\n\n if (idFromRemote) {\n const verified = await tryVerifyProject(client, idFromRemote);\n if (verified) {\n projectId = idFromRemote;\n taskInfo = verified;\n projectSource = 'git-remote';\n }\n }\n\n if (!projectId && envFile.PROJECT_ID) {\n const verified = await tryVerifyProject(client, envFile.PROJECT_ID);\n if (verified) {\n projectId = envFile.PROJECT_ID;\n taskInfo = verified;\n projectSource = 'env-md';\n }\n }\n\n if (!projectId || !taskInfo || !projectSource) {\n throw new RushError(NO_PROJECT_HINT, {}, 'NO_RUSH_PROJECT', 1);\n }\n\n if (format !== 'json') {\n output.info(`Using Rush project ${projectId}`);\n }\n\n // ─── Step 3: Ensure the working tree has no uncommitted changes ─\n //\n // `task push` intentionally auto-commits (spec §J-2) so IDE flows\n // don't force the user to drop into a terminal mid-edit. Commit\n // message stays `'deploy to rush'` for backward-compat with the\n // old flow (any automation grepping the history still matches).\n if (!isGitRepo(projectPath)) {\n // Edge case: if remote-based identification fell through to\n // env.md, the directory might not even be a git repo. In that\n // case bootstrap one so the subsequent push has something to\n // push — this mirrors the pre-#1149 behaviour.\n gitInit(projectPath);\n }\n\n if (hasUncommittedChanges(projectPath)) {\n gitAddAndCommit(projectPath, 'deploy to rush');\n }\n\n // ─── Step 4: git push ───────────────────────────────────────────\n //\n // Push target depends on HOW we identified the project:\n //\n // projectSource='git-remote' (authoritative)\n // → push to `origin`, with env.md GIT_REMOTE as a CI-only\n // fallback (tokenised URL — users without SSO).\n //\n // projectSource='env-md' (origin is not a Rush URL)\n // → push ONLY to env.md.GIT_REMOTE. Never touch `origin` —\n // it might point at an unrelated repo (e.g. GitHub) and\n // pushing there would succeed, report success to the user,\n // yet leave the Rush project at the old commit (pod\n // sync/pull would be a no-op). Caught by Sparring on #1149.\n if (format !== 'json') {\n output.info('Pushing code to Rush...');\n }\n\n const pushResult = pushWithRouting(\n projectPath,\n projectSource,\n envFile.GIT_REMOTE\n );\n if (!pushResult.success) {\n if (format === 'json') {\n output.log(\n JSON.stringify(\n {\n error: 'Git push failed',\n details: pushResult.stderr,\n projectId,\n },\n null,\n 2\n )\n );\n } else {\n output.error('Git push failed:');\n output.log(pushResult.stderr);\n output.dim(\n 'Check your VPN / SSO session and git credentials. Code was not pushed.'\n );\n output.dim(`Project ID: ${projectId}`);\n }\n process.exit(1);\n }\n\n if (format !== 'json') {\n output.success('Code pushed successfully');\n }\n\n // ─── Step 5: Trigger pod git pull ───────────────────────────────\n //\n // `POST /api/repository/sync/pull` runs `git pull --rebase` inside\n // the project pod under a distributed lock, so Vite HMR picks up\n // the new files within a second or two. Measured end-to-end:\n // git push (~1s) + sync/pull (~1s) + HMR reload (<2s) ≈ 4s, vs\n // 30-60s for the retired restart + pod-ready polling path.\n if (format !== 'json') {\n output.info('Syncing code in the pod...');\n }\n\n let syncResp: SyncPullResponse;\n try {\n const { data } = await client.post<SyncPullResponse>(\n '/api/repository/sync/pull',\n { projectId }\n );\n syncResp = data;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new RushError(\n `Pod sync failed: ${message}`,\n { projectId },\n 'SYNC_PULL_FAILED',\n 1\n );\n }\n\n if (!syncResp.success) {\n if (syncResp.state === 'conflict') {\n throw new RushError(\n 'Pod sync failed: local repository diverged from the pod. ' +\n 'Rebase locally (git pull --rebase origin HEAD) and retry `rush-ai task push`.',\n { projectId, state: 'conflict' },\n 'SYNC_PULL_CONFLICT',\n 1\n );\n }\n throw new RushError(\n `Pod sync failed: ${syncResp.message ?? 'unknown error'}`,\n { projectId, state: syncResp.state },\n 'SYNC_PULL_FAILED',\n 1\n );\n }\n\n // ─── Step 6: Output ─────────────────────────────────────────────\n const previewUrl =\n (taskInfo.previewUrl as string | null | undefined) ??\n envFile.PREVIEW_URL ??\n null;\n const chatUrl = `https://rush.zhenguanyu.com/project/${projectId}`;\n const result = {\n projectId,\n previewUrl,\n chatUrl,\n syncState: syncResp.state ?? 'pulled',\n };\n\n if (format === 'json') {\n output.log(JSON.stringify(result, null, 2));\n return;\n }\n\n output.newline();\n output.success('Push complete!');\n output.log(` ${chalk.bold('Project ID:')} ${result.projectId}`);\n if (result.previewUrl) {\n output.log(` ${chalk.bold('Preview:')} ${result.previewUrl}`);\n }\n output.log(` ${chalk.bold('Chat:')} ${result.chatUrl}`);\n output.log(` ${chalk.bold('Sync:')} ${result.syncState}`);\n output.newline();\n });\n}\n\n/**\n * Verify that a projectId exists via `GET /api/tasks/:id`. Any error\n * (404 / network / 5xx) counts as \"could not verify\" → returns `null` so\n * the caller can fall through to the next resolution strategy.\n *\n * The explicit id-match check (`data.id === id`) defends against a mock or\n * proxy returning a generic OK body for a 200 status.\n */\nasync function tryVerifyProject(\n client: ReturnType<typeof createClient>,\n id: string\n): Promise<TaskStatusResponse | null> {\n try {\n const { data } = await client.get<TaskStatusResponse>(\n `/api/tasks/${encodeURIComponent(id)}`\n );\n if (data && data.id === id) return data;\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Route the `git push` based on how the rush project was identified.\n *\n * The routing rule — re-derived after issue #1149's Sparring review —\n * refuses to push to an `origin` that we could not prove is the rush\n * project. Past behaviour would silently push to a user's GitHub origin\n * (for example) and then trigger the pod `sync/pull` on an unrelated\n * Rush project, yielding a false-positive \"push complete\" in the CLI\n * while the pod kept serving the old commit.\n *\n * Matrix:\n *\n * | source | primary | fallback |\n * |-------------|--------------------|--------------------------|\n * | git-remote | `origin` | env.md GIT_REMOTE (CI) |\n * | env-md | env.md GIT_REMOTE | — (error if unset/masked)|\n *\n * Masked tokens (legacy `****` placeholder values, written by pre-m3-5\n * `task init`) are skipped: they always fail auth, and the resulting\n * \"403 Forbidden\" misleads users into thinking the server rejected their\n * permissions when the real cause is a placeholder secret.\n */\nfunction pushWithRouting(\n projectPath: string,\n source: 'git-remote' | 'env-md',\n envGitRemote: string | undefined\n): { success: boolean; stderr: string } {\n const envRemoteUsable =\n typeof envGitRemote === 'string' &&\n envGitRemote.length > 0 &&\n !envGitRemote.includes('****');\n\n if (source === 'git-remote') {\n const primary = gitPushUrl(projectPath, 'origin');\n if (primary.success) return primary;\n if (!envRemoteUsable) return primary;\n\n const fallback = gitPushUrl(projectPath, envGitRemote as string);\n // Return the fallback error on failure — it's the more actionable\n // one (token probably expired/revoked).\n return fallback;\n }\n\n // source === 'env-md': origin is NOT a rush URL, so the only safe push\n // target is the env.md GIT_REMOTE. Refuse if it's missing or masked.\n if (!envRemoteUsable) {\n return {\n success: false,\n stderr:\n 'Cannot push: .rush/env.md identifies a Rush project but has no usable GIT_REMOTE. ' +\n 'Re-run `rush-ai task init` to refresh the deploy token, or set `origin` to the Rush GitLab URL.',\n };\n }\n return gitPushUrl(projectPath, envGitRemote as string);\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport path from 'node:path';\n\n/**\n * `.rush/env.md` lives in the user's project root. It is **never** committed\n * (the file header says so explicitly, and `ensureRushGitignore` keeps\n * `.rush/` in `.gitignore`).\n *\n * Contract (see `specs/rush-v2/cli/roadmap.spec.md` §M3 invariants 2, 5):\n * - Top of file must carry the `# DO NOT commit` warning. If an existing\n * file lost the warning (user edited it) we re-prepend it on write.\n * - Body is a set of `KEY=VALUE` lines, parsed the usual dotenv way.\n * - `STATE` is a checkpoint for `task link`'s compensating-consistency flow\n * (`pod-only` | `pod-db` | `pod-db-oss` | `complete`). On resume, `task\n * link` reads STATE to skip remote calls it already made.\n * - No logs / error stacks / telemetry ever land here. The serializer only\n * writes fixed keys + a short comment map.\n */\n\nexport type CheckpointState = 'pod-only' | 'pod-db' | 'pod-db-oss' | 'complete';\n\nexport interface RushEnv {\n /** Required once `task link` has created the pod. */\n PROJECT_ID?: string;\n PREVIEW_URL?: string;\n GIT_REMOTE?: string;\n STATE?: CheckpointState;\n // DB (optional)\n DATABASE_URL?: string;\n SUPABASE_URL?: string;\n SUPABASE_ANON_KEY?: string;\n // Rush-proxied object storage (optional; no direct OSS access)\n STORAGE_API_BASE?: string;\n /**\n * Unknown keys are preserved verbatim so hand-edits survive a round-trip.\n * Callers should not rely on this bucket; it is for forward-compat only.\n */\n extras?: Record<string, string>;\n}\n\nexport const ENV_FILE_HEADER = [\n '# Rush Project Environment',\n '',\n '# DO NOT commit — contains credentials',\n '# This file is auto-generated by `rush-ai task link`. Do not write logs / error stacks / telemetry here.',\n '',\n].join('\\n');\n\n/** Resolve `.rush/env.md` for a given project directory. */\nexport function envFilePath(projectPath: string): string {\n return path.join(projectPath, '.rush', 'env.md');\n}\n\n/** Resolve `.rush/agent-handoff.md` for a given project directory. */\nexport function handoffFilePath(projectPath: string): string {\n return path.join(projectPath, '.rush', 'agent-handoff.md');\n}\n\n/**\n * Parse `.rush/env.md`. Returns an empty `RushEnv` if the file does not\n * exist. The parser is tolerant: blank lines, `#` comments, and lines\n * without `=` are ignored. Quoted values have matching outer quotes\n * stripped so `KEY=\"value\"` and `KEY=value` round-trip identically.\n */\nexport function readEnvFile(projectPath: string): RushEnv {\n const file = envFilePath(projectPath);\n if (!existsSync(file)) return {};\n\n const raw = readFileSync(file, 'utf8');\n const env: RushEnv = {};\n const extras: Record<string, string> = {};\n\n const known = new Set<keyof RushEnv>([\n 'PROJECT_ID',\n 'PREVIEW_URL',\n 'GIT_REMOTE',\n 'STATE',\n 'DATABASE_URL',\n 'SUPABASE_URL',\n 'SUPABASE_ANON_KEY',\n 'STORAGE_API_BASE',\n ]);\n\n for (const line of raw.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eq = trimmed.indexOf('=');\n if (eq < 0) continue;\n\n const key = trimmed.slice(0, eq).trim();\n let value = trimmed.slice(eq + 1).trim();\n if (\n value.length >= 2 &&\n ((value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\")))\n ) {\n value = value.slice(1, -1);\n }\n if (!key) continue;\n\n if (known.has(key as keyof RushEnv)) {\n // STATE values are validated at the call site; we store whatever\n // the file says so a resume can detect a corrupted checkpoint.\n (env as Record<string, string>)[key] = value;\n } else {\n extras[key] = value;\n }\n }\n\n if (Object.keys(extras).length > 0) env.extras = extras;\n return env;\n}\n\n/**\n * Serialize a `RushEnv` back into `.rush/env.md`. The header warning is\n * always re-prepended — even if a prior edit dropped it — because the\n * no-commit invariant depends on it being visible.\n *\n * The write is not atomic. If the process dies mid-write the user can\n * re-run `task link`, which treats a missing / partial file as a fresh\n * start rather than trying to salvage.\n */\nexport function writeEnvFile(projectPath: string, env: RushEnv): void {\n const dir = path.join(projectPath, '.rush');\n mkdirSync(dir, { recursive: true });\n\n const lines: string[] = [ENV_FILE_HEADER.trimEnd(), ''];\n\n if (env.PROJECT_ID) lines.push(`PROJECT_ID=${env.PROJECT_ID}`);\n if (env.PREVIEW_URL) lines.push(`PREVIEW_URL=${env.PREVIEW_URL}`);\n if (env.GIT_REMOTE) lines.push(`GIT_REMOTE=${env.GIT_REMOTE}`);\n\n lines.push('');\n lines.push('# Checkpoint state (for idempotent resume)');\n lines.push('# Values: pod-only | pod-db | pod-db-oss | complete');\n if (env.STATE) lines.push(`STATE=${env.STATE}`);\n\n const hasDb = env.DATABASE_URL || env.SUPABASE_URL || env.SUPABASE_ANON_KEY;\n if (hasDb) {\n lines.push('');\n lines.push('# DB (optional)');\n if (env.DATABASE_URL) lines.push(`DATABASE_URL=${env.DATABASE_URL}`);\n if (env.SUPABASE_URL) lines.push(`SUPABASE_URL=${env.SUPABASE_URL}`);\n if (env.SUPABASE_ANON_KEY)\n lines.push(`SUPABASE_ANON_KEY=${env.SUPABASE_ANON_KEY}`);\n }\n\n if (env.STORAGE_API_BASE) {\n lines.push('');\n lines.push('# Rush-proxied object storage (optional)');\n lines.push(\n '# Authentication: use RUSH_PLATFORM_TOKEN from your shell environment (existing Rush mechanism).'\n );\n lines.push(\n '# Platform Token MUST remain server-side only — never expose to the browser.'\n );\n lines.push(`STORAGE_API_BASE=${env.STORAGE_API_BASE}`);\n }\n\n if (env.extras) {\n const extraKeys = Object.keys(env.extras);\n if (extraKeys.length > 0) {\n lines.push('');\n lines.push('# Additional (preserved from prior hand-edits)');\n for (const k of extraKeys) {\n lines.push(`${k}=${env.extras[k]}`);\n }\n }\n }\n\n writeFileSync(file(projectPath), `${lines.join('\\n')}\\n`, 'utf8');\n}\n\nfunction file(projectPath: string): string {\n return envFilePath(projectPath);\n}\n\n/**\n * Idempotently add `.rush/` to the project's `.gitignore`. The user's\n * `.rush/env.md` must never be committed (spec §M3 invariant 2), and we\n * cannot rely on the user to add the entry themselves. Matches any of\n * `.rush`, `.rush/`, `/.rush`, `/.rush/` (with or without trailing\n * comment) on a line by itself, so we won't double-append on re-run.\n */\nexport function ensureRushGitignore(projectPath: string): {\n added: boolean;\n path: string;\n} {\n const gi = path.join(projectPath, '.gitignore');\n const existing = existsSync(gi) ? readFileSync(gi, 'utf8') : '';\n\n const alreadyIgnored = existing\n .split(/\\r?\\n/)\n .map((l) => l.replace(/#.*$/, '').trim())\n .some(\n (l) =>\n l === '.rush' || l === '.rush/' || l === '/.rush' || l === '/.rush/'\n );\n\n if (alreadyIgnored) return { added: false, path: gi };\n\n const needsLeadingNewline = existing.length > 0 && !existing.endsWith('\\n');\n const block = `${needsLeadingNewline ? '\\n' : ''}# rush-ai local artifacts — contains credentials, do not commit\\n.rush/\\n`;\n\n writeFileSync(gi, existing + block, 'utf8');\n return { added: true, path: gi };\n}\n\n/** Valid STATE values. Used by callers that need to guard against a\n * corrupted checkpoint read from disk. */\nexport const CHECKPOINT_STATES: readonly CheckpointState[] = [\n 'pod-only',\n 'pod-db',\n 'pod-db-oss',\n 'complete',\n] as const;\n\nexport function isCheckpointState(v: unknown): v is CheckpointState {\n return (\n typeof v === 'string' &&\n (CHECKPOINT_STATES as readonly string[]).includes(v)\n );\n}\n\n/**\n * Write the agent handoff checklist. `task link` produces this so the\n * calling IDE agent has a machine-consumable list of local code edits\n * to perform (change `.env.local`, run migrations, swap upload code).\n * Overwrites on every call — the handoff is a snapshot of the\n * init outcome, not accumulated state.\n */\nexport function writeHandoffFile(projectPath: string, content: string): void {\n const dir = path.join(projectPath, '.rush');\n mkdirSync(dir, { recursive: true });\n writeFileSync(handoffFilePath(projectPath), content, 'utf8');\n}\n","import type { Command } from 'commander';\nimport { resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport {\n type ApiTaskResult,\n type CliEnv,\n resolveWebBuilderTask,\n toDeployVersionEnv,\n toPublishEnv,\n type WebBuilderTask,\n} from '../../util/api-task.js';\nimport { isCIMode } from '../../util/ci.js';\nimport { createClient, type RushClient } from '../../util/client.js';\nimport { getGlobalConfig } from '../../util/config.js';\nimport { ApiError, RushError, TaskFailedError } from '../../util/errors.js';\nimport {\n type PublishPodEvent,\n streamPublishPod,\n} from '../../util/publish-pod-stream.js';\nimport { requireAuth } from '../../util/require-auth.js';\nimport { buildDeployDomain, buildDeployUrl } from '../../util/rush-domain.js';\nimport { registerPushSubcommand } from './push.js';\n\n/**\n * Allowed domain-prefix shape per spec §5. Client-side pre-check: the server\n * runs the same regex, but catching it here avoids a round-trip + lets us\n * give a more pointed error message.\n */\nconst DOMAIN_PREFIX_REGEX = /^[a-z0-9][a-z0-9-]{0,28}[a-z0-9]$|^[a-z0-9]$/;\n\nexport function validateDomainPrefix(prefix: string): void {\n if (!DOMAIN_PREFIX_REGEX.test(prefix)) {\n throw new RushError(\n `Invalid domain prefix: \"${prefix}\". Use 1-30 lowercase letters/digits/hyphens; must start and end with a letter or digit.`,\n { reason: 'invalid_domain_prefix', domain: prefix }\n );\n }\n}\n\ninterface DeployCliOptions {\n // NEW flow\n version?: string;\n env?: CliEnv;\n domain?: string;\n yes?: boolean;\n // Legacy shim (retained so commander does not reject old invocations)\n name?: string;\n path?: string;\n}\n\ninterface VersionListResponse {\n success?: boolean;\n versions?: Array<{\n id: string;\n versionNumber?: number;\n createdAt?: string;\n [key: string]: unknown;\n }>;\n}\n\ninterface BuildCheckResponse {\n exists: boolean;\n commitHash: string;\n shortHash?: string;\n ossBaseUrl?: string;\n message?: string;\n}\n\ninterface HasProductionResponse {\n hasProduction: boolean;\n}\n\ninterface DomainCheckResponse {\n available: boolean;\n conflictProjectId?: string;\n}\n\ninterface PublishResponse {\n success: boolean;\n error?: string;\n message?: string;\n data?: {\n domain?: string;\n url?: string;\n environment?: string;\n [key: string]: unknown;\n };\n}\n\ntype FlowStep =\n | 'resolve'\n | 'version'\n | 'preflight'\n | 'domain'\n | 'confirm'\n | 'publish';\n\n/**\n * Error that carries the pipeline step where the failure happened. CI mode\n * repackages these as `TaskFailedError` with the step in `meta.step` — this\n * is the structured payload spec §9 promises to CI consumers.\n */\nclass DeployStepError extends RushError {\n public step: FlowStep;\n constructor(\n step: FlowStep,\n message: string,\n meta: Record<string, unknown> = {}\n ) {\n super(message, { ...meta, step });\n // Spec §\"Error-code convention\": no new names/codes — keep this\n // observationally indistinguishable from RushError so CLI consumers\n // branch on `meta.step` / `meta.reason`, not on the class identity.\n this.name = 'RushError';\n this.step = step;\n }\n}\n\nexport function registerDeploySubcommand(\n task: Command,\n program: Command\n): void {\n task\n .command('deploy')\n .description(\n 'Deploy a web-builder task to production. Pass <id> to publish a built version; legacy `task deploy` with no id delegates to `task push` for this release only.'\n )\n .argument(\n '[id]',\n 'Task ID to deploy. Omit to invoke the deprecated legacy `task deploy` shim (forwards to `task push`).'\n )\n // NEW flow options (spec §\"Command signatures — task deploy [id]\")\n //\n // No commander-level defaults for `--version` / `--env`: we need to\n // distinguish \"user did not pass the flag\" from \"user passed the default\n // value explicitly\" in the deprecation shim (spec §\"Deprecation shim\":\n // bare `task deploy --env production` must RushError, not delegate). We\n // apply the defaults inside the action instead.\n .option(\n '--version <hash|latest>',\n 'Commit hash to deploy, or \"latest\" (defaults to latest)'\n )\n .option(\n '--env <test|production>',\n 'Target environment (defaults to production). Next.js projects only deploy to production.'\n )\n .option(\n '--domain <prefix>',\n 'Optional custom domain prefix (1-30 lowercase letters/digits/hyphens)'\n )\n .option('--yes', 'Skip the pre-publish confirmation prompt')\n // LEGACY shim options — declared so commander does not reject old calls\n .option('-n, --name <name>', 'Legacy: project name for `task push` flow')\n .option(\n '-p, --path <dir>',\n 'Legacy: project directory for `task push` flow'\n )\n .action(async (id: string | undefined, opts: DeployCliOptions) => {\n const ciMode = isCIMode(program.opts());\n\n // Deprecation shim dispatch (spec §\"Deprecation shim\")\n if (id === undefined) {\n if (hasNewFlowOptions(opts)) {\n throw new RushError(\n '`task deploy` now requires a task id. Example: `rush-ai task deploy <taskId> --domain my-app`.',\n { reason: 'missing_task_id' }\n );\n }\n output.warn(\n '`task deploy` (no id) is deprecated. Use `rush-ai task push` instead. Falling back for this release.'\n );\n await invokeLegacyPushShim(program, {\n name: opts.name,\n path: opts.path,\n });\n return;\n }\n\n if (opts.name !== undefined || opts.path !== undefined) {\n output.warn(\n 'Legacy flags -n/--name and -p/--path are ignored when a task id is provided.'\n );\n }\n\n try {\n await runDeployFlow(program, id, opts);\n } catch (err) {\n if (ciMode && err instanceof DeployStepError) {\n throw new TaskFailedError(err.message, err.meta);\n }\n throw err;\n }\n });\n}\n\nfunction hasNewFlowOptions(opts: DeployCliOptions): boolean {\n // `--version` / `--env` / `--domain` / `--yes` are all optional with no\n // commander-level defaults (see the `.option(...)` calls above), so\n // `undefined` here means \"the user did not pass this flag\". Passing\n // `--version latest` explicitly — even though it equals the internal\n // default — still counts as using the new flow and must RushError per\n // spec §\"Deprecation shim\".\n return (\n opts.version !== undefined ||\n opts.env !== undefined ||\n opts.domain !== undefined ||\n opts.yes === true\n );\n}\n\nasync function invokeLegacyPushShim(\n program: Command,\n opts: { name?: string; path?: string }\n): Promise<void> {\n // Reach into the already-registered `push` subcommand and invoke its action\n // via commander. This keeps the legacy entry point 1:1 with the `task push`\n // behavior without duplicating that logic here.\n const task = program.commands.find((c) => c.name() === 'task');\n if (!task) {\n throw new RushError('Internal: `task` command not registered', {\n reason: 'internal_task_missing',\n });\n }\n const argv = ['node', 'rush-ai', 'task', 'push'];\n if (opts.name) argv.push('--name', opts.name);\n if (opts.path) argv.push('--path', opts.path);\n await program.parseAsync(argv);\n}\n\nasync function runDeployFlow(\n program: Command,\n id: string,\n opts: DeployCliOptions\n): Promise<void> {\n // Step 1: auth\n requireAuth();\n\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n // Step 2: resolve task (web-builder gate via helper)\n let resolved: WebBuilderTask;\n try {\n resolved = await resolveWebBuilderTask(client, id);\n } catch (err) {\n throw toStepError('resolve', err, { taskId: id });\n }\n\n // Step 3: resolve version\n const commitHash = await resolveCommitHash(client, id, opts.version, format);\n\n // Step 4: template-specific preflight\n await preflight(client, id, commitHash, opts.env ?? 'production', resolved);\n\n // Step 5: domain preflight (only when --domain passed)\n if (opts.domain !== undefined) {\n await domainPreflight(client, id, opts.domain);\n }\n\n // Step 6: confirm (skipped in CI / with --yes)\n const confirmed = await confirmPublish({\n format,\n skip: Boolean(opts.yes) || isCIMode(program.opts()),\n env: opts.env ?? 'production',\n commitHash,\n domain: opts.domain,\n isNextjs: resolved.isNextjs,\n });\n if (!confirmed) {\n output.info('Cancelled.');\n return;\n }\n\n // Step 7: publish\n const publishResult = resolved.isNextjs\n ? await publishNextjs(client, id, commitHash, opts.domain, format)\n : await publishStatic(\n client,\n id,\n commitHash,\n opts.env ?? 'production',\n opts.domain\n );\n\n // Step 8: output (step 9 — CI wrapping — is handled by the `action`)\n emitSuccess({\n format,\n projectId: id,\n commitHash,\n env: opts.env ?? 'production',\n domain: publishResult.domain,\n url: publishResult.url,\n template: resolved.template,\n });\n}\n\nfunction toStepError(\n step: FlowStep,\n err: unknown,\n extraMeta: Record<string, unknown>\n): DeployStepError {\n if (err instanceof DeployStepError) return err;\n if (err instanceof RushError) {\n return new DeployStepError(step, err.message, {\n ...err.meta,\n ...extraMeta,\n });\n }\n if (err instanceof Error) {\n return new DeployStepError(step, err.message, extraMeta);\n }\n return new DeployStepError(step, String(err), extraMeta);\n}\n\nasync function resolveCommitHash(\n client: RushClient,\n projectId: string,\n versionOpt: string | undefined,\n format: string\n): Promise<string> {\n const wantsLatest = !versionOpt || versionOpt === 'latest';\n if (!wantsLatest) {\n return versionOpt;\n }\n\n let response: { data: VersionListResponse };\n try {\n response = await client.get<VersionListResponse>(\n `/api/version/list?projectId=${encodeURIComponent(projectId)}`\n );\n } catch (err) {\n throw toStepError('version', err, { taskId: projectId });\n }\n\n const versions = response.data.versions ?? [];\n if (versions.length === 0) {\n throw new DeployStepError(\n 'version',\n 'No versions yet. Send a message to the task first to produce a buildable commit.',\n { taskId: projectId, reason: 'no_versions' }\n );\n }\n const latest = versions[versions.length - 1];\n if (format !== 'json') {\n output.dim(`Using latest version: ${shortHash(latest.id)}`);\n }\n return latest.id;\n}\n\nasync function preflight(\n client: RushClient,\n projectId: string,\n commitHash: string,\n env: CliEnv,\n resolved: WebBuilderTask\n): Promise<void> {\n if (resolved.isNextjs) {\n if (env === 'test') {\n throw new DeployStepError(\n 'preflight',\n 'Next.js projects only deploy to production. Drop `--env test` or deploy a non-Next.js template.',\n { taskId: projectId, reason: 'nextjs_env_test_unsupported' }\n );\n }\n let hasProd: { data: HasProductionResponse };\n try {\n hasProd = await client.get<HasProductionResponse>(\n `/api/projects/${encodeURIComponent(projectId)}/database/has-production`\n );\n } catch (err) {\n throw toStepError('preflight', err, { taskId: projectId });\n }\n if (!hasProd.data.hasProduction) {\n throw new DeployStepError(\n 'preflight',\n 'Production database not created. Open the Web UI to provision it before deploying.',\n { taskId: projectId, reason: 'no_production_db' }\n );\n }\n return;\n }\n\n // Non-nextjs: build artifacts must be present.\n let check: { data: BuildCheckResponse };\n try {\n check = await client.get<BuildCheckResponse>(\n `/api/build/check?projectId=${encodeURIComponent(projectId)}&commitHash=${encodeURIComponent(commitHash)}`\n );\n } catch (err) {\n throw toStepError('preflight', err, { taskId: projectId, commitHash });\n }\n if (!check.data.exists) {\n throw new DeployStepError(\n 'preflight',\n `Build artifacts missing for commit ${shortHash(commitHash)}. Send a new message to rebuild.`,\n { taskId: projectId, commitHash, reason: 'build_missing' }\n );\n }\n}\n\nasync function domainPreflight(\n client: RushClient,\n projectId: string,\n prefix: string\n): Promise<void> {\n try {\n validateDomainPrefix(prefix);\n } catch (err) {\n throw toStepError('domain', err, { taskId: projectId, domain: prefix });\n }\n\n let check: { data: DomainCheckResponse };\n try {\n check = await client.get<DomainCheckResponse>(\n `/api/projects/check-domain-prefix?prefix=${encodeURIComponent(prefix)}&projectId=${encodeURIComponent(projectId)}`\n );\n } catch (err) {\n throw toStepError('domain', err, { taskId: projectId, domain: prefix });\n }\n if (!check.data.available) {\n throw new DeployStepError(\n 'domain',\n `Domain prefix \"${prefix}\" is taken${check.data.conflictProjectId ? ` by project ${check.data.conflictProjectId}` : ''}.`,\n {\n taskId: projectId,\n domain: prefix,\n conflictProjectId: check.data.conflictProjectId,\n reason: 'domain_taken',\n }\n );\n }\n}\n\ninterface ConfirmArgs {\n format: string;\n skip: boolean;\n env: CliEnv;\n commitHash: string;\n domain?: string;\n isNextjs: boolean;\n}\n\nasync function confirmPublish(args: ConfirmArgs): Promise<boolean> {\n if (args.skip) return true;\n if (args.format === 'json') return true; // JSON mode implies non-interactive\n if (!process.stdin.isTTY) return true; // piped stdin: behave like --yes\n\n output.log(`About to deploy:`);\n output.log(` Version: ${shortHash(args.commitHash)}`);\n output.log(` Env: ${args.env}`);\n if (args.domain) output.log(` Domain: ${args.domain}`);\n output.log(\n ` Template: ${args.isNextjs ? 'nextjs-fullstack (pod)' : 'static (OSS)'}`\n );\n process.stderr.write('Proceed? [y/N] ');\n const answer = await readOneLine();\n return answer.trim().toLowerCase().startsWith('y');\n}\n\nfunction readOneLine(): Promise<string> {\n return new Promise((resolve) => {\n let acc = '';\n const onData = (chunk: Buffer) => {\n acc += chunk.toString('utf-8');\n const nlIdx = acc.indexOf('\\n');\n if (nlIdx !== -1) {\n process.stdin.removeListener('data', onData);\n process.stdin.pause();\n resolve(acc.slice(0, nlIdx));\n }\n };\n process.stdin.resume();\n process.stdin.on('data', onData);\n });\n}\n\ninterface PublishOutcome {\n url?: string;\n domain?: string;\n}\n\nasync function publishNextjs(\n client: RushClient,\n projectId: string,\n commitHash: string,\n domain: string | undefined,\n format: string\n): Promise<PublishOutcome> {\n try {\n const result = await streamPublishPod(client, projectId, {\n commitId: commitHash,\n customDomainPrefix: domain,\n onEvent: (event: PublishPodEvent) => {\n if (format === 'json') return;\n if (event.message) {\n output.dim(`[${event.event}] ${event.message}`);\n }\n },\n });\n // Mirror the static-publish logic: when the user passed `--domain`, the\n // CLI owns the URL shape (same rule the web UI follows in its\n // `buildUrl` calls). Next.js pod publishes only target production.\n if (domain !== undefined) {\n const { api: apiBase } = getGlobalConfig();\n return {\n url: buildDeployUrl(domain, 'production', apiBase),\n domain: buildDeployDomain(domain, 'production', apiBase),\n };\n }\n return { url: result.url, domain: result.domain };\n } catch (err) {\n throw toStepError('publish', err, { taskId: projectId, commitHash });\n }\n}\n\nasync function publishStatic(\n client: RushClient,\n projectId: string,\n commitHash: string,\n env: CliEnv,\n domain: string | undefined\n): Promise<PublishOutcome> {\n const publishBody: Record<string, unknown> = {\n commitId: commitHash,\n // Spec §Environment mapping: publish endpoint uses `online` for prod.\n environment: toPublishEnv(env),\n };\n if (domain !== undefined) publishBody.customDomainPrefix = domain;\n\n let publishResp: { data: PublishResponse };\n try {\n publishResp = await client.post<PublishResponse>(\n `/api/projects/${encodeURIComponent(projectId)}/publish`,\n publishBody\n );\n } catch (err) {\n if (err instanceof ApiError && err.status === 409) {\n throw new DeployStepError(\n 'publish',\n err.message ||\n 'Domain prefix was taken during the publish window (TOCTOU).',\n { taskId: projectId, reason: 'publish_conflict', status: 409 }\n );\n }\n throw toStepError('publish', err, { taskId: projectId, commitHash });\n }\n if (!publishResp.data.success) {\n throw new DeployStepError(\n 'publish',\n publishResp.data.error ||\n publishResp.data.message ||\n 'Publish failed with no error message.',\n { taskId: projectId, reason: 'publish_failed' }\n );\n }\n\n // The static publish upstream currently returns the ingress's default\n // hostname (projectId-based) in `data.domain`/`data.url`, even when we\n // passed a `customDomainPrefix`. The web UI works around this by\n // deriving the URL itself via `buildUrl` — we mirror that here so the\n // CLI reports the actually-served URL to the user.\n //\n // Precedence:\n // 1. custom prefix (`--domain`) → `{prefix}[-test].{suffix}`\n // 2. upstream-returned `data.url` / `data.domain` (fallback for\n // non-custom deploys where projectId-default is correct)\n const { api: apiBase } = getGlobalConfig();\n const resolvedDomain =\n domain !== undefined\n ? buildDeployDomain(domain, env, apiBase)\n : publishResp.data.data?.domain;\n const resolvedUrl =\n domain !== undefined\n ? buildDeployUrl(domain, env, apiBase)\n : publishResp.data.data?.url;\n\n // Spec §8: follow up with deploy-version (best-effort — warn on failure).\n const versionId = commitHash;\n const deployBody = {\n versionId,\n // Spec §Environment mapping: deploy-version uses `production` literal.\n environment: toDeployVersionEnv(env),\n deployedUrl: resolvedUrl ?? '',\n ...(domain !== undefined ? { customDomainPrefix: domain } : {}),\n };\n try {\n await client.post(\n `/api/projects/${encodeURIComponent(projectId)}/deploy-version`,\n deployBody\n );\n } catch (err) {\n const details = err instanceof Error ? err.message : String(err);\n output.warn(\n `deploy-version write failed (best-effort; deploy succeeded): ${details}`\n );\n }\n\n return { url: resolvedUrl, domain: resolvedDomain };\n}\n\ninterface EmitArgs {\n format: string;\n projectId: string;\n commitHash: string;\n env: CliEnv;\n domain?: string;\n url?: string;\n template: string;\n}\n\nfunction emitSuccess(args: EmitArgs): void {\n if (args.format === 'json') {\n output.log(\n JSON.stringify(\n {\n projectId: args.projectId,\n version: args.commitHash,\n shortVersion: shortHash(args.commitHash),\n env: args.env,\n domain: args.domain ?? null,\n url: args.url ?? null,\n template: args.template,\n },\n null,\n 2\n )\n );\n return;\n }\n\n output.newline();\n output.success('Deployed!');\n if (args.url) output.log(` URL: ${args.url}`);\n output.log(` Version: ${shortHash(args.commitHash)}`);\n output.log(` Env: ${args.env}`);\n if (args.domain) output.log(` Domain: ${args.domain}`);\n}\n\nfunction shortHash(hash: string): string {\n return hash.length > 8 ? hash.slice(0, 8) : hash;\n}\n\n// Re-export for index.ts — tests that exercise the legacy shim poke at the\n// push registration through the same module graph.\nexport { registerPushSubcommand };\n\n/**\n * Test seam: exposed strictly so integration tests can assert that helper\n * functions from api-task are wired in. Runtime code does not import this.\n * @internal\n */\nexport const __envMappers = {\n toPublishEnv,\n toDeployVersionEnv,\n};\n\n// Keep ApiTaskResult referenced so downstream type narrowing works for\n// consumers that destructure `resolved.task`.\nexport type { ApiTaskResult };\n","import type { Command } from 'commander';\nimport { resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport { resolveWebBuilderTask } from '../../util/api-task.js';\nimport { createClient } from '../../util/client.js';\nimport { getGlobalConfig } from '../../util/config.js';\nimport { RushError } from '../../util/errors.js';\nimport { requireAuth } from '../../util/require-auth.js';\nimport { buildDeployUrl } from '../../util/rush-domain.js';\n\n/**\n * Allowed domain-prefix shape per spec §5 / §\"CLI execution flow — task\n * domain check\": 1-30 lowercase letters/digits/hyphens, starting and\n * ending with a letter or digit. Single-char aliases (`^[a-z0-9]$`) are\n * explicitly allowed.\n *\n * The server runs the same regex — we pre-check client-side to avoid a\n * round-trip and give a more pointed error message.\n */\nconst DOMAIN_PREFIX_REGEX = /^[a-z0-9][a-z0-9-]{0,28}[a-z0-9]$|^[a-z0-9]$/;\n\nexport function validateDomainPrefix(prefix: string): void {\n if (!DOMAIN_PREFIX_REGEX.test(prefix)) {\n throw new RushError(\n `Invalid domain prefix: \"${prefix}\". Use 1-30 lowercase letters/digits/hyphens; must start and end with a letter or digit.`,\n { reason: 'invalid_domain_prefix', domain: prefix }\n );\n }\n}\n\ninterface DomainCheckResponse {\n available: boolean;\n conflictProjectId?: string;\n}\n\ninterface DomainCheckCliOptions {\n task: string;\n}\n\nexport function registerDomainSubcommand(\n task: Command,\n program: Command\n): void {\n const domain = task\n .command('domain')\n .description('Inspect and validate custom domain prefixes for a task');\n\n domain\n .command('check')\n .description(\n 'Validate a domain prefix and confirm availability for a web-builder task'\n )\n .argument(\n '<prefix>',\n 'Domain prefix (1-30 lowercase letters/digits/hyphens)'\n )\n .requiredOption('--task <id>', 'Web-builder task ID to scope the check')\n .action(async (prefix: string, opts: DomainCheckCliOptions) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n // Step 2: client-side regex pre-check (spec §5 / §\"task domain check\" step 2)\n validateDomainPrefix(prefix);\n\n // Step 3: web-builder gating via the shared helper\n await resolveWebBuilderTask(client, opts.task);\n\n // Step 4: upstream availability check\n const { data } = await client.get<DomainCheckResponse>(\n `/api/projects/check-domain-prefix?prefix=${encodeURIComponent(\n prefix\n )}&projectId=${encodeURIComponent(opts.task)}`\n );\n\n // Step 5: emit\n if (format === 'json') {\n output.log(\n JSON.stringify(\n {\n prefix,\n taskId: opts.task,\n available: data.available,\n conflictProjectId: data.conflictProjectId ?? null,\n },\n null,\n 2\n )\n );\n if (!data.available) {\n // Non-fatal in --json mode: consumer parses the payload. But the\n // exit-code contract (RushError = exit 2) must still hold so CI\n // wrappers can detect the conflict branch.\n throw new RushError(\n `Domain prefix \"${prefix}\" is taken${\n data.conflictProjectId\n ? ` by project ${data.conflictProjectId}`\n : ''\n }.`,\n {\n reason: 'domain_taken',\n domain: prefix,\n conflictProjectId: data.conflictProjectId,\n }\n );\n }\n return;\n }\n\n if (data.available) {\n // Derive the actual deploy URL from the configured API base — the\n // availability endpoint itself doesn't return a suffix, but we know\n // it from the profile's `api` (config.ts) by the same host→suffix\n // rule the web UI's `buildUrl` uses. This prints a clickable URL\n // instead of a literal `<suffix>` placeholder.\n const { api } = getGlobalConfig();\n const url = buildDeployUrl(prefix, 'production', api);\n output.success(`Prefix available: ${url}`);\n } else {\n throw new RushError(\n `Domain prefix \"${prefix}\" is taken${\n data.conflictProjectId\n ? ` by project ${data.conflictProjectId}`\n : ''\n }.`,\n {\n reason: 'domain_taken',\n domain: prefix,\n conflictProjectId: data.conflictProjectId,\n }\n );\n }\n });\n}\n\n// Test seam so unit tests can exercise the regex constant directly without\n// round-tripping through commander.\nexport { DOMAIN_PREFIX_REGEX };\n","import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { Command } from 'commander';\nimport { resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport { isCIMode } from '../../util/ci.js';\nimport { createClient } from '../../util/client.js';\nimport {\n ensureRushGitignore,\n isCheckpointState,\n type RushEnv,\n readEnvFile,\n writeEnvFile,\n writeHandoffFile,\n} from '../../util/env-file.js';\nimport { RushError } from '../../util/errors.js';\nimport { requireAuth } from '../../util/require-auth.js';\n\n/**\n * `task link` provisions the remote pieces that a local project needs to\n * run on Rush (pod + optional Supabase DB + optional Rush-proxied object\n * storage) and writes everything the calling IDE agent needs to finish\n * wiring up locally into `.rush/env.md` + `.rush/agent-handoff.md`.\n *\n * The command is non-atomic and **resumable**: each successful remote\n * call flips a STATE checkpoint in `.rush/env.md` so a re-run after a\n * failure picks up from the last good point rather than re-provisioning\n * resources that already exist.\n *\n * Contract surface (see `specs/rush-v2/cli/roadmap.spec.md` §M3 / §M4,\n * and `specs/rush-v2/cli/task-storage-api.spec.md`):\n * - Never mutates user code; artifacts live in `.rush/` only.\n * - Never writes logs, error stacks, or telemetry into `.rush/env.md`.\n * - Auto-appends `.rush/` to `.gitignore` (idempotent).\n * - The --oss flag records a `STORAGE_API_BASE` pointing at the\n * Rush-proxied object storage API. Authentication reuses the existing\n * `RUSH_PLATFORM_TOKEN` env var (server-side only); no per-project\n * token is minted and `.rush/env.md` never stores a bearer secret.\n *\n * History: renamed from `task init` in 0.10.0 (issue #1150). `init`\n * remains a commander alias until 0.13.0; invoking via the alias prints a\n * one-time deprecation warning to stderr, suppressed in JSON / CI mode so\n * machine-readable pipes stay clean.\n */\n\n/**\n * Detect whether the user invoked this command via the deprecated `init`\n * alias rather than the canonical `link` name.\n *\n * Primary signal: the `task` command's `args` (commander populates\n * `command.args` with the operands + unknown tokens). The first non-flag\n * token is the subcommand name the user typed (pre-resolution).\n *\n * Fallback signal: scan `rawArgv` for every `'task'` occurrence and\n * inspect the next non-flag token. We iterate candidates because a\n * global option value could happen to equal `'task'` (e.g. an upstream\n * `--profile task`); the real subcommand is the `task` followed by\n * `init` or `link`. The fallback guards against commander internals\n * differing across versions / environments (observed CI vs local\n * divergence where `parent.args` came in empty under concurrent test\n * load, losing the `init` token before our action ran).\n *\n * We cannot rely on `thisCommand.name()` (commander reports the\n * canonical name regardless of invocation) nor on a hypothetical \"used\n * alias\" API — it does not exist in commander 13.\n *\n * `rawArgv` defaults to an empty array so unit tests can assert the\n * primary-signal behavior without the test process's argv leaking in.\n * Production call sites pass `process.argv` explicitly.\n */\nexport function invokedViaInitAlias(\n parentArgs: readonly string[],\n rawArgv: readonly string[] = []\n): boolean {\n // Primary: parent.args first non-flag token.\n for (const token of parentArgs) {\n if (!token || token.startsWith('-')) continue;\n if (token === 'init') return true;\n if (token === 'link') return false;\n // Any other first operand is unexpected — fall through to argv scan\n // rather than guessing.\n break;\n }\n\n // Fallback: find every `task` in argv and inspect the next non-flag\n // token. A hit on `init` or `link` wins; anything else keeps scanning\n // (handles `--some-option task` value collisions).\n for (let i = 0; i < rawArgv.length; i += 1) {\n if (rawArgv[i] !== 'task') continue;\n for (let j = i + 1; j < rawArgv.length; j += 1) {\n const token = rawArgv[j];\n if (!token || token.startsWith('-')) continue;\n if (token === 'init') return true;\n if (token === 'link') return false;\n // Some other operand — likely `--profile task othervalue`; keep\n // looking for the real `task init|link` pair.\n break;\n }\n }\n return false;\n}\n\nconst DEPRECATION_WARNING =\n '`task init` is renamed to `task link`. Support for `init` will be removed in 0.13.0.';\n\ninterface TaskDeployResponse {\n id: string;\n status: string;\n gitPushUrl: string;\n chatUrl: string;\n previewUrl: string | null;\n}\n\ninterface DatabaseCreateResponse {\n success: boolean;\n action?: string;\n databaseKey?: string;\n supabaseUrl?: string;\n postgresUrl?: string;\n message?: string;\n error?: string;\n}\n\ninterface DetectedNeeds {\n db: {\n hasEnvLocal: boolean;\n hasSupabaseMigrations: boolean;\n suggestion: boolean;\n };\n oss: {\n publicDirSizeKB: number;\n largeAssetCount: number;\n suggestion: boolean;\n };\n}\n\nconst PUBLIC_ASSET_THRESHOLD_KB = 50; // per-file threshold; >50KB counts as a candidate\nconst PUBLIC_DIR_OSS_HINT_KB = 500; // total public/ size that tips us toward suggesting OSS\n\ninterface LinkCliOptions {\n db?: boolean;\n oss?: boolean;\n name?: string;\n path: string;\n yes?: boolean;\n json?: boolean;\n}\n\nexport function registerLinkSubcommand(task: Command, program: Command): void {\n task\n .command('link')\n // Deprecated alias — to be removed in 0.13.0. See `invokedViaInitAlias`\n // above for the runtime warning path; commander itself treats the alias\n // as fully transparent (same help, same flags, same action).\n .alias('init')\n .description(\n 'Link a local codebase to Rush (pod + optional DB + optional OSS token) and produce .rush/env.md + .rush/agent-handoff.md'\n )\n .option('--db', 'Create a Supabase database for this project')\n .option('--oss', 'Issue an OSS upload token for this project')\n .option('-n, --name <slug>', 'Project name (defaults to directory name)')\n .option('-p, --path <dir>', 'Project directory', '.')\n .option('-y, --yes', 'Skip interactive prompts; accept detected defaults')\n .action(async (opts: LinkCliOptions, thisCommand: Command) => {\n const format = resolveFormat(program.opts());\n\n // Deprecation warning for users still invoking `task init`. Printed\n // BEFORE `requireAuth` so users see it even when the auth check\n // short-circuits — otherwise an unauthenticated user on the alias\n // path would never learn the name is changing. Gated on JSON / CI\n // mode so machine-readable pipes stay clean (the warning is\n // advisory, not a behavioral change). Stderr-only via `output.warn`.\n const parentArgs = thisCommand.parent?.args ?? [];\n // Commander's top-level `program.rawArgs` preserves the literal\n // argv that was passed to `parseAsync`, even in environments where\n // `command.parent.args` is trimmed by the time the action runs\n // (observed in CI under concurrent turbo+vitest workers). Using it\n // as the fallback instead of `process.argv` makes the signal\n // immune to test-worker argv interference.\n const rawArgv = (program as unknown as { rawArgs?: string[] }).rawArgs;\n if (\n invokedViaInitAlias(parentArgs, rawArgv ?? []) &&\n format !== 'json' &&\n !isCIMode(program.opts())\n ) {\n output.warn(DEPRECATION_WARNING);\n }\n\n requireAuth();\n const projectPath = path.resolve(opts.path);\n\n // Step 0: Read any existing `.rush/env.md`. A partially-provisioned\n // project (crashed mid-run) re-enters through this path.\n const existing = readEnvFile(projectPath);\n const resumed = Boolean(existing.PROJECT_ID);\n if (resumed && existing.STATE && !isCheckpointState(existing.STATE)) {\n throw new RushError(\n `.rush/env.md has an invalid STATE='${existing.STATE}'. Delete the file to start fresh or fix it manually.`,\n { state: existing.STATE }\n );\n }\n\n const detected = detectNeeds(projectPath);\n const { wantDb, wantOss } = resolveWants(\n opts,\n detected,\n !!opts.json || format === 'json'\n );\n\n if (format !== 'json') {\n if (resumed) {\n output.info(\n `Resuming \\`task link\\` for project ${existing.PROJECT_ID} (state=${existing.STATE ?? 'unknown'})`\n );\n } else {\n output.info('Provisioning Rush project...');\n }\n }\n\n // Snapshot of what we'll do; written back to env.md as checkpoints advance.\n const env: RushEnv = { ...existing };\n\n // Step 1 — Pod. If already provisioned (STATE >= pod-only), skip.\n if (!env.PROJECT_ID) {\n const projectName =\n opts.name ??\n path.basename(projectPath) ??\n `rush-${Date.now().toString(36)}`;\n const client = createClient();\n const { data: created } = await client.post<TaskDeployResponse>(\n '/api/tasks/deploy',\n { name: projectName }\n );\n if (!created.id || !created.gitPushUrl) {\n throw new RushError(\n 'Rush API did not return a project id / git push URL; cannot continue.'\n );\n }\n\n env.PROJECT_ID = created.id;\n env.PREVIEW_URL = created.previewUrl ?? undefined;\n // Persist the unmasked git push URL so `task push` can reuse the\n // project without re-minting a deploy token. `.rush/env.md` is\n // gitignored and DO-NOT-commit-branded; the file is the sole\n // on-disk store of runtime credentials (DB URL, OSS token, etc.)\n // and the embedded git token is no different. JSON output +\n // terminal display still mask it via `maskToken` below.\n env.GIT_REMOTE = created.gitPushUrl;\n env.STATE = 'pod-only';\n writeEnvFile(projectPath, env);\n ensureRushGitignore(projectPath);\n if (format !== 'json') output.success(`Project created: ${created.id}`);\n }\n\n // After step 1 we must have a projectId — either from resume or from\n // the just-completed create. The explicit guard narrows the type and\n // keeps TypeScript (and biome's noNonNullAssertion rule) happy for\n // the rest of the function without scattering `!` everywhere.\n if (!env.PROJECT_ID) {\n throw new RushError(\n 'Internal error: PROJECT_ID unset after provisioning step.'\n );\n }\n const projectId = env.PROJECT_ID;\n\n // Step 2 — DB (optional). The DB endpoint is idempotent on the pod\n // side (workflow short-circuits if a DB already exists), so re-running\n // after a pod-only checkpoint is safe. We gate on \"user asked for DB\n // AND we haven't stored DB creds yet\" rather than on STATE — STATE\n // can be `complete` from a prior pod-only run, and we want\n // `task link --db` to still provision the DB in that case.\n const alreadyHasDbCreds = Boolean(env.DATABASE_URL || env.SUPABASE_URL);\n if (wantDb && !alreadyHasDbCreds) {\n if (format !== 'json') output.info('Creating Supabase database...');\n const client = createClient();\n const { data: db } = await client.post<DatabaseCreateResponse>(\n `/api/projects/${encodeURIComponent(projectId)}/database/create`,\n {}\n );\n if (!db.success && db.action !== 'skipped') {\n throw new RushError(\n `Database create failed: ${db.error ?? db.message ?? 'unknown'}`,\n { projectId: env.PROJECT_ID }\n );\n }\n if (db.postgresUrl) env.DATABASE_URL = db.postgresUrl;\n if (db.supabaseUrl) env.SUPABASE_URL = db.supabaseUrl;\n env.STATE = 'pod-db';\n writeEnvFile(projectPath, env);\n if (format !== 'json') output.success('Database ready');\n } else if (!wantDb && env.STATE === 'pod-only') {\n // User didn't ask for DB → advance the checkpoint past the DB stage\n // so OSS provisioning (if requested) can run next time without\n // re-entering the DB branch.\n env.STATE = 'pod-db';\n writeEnvFile(projectPath, env);\n }\n\n // Step 3 — Rush-proxied storage (optional). No server round-trip: the\n // storage API is unconditionally available for any existing task, and\n // authentication reuses the existing Platform Token. We just record\n // the endpoint in env.md so the IDE agent can point the app at it.\n if (wantOss && !env.STORAGE_API_BASE) {\n env.STORAGE_API_BASE = storageApiBaseForProject(projectId);\n env.STATE = 'pod-db-oss';\n writeEnvFile(projectPath, env);\n if (format !== 'json') output.success('Storage API endpoint recorded');\n } else if (!wantOss && env.STATE === 'pod-db') {\n env.STATE = 'pod-db-oss';\n writeEnvFile(projectPath, env);\n }\n\n // Step 4 — complete\n env.STATE = 'complete';\n writeEnvFile(projectPath, env);\n ensureRushGitignore(projectPath);\n\n // Step 5 — handoff for the IDE agent\n writeHandoffFile(\n projectPath,\n renderHandoff({\n projectId: projectId,\n previewUrl: env.PREVIEW_URL,\n db: wantDb\n ? {\n databaseUrl: env.DATABASE_URL,\n supabaseUrl: env.SUPABASE_URL,\n }\n : null,\n storage: wantOss\n ? {\n storageApiBase:\n env.STORAGE_API_BASE ?? storageApiBaseForProject(projectId),\n }\n : null,\n detected,\n })\n );\n\n const result = {\n projectId: projectId,\n previewUrl: env.PREVIEW_URL ?? null,\n state: env.STATE,\n db: wantDb\n ? {\n databaseUrl: env.DATABASE_URL ?? null,\n supabaseUrl: env.SUPABASE_URL ?? null,\n }\n : null,\n storage: wantOss\n ? {\n storageApiBase:\n env.STORAGE_API_BASE ?? storageApiBaseForProject(projectId),\n }\n : null,\n files: {\n env: path.join(projectPath, '.rush', 'env.md'),\n handoff: path.join(projectPath, '.rush', 'agent-handoff.md'),\n },\n };\n\n if (format === 'json') {\n output.log(JSON.stringify(result, null, 2));\n return;\n }\n\n output.newline();\n output.success('Link complete');\n output.log(` ${chalk.bold('Project ID:')} ${result.projectId}`);\n if (result.previewUrl) {\n output.log(` ${chalk.bold('Preview:')} ${result.previewUrl}`);\n }\n output.log(` ${chalk.bold('Env file:')} ${result.files.env}`);\n output.log(` ${chalk.bold('Handoff:')} ${result.files.handoff}`);\n output.newline();\n output.dim(\n 'Next: read .rush/agent-handoff.md and apply the local changes it lists.'\n );\n });\n}\n\n/** Decide what to provision given CLI flags + detection results. In\n * non-interactive mode (JSON / no TTY / --yes) explicit flags win and\n * detection is only used to populate the handoff. Interactive sessions\n * default to detection-driven but do not currently prompt via readline\n * — we favor explicit flags to keep the command reproducible. */\nexport function resolveWants(\n opts: LinkCliOptions,\n detected: DetectedNeeds,\n jsonMode: boolean\n): { wantDb: boolean; wantOss: boolean } {\n // Explicit flags always win.\n if (opts.db !== undefined || opts.oss !== undefined) {\n return {\n wantDb: !!opts.db,\n wantOss: !!opts.oss,\n };\n }\n // Non-interactive: don't guess. Be conservative — provision only the\n // pod so re-run with an explicit flag is predictable.\n if (jsonMode || !process.stdout.isTTY || opts.yes) {\n return { wantDb: false, wantOss: false };\n }\n // Interactive fallback without readline: surface detection hints and\n // let the user re-run with flags. This is the safest default; we can\n // wire up a proper prompt in a follow-up.\n if (detected.db.suggestion || detected.oss.suggestion) {\n output.newline();\n output.info('Detection hints:');\n if (detected.db.suggestion) {\n output.log(\n ' • .env / supabase/migrations found — consider `--db` to provision a Supabase database.'\n );\n }\n if (detected.oss.suggestion) {\n output.log(\n ` • public/ has ${detected.oss.largeAssetCount} asset(s) (${detected.oss.publicDirSizeKB} KB total) — consider \\`--oss\\` to move them behind signed uploads.`\n );\n }\n output.dim(\n 'Re-run `rush-ai task link` with `--db` / `--oss` to include those steps.'\n );\n output.newline();\n }\n return { wantDb: false, wantOss: false };\n}\n\nexport function detectNeeds(projectPath: string): DetectedNeeds {\n // DB signals: `.env.local` / `.env` with DATABASE_URL or SUPABASE vars,\n // or a `supabase/migrations/` directory.\n const envFiles = ['.env.local', '.env', '.env.development'];\n let hasEnvLocal = false;\n for (const f of envFiles) {\n const p = path.join(projectPath, f);\n if (!existsSync(p)) continue;\n try {\n const content = readFileSync(p, 'utf8');\n if (\n /\\b(DATABASE_URL|SUPABASE_URL|POSTGRES_URL|USER_POSTGRESQL_URL)\\s*=/.test(\n content\n )\n ) {\n hasEnvLocal = true;\n break;\n }\n } catch {\n // unreadable — ignore\n }\n }\n const hasSupabaseMigrations = existsSync(\n path.join(projectPath, 'supabase', 'migrations')\n );\n\n // OSS signals: large / many files in public/.\n const publicDir = path.join(projectPath, 'public');\n let publicDirSizeKB = 0;\n let largeAssetCount = 0;\n if (existsSync(publicDir)) {\n try {\n for (const entry of walk(publicDir, 3)) {\n const st = statSync(entry);\n if (!st.isFile()) continue;\n const kb = Math.round(st.size / 1024);\n publicDirSizeKB += kb;\n if (kb > PUBLIC_ASSET_THRESHOLD_KB) largeAssetCount += 1;\n }\n } catch {\n // permission problem — treat as no signal\n }\n }\n\n return {\n db: {\n hasEnvLocal,\n hasSupabaseMigrations,\n suggestion: hasEnvLocal || hasSupabaseMigrations,\n },\n oss: {\n publicDirSizeKB,\n largeAssetCount,\n suggestion:\n publicDirSizeKB >= PUBLIC_DIR_OSS_HINT_KB || largeAssetCount >= 3,\n },\n };\n}\n\nfunction* walk(dir: string, depth: number): Generator<string> {\n if (depth < 0) return;\n let entries: string[] = [];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const name of entries) {\n const p = path.join(dir, name);\n let st: ReturnType<typeof statSync>;\n try {\n st = statSync(p);\n } catch {\n continue;\n }\n if (st.isDirectory()) {\n yield* walk(p, depth - 1);\n } else {\n yield p;\n }\n }\n}\n\ninterface HandoffData {\n projectId: string;\n previewUrl?: string;\n db: { databaseUrl?: string; supabaseUrl?: string } | null;\n storage: {\n storageApiBase: string;\n } | null;\n detected: DetectedNeeds;\n}\n\n/** Storage API base URL for the given taskId. Kept centralized so tests and\n * handoff rendering agree on a single format. Host defaults to the shared Rush\n * host; future work can override via a client config if we ever need to point\n * at a staging deployment. */\nexport function storageApiBaseForProject(taskId: string): string {\n const host = 'https://rush.zhenguanyu.com';\n return `${host}/api/tasks/${encodeURIComponent(taskId)}/storage`;\n}\n\nexport function renderHandoff(data: HandoffData): string {\n const lines: string[] = [];\n lines.push('# Rush Agent Handoff');\n lines.push('');\n lines.push(\n `This file lists the local code changes needed to finish wiring up project \\`${data.projectId}\\` to the Rush-provisioned backend. Apply them in order. When you're done, run \\`rush-ai task push\\` to validate.`\n );\n lines.push('');\n lines.push(\n '> Credentials needed below live in `.rush/env.md` (never commit).'\n );\n lines.push('');\n lines.push('## Project');\n lines.push('');\n lines.push(`- Project ID: \\`${data.projectId}\\``);\n if (data.previewUrl) lines.push(`- Preview URL: ${data.previewUrl}`);\n lines.push('');\n\n lines.push('## Checklist');\n lines.push('');\n\n if (data.db) {\n lines.push('### Database');\n lines.push('');\n lines.push(\n '- [ ] Point the app at the new DB: replace `DATABASE_URL` / `SUPABASE_URL` in `.env.local` with the values from `.rush/env.md`.'\n );\n if (data.detected.db.hasSupabaseMigrations) {\n lines.push(\n '- [ ] Run `supabase/migrations/*.sql` against the new database (or `supabase db push` if the CLI is wired up).'\n );\n } else {\n lines.push(\n '- [ ] Create the schema you need — no migrations were detected in `supabase/migrations/`.'\n );\n }\n lines.push(\n '- [ ] (Optional) Migrate any existing local data to the new database.'\n );\n lines.push('');\n }\n\n if (data.storage) {\n lines.push('### File uploads (Rush-proxied object storage)');\n lines.push('');\n lines.push(\n `- [ ] Route uploads through the Rush storage proxy at \\`${data.storage.storageApiBase}\\` (\\`POST /upload\\`, \\`GET /object/<key>\\`, \\`DELETE /object/<key>\\`, \\`GET /list\\`).`\n );\n lines.push(\n '- [ ] **Double-hop pattern** (required): browser → user-app server route → rush-app storage API. Only the user-app server holds `RUSH_PLATFORM_TOKEN`; the browser must never carry it.'\n );\n lines.push('');\n lines.push(' Example (Next.js server route inside your app):');\n lines.push('');\n lines.push(' ```ts');\n lines.push(' // app/api/upload/route.ts');\n lines.push(' export async function POST(req: Request) {');\n lines.push(' const formData = await req.formData();');\n lines.push(\n ' const rushRes = await fetch(`${process.env.STORAGE_API_BASE}/upload`, {'\n );\n lines.push(' method: \"POST\",');\n lines.push(\n ' headers: { Authorization: `Bearer ${process.env.RUSH_PLATFORM_TOKEN}` },'\n );\n lines.push(' body: formData,');\n lines.push(' });');\n lines.push(' return Response.json(await rushRes.json());');\n lines.push(' }');\n lines.push(' ```');\n lines.push('');\n lines.push(\n '- [ ] Display uploaded files directly via `<img src={url} />` — the returned URL is already the Rush-hosted proxy endpoint.'\n );\n if (data.detected.oss.suggestion) {\n lines.push(\n `- [ ] (Optional) Move the ${data.detected.oss.largeAssetCount} large asset(s) under \\`public/\\` into the storage proxy so they load from the Rush-hosted endpoint.`\n );\n }\n lines.push('');\n }\n\n lines.push('### Verify');\n lines.push('');\n lines.push(\n '- [ ] `rush-ai task push` — the CLI will reuse `PROJECT_ID` from `.rush/env.md`, skip pod provisioning, and push the edited code.'\n );\n lines.push(\n '- [ ] Open the preview URL and exercise DB / upload paths end-to-end.'\n );\n lines.push('');\n\n lines.push('## Constraints');\n lines.push('');\n lines.push(\n '- `.rush/env.md` must never be committed. `.rush/` is in `.gitignore`.'\n );\n lines.push(\n '- Pushed projects use `template = null` and **cannot** go through `rush-ai task deploy` / custom domains. The preview URL is all you get.'\n );\n if (data.storage) {\n lines.push(\n '- Platform Token (`RUSH_PLATFORM_TOKEN`) is server-side only. Never expose it to the browser or ship it in a public bundle.'\n );\n lines.push(\n '- Uploaded objects are publicly readable via their storage URL; do not upload secrets / private data through the proxy.'\n );\n }\n lines.push('');\n\n return lines.join('\\n');\n}\n","import type { Command } from 'commander';\nimport { formatOutput, resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport { resolveWebBuilderTask } from '../../util/api-task.js';\nimport { createClient } from '../../util/client.js';\nimport { RushError } from '../../util/errors.js';\nimport { requireAuth } from '../../util/require-auth.js';\n\n/**\n * Single entry in `GET /api/version/list?projectId=<id>`.\n *\n * `deployedToTest` / `deployedToProduction` are **authoritative** — spec\n * §\"Version-list derived fields\" explicitly forbids computing these by\n * comparing against `task.deployed_*_version_id`, because the task API\n * only exposes `deployedProductionUrl` for web-builder tasks and can\n * lag the version list. This also keeps `versions` untouched by\n * deploy-version write failures (deploy-version is best-effort — see\n * spec §8).\n */\ninterface VersionEntry {\n id: string;\n versionNumber?: number | string;\n /**\n * ISO 8601 timestamp of the commit. The API returns this as `timestamp`\n * (see `packages/shared/src/types/version.ts` — `Version.timestamp`).\n * We also accept `createdAt` as a fallback for future API revisions or\n * self-hosted variants.\n */\n timestamp?: string;\n createdAt?: string;\n deployedToTest?: boolean;\n deployedToProduction?: boolean;\n [key: string]: unknown;\n}\n\ninterface VersionListResponse {\n success?: boolean;\n versions?: VersionEntry[];\n}\n\ninterface VersionsCliOptions {\n last?: string;\n}\n\nconst MARK_YES = '✓';\nconst MARK_NO = '';\n\nexport function registerVersionsSubcommand(\n task: Command,\n program: Command\n): void {\n task\n .command('versions')\n .description('List buildable versions for a web-builder task')\n .argument('<id>', 'Task ID')\n .option(\n '--last <n>',\n 'Show only the last N versions (after sort, preserving order)'\n )\n .action(async (id: string, opts: VersionsCliOptions) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n // Web-builder gate — same helper as `task deploy` / `task domain check`.\n await resolveWebBuilderTask(client, id);\n\n const { data } = await client.get<VersionListResponse>(\n `/api/version/list?projectId=${encodeURIComponent(id)}`\n );\n\n const versions = data.versions ?? [];\n const clamped = clampToLast(versions, opts.last);\n\n if (format === 'json') {\n output.log(JSON.stringify(clamped, null, 2));\n return;\n }\n\n if (clamped.length === 0) {\n output.info(\n 'No versions yet. Send a message to the task to produce a buildable commit.'\n );\n return;\n }\n\n const rows = clamped.map((v, idx) => ({\n '#': String(idx + 1),\n Version: v.versionNumber != null ? String(v.versionNumber) : '',\n Commit: shortHash(v.id),\n Created: formatCreatedAt(v.timestamp ?? v.createdAt),\n Test: v.deployedToTest ? MARK_YES : MARK_NO,\n Production: v.deployedToProduction ? MARK_YES : MARK_NO,\n }));\n\n output.log(formatOutput(rows, format));\n });\n}\n\n/**\n * Trim to the trailing N entries. Preserves the list's original order\n * (the API returns oldest→newest; `--last` just slices the tail).\n *\n * Spec §\"CLI execution flow — task versions <id>\" step 5: \"truncates to\n * last n entries (after sort, preserving order)\".\n */\nexport function clampToLast<T>(\n items: readonly T[],\n lastOpt: string | undefined\n): T[] {\n if (lastOpt === undefined) return [...items];\n const n = Number.parseInt(lastOpt, 10);\n if (!Number.isFinite(n) || n <= 0) {\n throw new RushError(\n `Invalid --last value: \"${lastOpt}\" (expected positive integer).`,\n { reason: 'invalid_last', last: lastOpt }\n );\n }\n if (n >= items.length) return [...items];\n return items.slice(items.length - n);\n}\n\nfunction shortHash(hash: string): string {\n if (!hash) return '';\n return hash.length > 8 ? hash.slice(0, 8) : hash;\n}\n\nfunction formatCreatedAt(ts: string | undefined): string {\n if (!ts) return '';\n const date = new Date(ts);\n if (Number.isNaN(date.getTime())) return ts;\n return date.toLocaleString();\n}\n","import type { Command } from 'commander';\nimport { registerAgentCommand } from './agent/index.js';\nimport { registerAuthCommand } from './auth/index.js';\nimport { registerCheckCommand } from './check/index.js';\nimport { registerCompletionCommand } from './completion/index.js';\nimport { registerConfigCommand } from './config/index.js';\nimport { registerDoctorCommand } from './doctor/index.js';\nimport { registerMarketplaceCommand } from './marketplace/index.js';\nimport { registerMcpCommand } from './mcp/index.js';\nimport { registerPluginCommand } from './plugin/index.js';\nimport { registerSkillCommand } from './skill/index.js';\nimport { registerTaskCommand } from './task/index.js';\n\nexport function registerCommands(program: Command): void {\n registerAuthCommand(program);\n registerAgentCommand(program);\n registerTaskCommand(program);\n registerSkillCommand(program);\n registerCheckCommand(program);\n registerMcpCommand(program);\n registerMarketplaceCommand(program);\n registerPluginCommand(program);\n registerCompletionCommand(program);\n registerConfigCommand(program);\n registerDoctorCommand(program);\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { output } from '../output/logger.js';\n\nconst CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;\nconst FETCH_TIMEOUT_MS = 3000;\nconst REGISTRY_URL = 'https://registry.npmjs.org/rush-ai/latest';\n\nfunction parseVersion(v: string): number[] | null {\n const segments = v.replace(/^v/, '').split('-')[0].split('.').map(Number);\n if (segments.some((n) => Number.isNaN(n))) return null;\n return segments;\n}\n\nfunction isNewerVersion(current: string, latest: string): boolean {\n const c = parseVersion(current);\n const l = parseVersion(latest);\n if (!c || !l) return false;\n for (let i = 0; i < Math.max(c.length, l.length); i++) {\n const cv = c[i] ?? 0;\n const lv = l[i] ?? 0;\n if (lv > cv) return true;\n if (lv < cv) return false;\n }\n return false;\n}\n\nasync function writeLastCheck(checkFile: string): Promise<void> {\n try {\n await mkdir(dirname(checkFile), { recursive: true });\n await writeFile(checkFile, JSON.stringify({ lastCheck: Date.now() }));\n } catch {\n // Truly silent — filesystem may not be writable\n }\n}\n\nexport async function checkForUpdate(currentVersion: string): Promise<void> {\n const rushDir = join(homedir(), '.rush');\n const checkFile = join(rushDir, 'update-check.json');\n\n try {\n if (process.env.CI) return;\n\n let lastCheck = 0;\n try {\n const data = JSON.parse(await readFile(checkFile, 'utf-8'));\n lastCheck = data.lastCheck ?? 0;\n } catch {\n // Missing or corrupt — will check\n }\n\n if (Date.now() - lastCheck < CHECK_INTERVAL_MS) return;\n\n // Write lastCheck immediately to prevent retry storms on failure\n await writeLastCheck(checkFile);\n\n const response = await fetch(REGISTRY_URL, {\n signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),\n });\n if (!response.ok) return;\n\n const { version: latest } = (await response.json()) as {\n version: string;\n };\n\n if (isNewerVersion(currentVersion, latest)) {\n output.newline();\n output.warn(\n `Update available: ${currentVersion} → ${latest}\\n` +\n ` Run \\`npm update -g rush-ai\\` to update.`\n );\n }\n } catch {\n // Silent — never block CLI usage.\n // lastCheck was already written above, so next invocation won't retry.\n }\n}\n"],"mappings":"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","names":["chalk","page","data","totalPages","truncate","resolve","chalk","existsSync","readFileSync","join","execFileSync","existsSync","readFileSync","join","existsSync","join","execFileSync","join","existsSync","readFileSync","unignored","existsSync","join","existsSync","readFileSync","join","existsSync","readFileSync","join","VITE_CONFIG_FILES","file","devScript","chalk","existsSync","join","existsSync","join","chalk","existsSync","dirExists","existsSync","execFileSync","detectShell","execFileSync","existsSync","readFileSync","homedir","resolve","resolve","homedir","existsSync","readFileSync","chalk","displayResults","fsConstants","access","mkdir","readFile","rm","writeFile","homedir","dirname","resolve","resolve","readFile","resolve","path","resolve","readFile","path","resolve","fsConstants","access","resolve","resolve","access","fsConstants","resolve","homedir","mkdir","dirname","pathExists","rm","readFile","access","fsConstants","writeFile","randomUUID","fsConstants","access","mkdir","readFile","rename","rm","writeFile","homedir","dirname","resolve","pathExists","stats","runUpdate","truncate","randomUUID","mkdir","readdir","readFile","rename","rm","stat","homedir","dirname","join","resolve","execFileSync","isAbsolute","resolve","homedir","mkdir","resolve","join","rm","dirname","randomUUID","rename","path","readFile","stat","readdir","fsConstants","access","mkdir","readdir","readFile","rename","rm","stat","writeFile","homedir","basename","dirname","resolve","isAbsolute","RUSH_AI_PLUGIN_NAME","RUSH_AI_MARKETPLACE_NAME","RUSH_MCP_SERVER_KEY","randomUUID","fsConstants","access","copyFile","mkdir","readFile","rename","rm","stat","writeFile","dirname","pathExists","atomicWrite","homedir","pathExists","parsePluginKey","rm","mkdir","resolve","readFile","dirname","basename","rename","file","readdir","walk","writeFile","access","fsConstants","stat","randomUUID","fsConstants","access","cp","mkdir","readFile","rename","rm","stat","writeFile","homedir","dirname","resolve","randomUUID","fsConstants","access","mkdir","readFile","rename","rm","stat","writeFile","dirname","pathExists","resolve","readFile","resolve","resolve","readFile","rm","mkdir","dirname","randomUUID","writeFile","rename","stat","homedir","dirExists","readFile","resolve","cp","path","readdir","access","fsConstants","lstat","rm","unlink","homedir","resolve","access","lstat","readFile","readlink","stat","homedir","isAbsolute","resolve","pathExists","resolve","rm","path","lstat","unlink","homedir","appendFile","mkdir","homedir","dirname","resolve","access","readFile","homedir","isAbsolute","pathRelative","pathResolve","fsConstants","access","readdir","stat","extname","resolve","dirExists","fileExists","walk","fsConstants","access","readFile","resolve","resolve","access","fsConstants","readFile","RUSH_AI_PLUGIN_NAME","RUSH_AI_MARKETPLACE_NAME","RUSH_MCP_SERVER_KEY","isAbsolute","pathResolve","pathRelative","defaultRushAiBinaryResolver","homedir","RUSH_AI_PLUGIN_NAME","RUSH_AI_MARKETPLACE_NAME","errorMessage","access","readFile","runList","registerListCommand","statusIcon","formatRegistryError","targetDisplayName","runUpdate","registerUpdateCommand","registerListCommand","registerUpdateCommand","existsSync","readFileSync","basename","resolve","resolve","existsSync","basename","file","readFileSync","mkdir","readFile","stat","path","chalk","resolve","resolve","chalk","existsSync","mkdirSync","readFileSync","writeFileSync","file","resolve","chalk","resolve","DOMAIN_PREFIX_REGEX","validateDomainPrefix","existsSync","readdirSync","readFileSync","path","chalk","path","chalk","existsSync","readFileSync","readdirSync","shortHash","path","file","mkdir","stat","readFile","mkdir","readFile","writeFile","homedir","dirname","join","mkdir","dirname","writeFile","join","homedir","readFile","chalk"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/output/formatters/csv.ts","../src/output/formatters/table.ts","../src/output/formatters/index.ts","../src/util/ci.ts","../src/util/require-auth.ts","../src/commands/agent/index.ts","../src/commands/auth/browser-login.ts","../src/commands/auth/index.ts","../src/commands/check/index.ts","../src/commands/check/checks/database.ts","../src/commands/check/checks/framework.ts","../src/commands/check/checks/gitignore.ts","../src/util/git.ts","../src/commands/check/checks/lock-file.ts","../src/commands/check/checks/package-json.ts","../src/commands/check/checks/port-env.ts","../src/commands/completion/index.ts","../src/commands/config/index.ts","../src/commands/doctor/index.ts","../src/commands/doctor/checks/auth.ts","../src/commands/doctor/checks/config.ts","../src/commands/doctor/checks/connectivity.ts","../src/commands/doctor/checks/environment.ts","../src/commands/doctor/checks/plugins.ts","../src/marketplaces/cache.ts","../src/marketplaces/directory.ts","../src/marketplaces/manifest.ts","../src/marketplaces/source.ts","../src/marketplaces/github.ts","../src/commands/marketplace/add.ts","../src/installers/registry.ts","../src/commands/marketplace/list.ts","../src/commands/marketplace/remove.ts","../src/commands/marketplace/update.ts","../src/commands/marketplace/index.ts","../src/commands/mcp/index.ts","../src/installers/claude-code/installer.ts","../src/installers/claude-code/mcp.ts","../src/installers/claude-code/paths.ts","../src/installers/codex/installer.ts","../src/installers/codex/mcp.ts","../src/installers/codex/paths.ts","../src/installers/codex/toml.ts","../src/installers/cursor/installer.ts","../src/installers/cursor/marker.ts","../src/installers/cursor/mcp.ts","../src/installers/cursor/paths.ts","../src/installers/cursor/rules.ts","../src/installers/cursor/skills.ts","../src/migration/cleanup.ts","../src/migration/detect.ts","../src/migration/log.ts","../src/migration/migrate.ts","../src/plugins/resolver.ts","../src/plugins/capabilities.ts","../src/plugins/errors.ts","../src/plugins/manifest.ts","../src/commands/plugin/common.ts","../src/commands/plugin/install.ts","../src/commands/plugin/list.ts","../src/commands/plugin/uninstall.ts","../src/commands/plugin/update.ts","../src/commands/plugin/index.ts","../src/commands/skill/index.ts","../src/commands/task/index.ts","../src/output/diff.ts","../src/util/stdin.ts","../src/util/api-task.ts","../src/util/publish-pod-stream.ts","../src/util/rush-domain.ts","../src/commands/task/push.ts","../src/util/env-file.ts","../src/commands/task/deploy.ts","../src/commands/task/domain.ts","../src/commands/task/link.ts","../src/commands/task/versions.ts","../src/commands/index.ts","../src/util/update-check.ts"],"sourcesContent":["import chalk from 'chalk';\nimport { Command } from 'commander';\nimport { registerCommands } from './commands/index.js';\nimport { output } from './output/logger.js';\nimport { getAuthMethod } from './util/auth.js';\nimport { isCIMode } from './util/ci.js';\nimport { isRushError } from './util/errors.js';\nimport { checkForUpdate } from './util/update-check.js';\nimport { setVerbosity } from './util/verbosity.js';\nimport { VERSION } from './version.js';\n\nconst BANNER = `\n ${chalk.bold.cyan('Rush CLI')} ${chalk.dim(`v${VERSION}`)}\n ${chalk.dim('Call Rush agents from your terminal — relay the context, not the prompt.')}\n\n ${chalk.dim('Use cases:')}\n ${chalk.dim('·')} From Cursor / Claude Code, hand off a task to a Rush agent without restating context.\n ${chalk.dim('·')} Compose workflows where a Rush specialist agent runs as your sub-agent.\n\n ${chalk.dim('For agents:')} ${chalk.cyan('rush-ai skill')} prints ready-to-consume usage playbooks.\n`;\n\nfunction showWelcomeGuide(): void {\n const lines = [\n '',\n ` ${chalk.bold.cyan('Rush CLI')} ${chalk.dim(`v${VERSION}`)}`,\n ` ${chalk.dim('Call Rush agents from your terminal — relay the context, not the prompt.')}`,\n '',\n chalk.dim(' Quick start:'),\n ` ${chalk.cyan('rush-ai auth login')} Log in to the Rush platform`,\n ` ${chalk.cyan('rush-ai agent list')} Browse available agents`,\n ` ${chalk.cyan('rush-ai task create -a web-builder')} Hand a task to a Rush agent`,\n '',\n chalk.dim(' For AI agents:'),\n ` ${chalk.cyan('rush-ai skill')} Print usage playbooks (markdown)`,\n ` ${chalk.cyan('rush-ai skill hand-off')} IDE → Rush task hand-off`,\n ` ${chalk.cyan('rush-ai skill agent-shelf')} Calling a Rush agent as a sub-agent`,\n '',\n ];\n\n try {\n const method = getAuthMethod();\n if (method) {\n lines.push(chalk.dim(' Status:'), ` Logged in via ${method}`, '');\n }\n } catch {\n // Ignore auth errors in welcome guide\n }\n\n lines.push(chalk.dim(' Run rush-ai --help for all commands.'), '');\n console.error(lines.join('\\n'));\n}\n\nexport function createProgram(): Command {\n const program = new Command();\n\n program\n .name('rush-ai')\n .version(VERSION, '-v, --version')\n .description('Rush CLI - Command-line interface for the Rush AI platform')\n .configureHelp({\n sortSubcommands: true,\n })\n .addHelpText('before', BANNER)\n .option('--no-color', 'Disable color output')\n .option('--json', 'Output as JSON where supported')\n .option('-f, --format <type>', 'Output format (table, json, csv)')\n .option(\n '--ci',\n 'CI mode: JSON output, no interactive prompts, strict exit codes'\n )\n .option('--verbose', 'Show detailed output including HTTP requests')\n .option('--debug', 'Show debug-level output (implies --verbose)');\n\n // CI mode: auto-enable --json before any command runs\n // Set verbosity BEFORE any command action runs\n program.hook('preAction', (thisCommand) => {\n const opts = thisCommand.optsWithGlobals();\n if (isCIMode(opts)) {\n if (!opts.json) {\n thisCommand.setOptionValue('json', true);\n }\n }\n setVerbosity(!!opts.verbose, !!opts.debug);\n });\n\n // No-argument interactive guide (Commander handles --help/--version first)\n program.action(() => {\n if (process.stdout.isTTY) {\n showWelcomeGuide();\n } else {\n program.help();\n }\n });\n\n registerCommands(program);\n\n return program;\n}\n\nasync function main(): Promise<void> {\n // Disable color in non-TTY environments, --no-color, or CI mode\n if (\n !process.stdout.isTTY ||\n process.argv.includes('--no-color') ||\n isCIMode()\n ) {\n process.env.NO_COLOR = '1';\n }\n\n const program = createProgram();\n\n try {\n await program.parseAsync(process.argv);\n } catch (error) {\n const ci = isCIMode(program.opts());\n\n if (isRushError(error)) {\n if (ci) {\n output.log(\n JSON.stringify({\n error: true,\n code: error.code,\n message: error.message,\n exitCode: error.exitCode,\n })\n );\n } else {\n output.error(error.message);\n if (error.code === 'AUTH_ERROR') {\n output.newline();\n output.dim('Run `rush-ai auth login` to authenticate.');\n }\n }\n process.exit(error.exitCode);\n }\n\n if (error instanceof Error) {\n const exitCode = 2;\n if (ci) {\n output.log(\n JSON.stringify({\n error: true,\n code: 'UNKNOWN_ERROR',\n message: error.message,\n exitCode,\n })\n );\n } else {\n output.error(error.message);\n if (process.env.DEBUG) {\n console.error(error.stack);\n }\n }\n process.exit(exitCode);\n }\n\n if (ci) {\n output.log(\n JSON.stringify({\n error: true,\n code: 'UNKNOWN_ERROR',\n message: 'An unexpected error occurred.',\n exitCode: 2,\n })\n );\n } else {\n output.error('An unexpected error occurred.');\n }\n process.exit(2);\n }\n\n // Non-blocking version check after command completes\n void checkForUpdate(VERSION);\n}\n\n// Only run when executed as entry point, not when imported by tests\nif (!process.env.VITEST) {\n main();\n}\n","/**\n * RFC 4180 compliant CSV formatter.\n * No external dependencies.\n */\n\nfunction escapeField(value: string): string {\n // If value contains comma, quote, newline, or leading/trailing spaces, wrap in quotes\n if (\n value.includes(',') ||\n value.includes('\"') ||\n value.includes('\\n') ||\n value.includes('\\r') ||\n value !== value.trim()\n ) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n\nexport function formatCsv(rows: Record<string, string>[]): string {\n if (rows.length === 0) return '';\n\n const keys = Object.keys(rows[0]);\n\n // Header row\n const header = keys.map(escapeField).join(',');\n\n // Data rows\n const lines = rows.map((row) =>\n keys.map((k) => escapeField(row[k] ?? '')).join(',')\n );\n\n return [header, ...lines].join('\\n');\n}\n","import chalk from 'chalk';\n\nexport interface TableColumnOptions {\n maxWidth?: number;\n align?: 'left' | 'right';\n}\n\nexport interface TableFormatOptions {\n columns?: Record<string, TableColumnOptions>;\n}\n\nconst STATUS_COLORS: Record<string, (s: string) => string> = {\n completed: chalk.green,\n active: chalk.green,\n installed: chalk.green,\n running: chalk.yellow,\n pending: chalk.yellow,\n available: chalk.dim,\n failed: chalk.red,\n cancelled: chalk.red,\n error: chalk.red,\n};\n\nfunction colorizeStatus(value: string): string {\n const colorFn = STATUS_COLORS[value.toLowerCase()];\n return colorFn ? colorFn(value) : value;\n}\n\nfunction truncate(str: string, max: number): string {\n if (str.length <= max) return str;\n return `${str.slice(0, max - 3)}...`;\n}\n\nexport function formatTableEnhanced(\n rows: Record<string, string>[],\n options?: TableFormatOptions\n): string {\n if (rows.length === 0) return '';\n\n const keys = Object.keys(rows[0]);\n const colOpts = options?.columns ?? {};\n const widths: Record<string, number> = {};\n\n // Calculate column widths\n for (const key of keys) {\n widths[key] = key.length;\n }\n for (const row of rows) {\n for (const key of keys) {\n const val = row[key] ?? '';\n const maxW = colOpts[key]?.maxWidth;\n const display = maxW ? truncate(val, maxW) : val;\n if (display.length > widths[key]) {\n widths[key] = display.length;\n }\n }\n }\n\n // Build header\n const header = keys.map((k) => chalk.bold(k.padEnd(widths[k]))).join(' ');\n const separator = keys.map((k) => '-'.repeat(widths[k])).join(' ');\n\n // Build rows\n const lines = rows.map((row) =>\n keys\n .map((k) => {\n const raw = row[k] ?? '-';\n const display = raw === '' ? '-' : raw;\n const maxW = colOpts[k]?.maxWidth;\n const truncated = maxW ? truncate(display, maxW) : display;\n const align = colOpts[k]?.align ?? 'left';\n const padded =\n align === 'right'\n ? truncated.padStart(widths[k])\n : truncated.padEnd(widths[k]);\n\n // Colorize status-like values\n if (k.toLowerCase() === 'status' || k.toLowerCase() === 'state') {\n return colorizeStatus(padded);\n }\n return padded;\n })\n .join(' ')\n );\n\n return [header, separator, ...lines].join('\\n');\n}\n","import { formatCsv } from './csv.js';\nimport { formatTableEnhanced, type TableFormatOptions } from './table.js';\n\nexport type OutputFormat = 'table' | 'json' | 'csv';\n\nconst VALID_FORMATS: readonly string[] = ['table', 'json', 'csv'];\n\nexport interface FormatOptions extends TableFormatOptions {}\n\n/**\n * Resolve the effective output format from CLI options.\n * Throws if --json and --format conflict, or if format value is invalid.\n */\nexport function resolveFormat(opts: {\n json?: boolean;\n format?: string;\n}): OutputFormat {\n const hasJson = opts.json === true;\n const hasFormat = opts.format != null && opts.format !== undefined;\n\n // Validate format value\n if (hasFormat && !VALID_FORMATS.includes(opts.format as string)) {\n throw new Error(\n `Invalid format: \"${opts.format}\". Supported formats: ${VALID_FORMATS.join(', ')}`\n );\n }\n\n if (hasJson && hasFormat && opts.format !== 'json') {\n throw new Error(\n `Conflicting options: --json cannot be used with --format ${opts.format}. Use one or the other.`\n );\n }\n\n if (hasJson) return 'json';\n if (hasFormat) return opts.format as OutputFormat;\n return 'table';\n}\n\n/**\n * Format row data as a string in the specified format.\n * Note: JSON mode should be handled separately per-command to preserve payload structure.\n */\nexport function formatOutput(\n rows: Record<string, string>[],\n format: OutputFormat,\n options?: FormatOptions\n): string {\n switch (format) {\n case 'csv':\n return formatCsv(rows);\n case 'json':\n return JSON.stringify(rows, null, 2);\n default:\n return formatTableEnhanced(rows, options);\n }\n}\n\nexport { formatCsv } from './csv.js';\nexport type { TableColumnOptions, TableFormatOptions } from './table.js';\n// Re-export for convenience\nexport { formatTableEnhanced } from './table.js';\n","/**\n * CI mode detection utility.\n *\n * CI mode is activated by any of:\n * - `--ci` CLI flag\n * - `CI` environment variable (set by GitHub Actions, GitLab CI, etc.)\n * - `RUSH_CI` environment variable (explicit opt-in)\n */\n\n/**\n * Strict env-var truthy check.\n * Returns true only for '1', 'true', 'yes' (case-insensitive).\n */\nfunction isEnvTruthy(value: string | undefined): boolean {\n if (!value) return false;\n return ['1', 'true', 'yes'].includes(value.trim().toLowerCase());\n}\n\n/**\n * Detect CI mode from CLI flag or environment variables.\n *\n * The `process.argv` check runs unconditionally as a reliable fallback\n * that works even before Commander parses (e.g. in early error paths).\n */\nexport function isCIMode(programOpts?: { ci?: boolean }): boolean {\n if (process.argv.includes('--ci')) return true;\n if (programOpts?.ci) return true;\n if (isEnvTruthy(process.env.CI)) return true;\n if (isEnvTruthy(process.env.RUSH_CI)) return true;\n return false;\n}\n","import { output } from '../output/logger.js';\nimport { getAuthToken } from './auth.js';\nimport { isCIMode } from './ci.js';\nimport { checkAuthSourceMismatch, getAuthConfig } from './config.js';\nimport { AuthError } from './errors.js';\n\n/**\n * Guard that throws AuthError if not authenticated.\n * Checks RUSH_API_KEY env and stored CAS token (including expiry).\n *\n * Also surfaces a soft warning when the stored token's origin URL doesn't\n * match the profile's current API URL (see #832 — silent 401s after\n * `config set api` were a common way users lost their session without a\n * clear next-step).\n */\nexport function requireAuth(): void {\n const token = getAuthToken();\n if (!token) {\n throw new AuthError();\n }\n // Check expiry for stored tokens (env API keys never expire client-side)\n if (!process.env.RUSH_API_KEY) {\n const auth = getAuthConfig();\n if (auth.expiresAt && Date.now() > auth.expiresAt) {\n // CAS tokens with refresh_token can still be renewed lazily\n if (auth.method === 'cas' && auth.refreshToken) {\n return;\n }\n throw new AuthError(\n 'Token expired. Run `rush-ai auth login` to re-authenticate.'\n );\n }\n\n // Non-fatal URL mismatch warning — the token might still work, but this\n // is the single most common reason users see 401s without a clear hint.\n // Suppress in CI and JSON modes: the warning on every invocation turns\n // into pipe-log noise that automation has no way to act on. Interactive\n // users keep getting the guidance; scripted users can opt in via\n // `auth status` or `doctor` when they want to verify the binding.\n if (!isCIMode() && !isJsonMode()) {\n const mismatch = checkAuthSourceMismatch();\n if (mismatch) {\n output.warn(\n `Token was issued against ${mismatch.stored} but the current API is ${mismatch.current}.`\n );\n output.dim('If requests start failing, run `rush-ai auth login`.');\n }\n }\n }\n}\n\nfunction isJsonMode(): boolean {\n // Cheap argv scan — requireAuth runs before Commander parses in some\n // paths, so we can't rely on program.opts().json here.\n return process.argv.includes('--json');\n}\n","import type { Command } from 'commander';\nimport { formatOutput, resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport { createClient } from '../../util/client.js';\nimport { ApiError } from '../../util/errors.js';\nimport { requireAuth } from '../../util/require-auth.js';\n\n/** Matches DbAgent from the real API */\ninterface ApiAgent {\n id: string;\n name: string;\n description: string | null;\n capabilities: string[] | null;\n skills: string[] | null;\n mcp_servers: unknown[] | null;\n status: string;\n visibility: string;\n provider_type: string;\n provider_model: string | null;\n system_prompt: string | null;\n created_at: string;\n updated_at: string;\n created_by_id: string | null;\n is_builtin: boolean;\n}\n\ninterface AgentListResponse {\n agents: ApiAgent[];\n pagination: {\n total: number;\n page: number;\n limit: number;\n totalPages: number;\n };\n}\n\ninterface AgentDetailResponse {\n agent: ApiAgent;\n}\n\ninterface AgentCandidateItem {\n id: string;\n name: string;\n status: string;\n visibility: string;\n created_at: string;\n}\n\ninterface AgentCandidatesBody {\n error: string;\n candidates: AgentCandidateItem[];\n}\n\nconst AGENT_HELP_AFTER = `\nWhen to use:\n Rush hosts a shelf of specialist agents — call them as sub-agents from\n your own workflows instead of writing the prompts yourself. Always\n discover first, then invoke.\n\nTypical flows:\n $ rush-ai agent list --default --json # just the built-in defaults\n $ rush-ai agent list --search \"人力\" --json # fuzzy-filter by name / desc\n $ rush-ai agent list --all --json # everything — may be long\n $ rush-ai agent info <name> --json # inspect one agent's skills / MCP\n\n $ rush-ai task create -a <name> -p \"...\" # call the agent as a sub-agent\n $ rush-ai task status <id> --json # grab its output\n\nBuilt-in default agents (see --default):\n rush general-purpose agent\n web-builder site / landing-page builder (returns previewUrl + gitRepoUrl)\n skill-publisher publishes reusable skills back onto the platform\n\nFor agents: run \\`rush-ai skill agent-shelf\\` for the full playbook.\n`;\n\nexport function registerAgentCommand(program: Command): void {\n const agent = program\n .command('agent')\n .description('Manage and inspect agents')\n .addHelpText('after', AGENT_HELP_AFTER);\n\n agent\n .command('list')\n .alias('ls')\n .description('List available agents')\n .option(\n '-l, --limit <n>',\n 'Max agents per page (1–500, default 50)',\n (v) => Number.parseInt(v, 10),\n 50\n )\n .option(\n '-p, --page <n>',\n 'Page number (1-indexed, default 1)',\n (v) => Number.parseInt(v, 10),\n 1\n )\n .option('-q, --search <text>', 'Filter agents by name / description')\n .option(\n '--default',\n 'Only show built-in default agents (web-builder, rush, skill-publisher, ...)'\n )\n .option(\n '--all',\n 'Fetch every page and return the full list (may issue several requests)'\n )\n .action(\n async (opts: {\n limit: number;\n page: number;\n search?: string;\n default?: boolean;\n all?: boolean;\n }) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n const baseParams = new URLSearchParams();\n const rawLimit = Number.isFinite(opts.limit) ? opts.limit : 50;\n const apiLimit = Math.min(Math.max(rawLimit, 1), 500);\n const rawPage = Number.isFinite(opts.page) ? opts.page : 1;\n const requestedPage = Math.max(rawPage, 1);\n if (opts.search) baseParams.set('q', opts.search);\n if (opts.default) baseParams.set('is_builtin', 'true');\n\n async function fetchPage(\n page: number,\n limit: number\n ): Promise<AgentListResponse> {\n const params = new URLSearchParams(baseParams);\n params.set('page', String(page));\n params.set('limit', String(limit));\n const { data } = await client.get<AgentListResponse>(\n `/api/agents?${params.toString()}`\n );\n return data;\n }\n\n let data: AgentListResponse;\n if (opts.all) {\n // Pull every page so the caller really sees the whole shelf.\n // API caps `limit` at 500, so for realistic shelf sizes this is 1 request.\n const first = await fetchPage(1, 500);\n const totalPages = first.pagination?.totalPages ?? 1;\n const all = [...first.agents];\n for (let p = 2; p <= totalPages; p++) {\n const next = await fetchPage(p, 500);\n all.push(...next.agents);\n }\n data = {\n agents: all,\n pagination: first.pagination\n ? { ...first.pagination, page: 1, limit: all.length }\n : {\n total: all.length,\n page: 1,\n limit: all.length,\n totalPages: 1,\n },\n };\n } else {\n data = await fetchPage(requestedPage, apiLimit);\n }\n\n if (format === 'json') {\n output.log(JSON.stringify(data, null, 2));\n return;\n }\n\n if (data.agents.length === 0) {\n output.info('No agents found.');\n return;\n }\n\n const total = data.pagination?.total ?? data.agents.length;\n const shown = data.agents.length;\n const page = data.pagination?.page ?? 1;\n const totalPages = data.pagination?.totalPages ?? 1;\n\n output.log(output.bold(`Agents (showing ${shown} of ${total}):`));\n output.newline();\n\n const rows = data.agents.map((a) => ({\n Name: a.name,\n Description: truncate(a.description ?? '', 50),\n Status: a.status,\n Skills: String(a.skills?.length ?? 0),\n MCP: String(a.mcp_servers?.length ?? 0),\n }));\n\n output.log(\n formatOutput(rows, format, {\n columns: { Description: { maxWidth: 50 } },\n })\n );\n\n if (!opts.all && shown < total) {\n output.newline();\n output.dim(\n `Page ${page}/${totalPages}. Use --page <n>, --limit <n>, --search <text>, or --all to see more.`\n );\n }\n }\n );\n\n agent\n .command('info')\n .description('Show agent details')\n .argument('<name>', 'Agent name or ID')\n .action(async (nameOrId: string) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n let data: AgentDetailResponse;\n try {\n ({ data } = await client.get<AgentDetailResponse>(\n `/api/agents/${encodeURIComponent(nameOrId)}`\n ));\n } catch (err: unknown) {\n if (err instanceof ApiError && err.status === 409) {\n let parsed: AgentCandidatesBody | undefined;\n try {\n parsed = JSON.parse(err.meta.body as string);\n } catch {\n // body 不是合法 JSON,走默认错误处理\n }\n\n // 运行时守卫:确保 candidates 是合法数组且每项有必要字段\n const candidates = parsed?.candidates;\n const isValid =\n Array.isArray(candidates) &&\n candidates.length > 0 &&\n candidates.every(\n (c) => typeof c.id === 'string' && typeof c.name === 'string'\n );\n\n if (isValid) {\n if (format === 'json') {\n output.log(JSON.stringify(parsed, null, 2));\n process.exitCode = 1;\n return;\n }\n\n output.error(`Multiple agents found with name \"${nameOrId}\":`);\n output.newline();\n for (const c of candidates!) {\n const date = c.created_at?.split('T')[0] ?? '';\n output.dim(` ${c.id} ${c.name} ${c.status ?? ''} ${date}`);\n }\n output.newline();\n output.info(`Use agent ID to specify: rush agent info <id>`);\n process.exitCode = 1;\n return;\n }\n }\n throw err;\n }\n\n const a = data.agent;\n\n if (format === 'json') {\n output.log(JSON.stringify(a, null, 2));\n return;\n }\n\n output.log(output.bold(a.name));\n output.log(` ID: ${a.id}`);\n output.log(` Description: ${a.description ?? '(none)'}`);\n output.log(` Status: ${a.status}`);\n output.log(` Visibility: ${a.visibility}`);\n output.log(\n ` Provider: ${a.provider_type}${a.provider_model ? ` (${a.provider_model})` : ''}`\n );\n output.log(` Capabilities: ${a.capabilities?.join(', ') || 'none'}`);\n output.newline();\n\n if (a.skills && a.skills.length > 0) {\n output.log(output.bold(' Skills:'));\n for (const skill of a.skills) {\n output.log(` - ${skill}`);\n }\n output.newline();\n }\n\n if (a.mcp_servers && a.mcp_servers.length > 0) {\n output.log(output.bold(` MCP Servers (${a.mcp_servers.length}):`));\n for (const mcp of a.mcp_servers) {\n if (typeof mcp === 'object' && mcp !== null) {\n const m = mcp as Record<string, unknown>;\n const name = String(m.serverName ?? m.name ?? 'unknown');\n const disabled = m.disabled ? ' (disabled)' : '';\n output.log(` - ${name}${disabled}`);\n } else {\n output.log(` - ${JSON.stringify(mcp)}`);\n }\n }\n output.newline();\n }\n\n output.dim(` Updated: ${new Date(a.updated_at).toLocaleString()}`);\n });\n}\n\nfunction truncate(str: string, max: number): string {\n if (str.length <= max) return str;\n return `${str.slice(0, max - 3)}...`;\n}\n","import { randomBytes } from 'node:crypto';\nimport { createServer } from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport open from 'open';\nimport { output } from '../../output/logger.js';\nimport { getGlobalConfig, setAuthConfig } from '../../util/config.js';\nimport { AuthError } from '../../util/errors.js';\n\ninterface TokenResponse {\n token: string;\n token_id: string;\n expires_at: string;\n}\n\nfunction getApiBaseUrl(): string {\n return process.env.RUSH_API_URL ?? getGlobalConfig().api;\n}\n\nexport async function loginViaBrowser(jsonMode?: boolean): Promise<void> {\n const baseUrl = getApiBaseUrl();\n const state = randomBytes(16).toString('hex');\n\n return new Promise<void>((resolve, reject) => {\n const server = createServer(async (req, res) => {\n try {\n const url = new URL(req.url ?? '/', 'http://localhost');\n\n if (url.pathname !== '/callback') {\n res.writeHead(404);\n res.end('Not found');\n return;\n }\n\n const code = url.searchParams.get('code');\n const returnedState = url.searchParams.get('state');\n const error = url.searchParams.get('error');\n\n if (error) {\n const desc = url.searchParams.get('error_description') ?? '认证失败';\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(\n getHtmlPage(\n '认证失败',\n `错误:${desc}。你可以关闭此页面。`,\n baseUrl\n )\n );\n server.close();\n reject(new AuthError(`Browser authentication failed: ${desc}`));\n return;\n }\n\n if (!code || returnedState !== state) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(\n getHtmlPage(\n '认证失败',\n '回调参数无效,请重新登录。你可以关闭此页面。',\n baseUrl\n )\n );\n server.close();\n reject(\n new AuthError('Invalid callback: missing code or state mismatch')\n );\n return;\n }\n\n // Exchange code for Platform Token\n const tokenUrl = `${baseUrl}/api/cli/auth/token`;\n const tokenResponse = await fetch(tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ code, state }),\n });\n\n if (!tokenResponse.ok) {\n const errorText = await tokenResponse\n .text()\n .catch(() => 'Unknown error');\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(\n getHtmlPage(\n '认证失败',\n 'Token 交换失败,请重试。你可以关闭此页面。',\n baseUrl\n )\n );\n server.close();\n reject(new AuthError(`Token exchange failed: ${errorText}`));\n return;\n }\n\n const tokenData = (await tokenResponse.json()) as TokenResponse;\n\n const expiresAt = tokenData.expires_at\n ? new Date(tokenData.expires_at).getTime()\n : null;\n\n setAuthConfig({\n token: tokenData.token,\n expiresAt,\n refreshToken: null,\n method: 'platform_token',\n tokenId: tokenData.token_id,\n sourceUrl: baseUrl,\n });\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(\n getHtmlPage(\n '认证成功',\n '你已成功登录,可以关闭此页面并返回终端。',\n baseUrl,\n expiresAt ?? undefined\n )\n );\n\n server.close();\n\n if (jsonMode) {\n output.log(\n JSON.stringify({\n status: 'authenticated',\n method: 'platform_token',\n expiresAt,\n })\n );\n } else {\n output.success('Logged in successfully.');\n if (expiresAt) {\n output.dim(\n `Token expires: ${new Date(expiresAt).toLocaleString()}`\n );\n }\n }\n\n resolve();\n } catch (err) {\n server.close();\n reject(\n err instanceof AuthError\n ? err\n : new AuthError(\n `Authentication error: ${err instanceof Error ? err.message : 'Unknown'}`\n )\n );\n }\n });\n\n server.listen(0, '127.0.0.1', () => {\n const { port } = server.address() as AddressInfo;\n\n const authUrl = `${baseUrl}/api/cli/auth/authorize?callback_port=${port}&state=${encodeURIComponent(state)}`;\n\n if (!jsonMode) {\n output.log('Opening browser for authentication...');\n output.dim(\"If the browser doesn't open, visit:\");\n output.dim(authUrl);\n output.newline();\n output.dim('Waiting for authentication...');\n }\n\n open(authUrl).catch(() => {\n if (!jsonMode) {\n output.warn(\n 'Could not open browser. Please visit the URL above manually.'\n );\n }\n });\n });\n\n // Timeout after 5 minutes\n const timeoutId = setTimeout(() => {\n server.close();\n reject(new AuthError('Authentication timed out after 5 minutes'));\n }, 300_000);\n\n server.on('close', () => clearTimeout(timeoutId));\n });\n}\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction getHtmlPage(\n title: string,\n message: string,\n serverUrl: string,\n expiresAt?: number\n): string {\n const metaLines: string[] = [\n `<span class=\"label\">服务器</span> <span class=\"value\">${escapeHtml(serverUrl)}</span>`,\n ];\n if (expiresAt) {\n metaLines.push(\n `<span class=\"label\">有效期至</span> <span class=\"value\">${escapeHtml(new Date(expiresAt).toLocaleString('zh-CN'))}</span>`\n );\n }\n const metaHtml = metaLines\n .map((line) => `<div class=\"meta-row\">${line}</div>`)\n .join('\\n ');\n\n return `<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head><meta charset=\"utf-8\"><title>Rush CLI - ${escapeHtml(title)}</title>\n<style>\n body { font-family: -apple-system, BlinkMacSystemFont, \"PingFang SC\", \"Microsoft YaHei\", sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #f5f5f5; }\n .card { background: white; border-radius: 12px; padding: 40px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); text-align: center; max-width: 420px; }\n h1 { color: #333; font-size: 1.5em; margin-bottom: 8px; }\n p { color: #666; line-height: 1.6; }\n .meta { margin-top: 16px; padding-top: 16px; border-top: 1px solid #eee; text-align: left; }\n .meta-row { color: #888; font-size: 0.85em; line-height: 2; }\n .meta-row .label { color: #999; }\n .meta-row .value { color: #555; font-family: \"SF Mono\", Menlo, monospace; }\n</style>\n</head>\n<body>\n <div class=\"card\">\n <h1>${escapeHtml(title)}</h1>\n <p>${escapeHtml(message)}</p>\n <div class=\"meta\">\n ${metaHtml}\n </div>\n </div>\n</body>\n</html>`;\n}\n","import type { Command } from 'commander';\nimport { output } from '../../output/logger.js';\nimport { getAuthMethod } from '../../util/auth.js';\nimport {\n revokeCurrentSession,\n verifyCurrentAuthSession,\n} from '../../util/auth-session.js';\nimport {\n clearAuthConfig,\n getAuthConfig,\n getConfigDir,\n getGlobalConfig,\n isLoggedIn,\n setAuthConfig,\n} from '../../util/config.js';\nimport { RushError } from '../../util/errors.js';\nimport { loginViaBrowser } from './browser-login.js';\n\nexport function registerAuthCommand(program: Command): void {\n const auth = program.command('auth').description('Manage authentication');\n\n auth\n .command('login')\n .description('Log in to the Rush platform')\n .option('--api-key <key>', 'Authenticate with an API key instead of CAS')\n .option('--no-verify', 'Skip online validation for API key login')\n .action(async (options: { apiKey?: string; verify?: boolean }) => {\n const jsonMode = program.opts().json;\n\n if (options.apiKey) {\n setAuthConfig({\n token: options.apiKey,\n expiresAt: null,\n refreshToken: null,\n method: 'api_key',\n // Bind the API key to whichever URL it was activated against so\n // switching profiles / `config set api` can warn about mismatches.\n sourceUrl: process.env.RUSH_API_URL ?? getGlobalConfig().api,\n });\n\n let verification:\n | Awaited<ReturnType<typeof verifyCurrentAuthSession>>\n | undefined;\n if (options.verify !== false) {\n verification = await verifyCurrentAuthSession();\n if (!verification.valid) {\n clearAuthConfig();\n throw new RushError(\n `API key validation failed: ${verification.message ?? 'invalid credentials'}`,\n {},\n 'AUTH_ERROR'\n );\n }\n }\n\n if (jsonMode) {\n output.log(\n JSON.stringify({\n status: 'authenticated',\n method: 'api_key',\n verified: options.verify !== false,\n ...(verification ? { serverStatus: verification.status } : {}),\n })\n );\n } else {\n output.success('Authenticated with API key.');\n if (options.verify !== false) {\n output.dim('Server validation: OK');\n }\n }\n return;\n }\n\n // CAS PKCE flow\n if (isLoggedIn()) {\n if (jsonMode) {\n output.log(JSON.stringify({ status: 'already_authenticated' }));\n } else {\n output.warn('You are already logged in.');\n output.dim('Run `rush-ai auth logout` first to log out.');\n }\n return;\n }\n\n await loginViaBrowser(jsonMode);\n });\n\n auth\n .command('status')\n .description('Show the current authentication status')\n .option('--verify', 'Validate the current credential against the server')\n .action(async (options: { verify?: boolean }) => {\n const jsonMode = program.opts().json;\n const method = getAuthMethod();\n const authConfig = getAuthConfig();\n // Consider authenticated if: env API key set, OR stored token is valid\n const loggedIn =\n method !== null && (method === 'api_key' || isLoggedIn());\n const verification =\n loggedIn && options.verify ? await verifyCurrentAuthSession() : null;\n\n if (jsonMode) {\n output.log(\n JSON.stringify({\n authenticated: loggedIn,\n method: method ?? null,\n expiresAt: authConfig.expiresAt,\n hasRefreshToken: Boolean(authConfig.refreshToken),\n configDir: getConfigDir(),\n ...(verification\n ? {\n serverAuthenticated: verification.valid,\n serverStatus: verification.status,\n serverMessage: verification.message,\n }\n : {}),\n })\n );\n } else {\n if (loggedIn) {\n output.success(`Authenticated via ${method ?? 'token'}`);\n if (authConfig.expiresAt) {\n const expiresDate = new Date(authConfig.expiresAt).toLocaleString();\n output.dim(`Token expires: ${expiresDate}`);\n }\n if (authConfig.refreshToken) {\n output.dim('Refresh token: available');\n }\n } else {\n output.warn('Not authenticated.');\n output.dim('Run `rush-ai auth login` to log in.');\n }\n if (verification) {\n if (verification.valid) {\n output.dim('Server validation: OK');\n } else {\n output.warn(\n `Server validation failed: ${verification.message ?? 'token rejected'}`\n );\n }\n }\n output.dim(`Config: ${getConfigDir()}`);\n }\n });\n\n auth\n .command('logout')\n .description('Log out and clear stored credentials')\n .option('--no-revoke', 'Skip best-effort CAS revoke request')\n .action(async (options: { revoke?: boolean }) => {\n const jsonMode = program.opts().json;\n const authConfig = getAuthConfig();\n\n const hasToken = authConfig.token !== null;\n if (!hasToken) {\n if (jsonMode) {\n output.log(JSON.stringify({ status: 'not_authenticated' }));\n } else {\n output.warn('You are not currently logged in.');\n }\n return;\n }\n\n const shouldRevoke =\n options.revoke !== false &&\n (authConfig.method === 'cas' || authConfig.method === 'platform_token');\n const revoked = shouldRevoke ? await revokeCurrentSession() : false;\n\n clearAuthConfig();\n if (jsonMode) {\n output.log(\n JSON.stringify({\n status: 'logged_out',\n ...(shouldRevoke ? { revoked } : {}),\n })\n );\n } else {\n output.success('Logged out successfully.');\n if (shouldRevoke) {\n if (revoked) {\n output.dim('Remote token revoked.');\n } else {\n output.warn(\n 'Could not confirm remote revoke. Local credentials were cleared.'\n );\n }\n }\n }\n });\n}\n","import { resolve } from 'node:path';\nimport chalk from 'chalk';\nimport type { Command } from 'commander';\nimport { output } from '../../output/logger.js';\nimport { checkDatabase } from './checks/database.js';\nimport { checkFramework, detectFramework } from './checks/framework.js';\nimport { checkGitignore } from './checks/gitignore.js';\nimport { checkLockFile, detectPackageManager } from './checks/lock-file.js';\nimport { checkPackageJson } from './checks/package-json.js';\nimport { checkPortEnv } from './checks/port-env.js';\nimport type { DeployCheck, DeployCheckReport } from './types.js';\n\nfunction severityIcon(severity: DeployCheck['severity']): string {\n switch (severity) {\n case 'error':\n return chalk.red('✗');\n case 'warning':\n return chalk.yellow('!');\n case 'info':\n return chalk.dim('·');\n }\n}\n\nfunction displayResults(report: DeployCheckReport): void {\n output.newline();\n output.log(chalk.bold(' Rush Deploy Check'));\n output.log(\n chalk.dim(\n ` Framework: ${report.framework} | Package Manager: ${report.packageManager ?? 'unknown'}`\n )\n );\n output.newline();\n\n const grouped = {\n error: report.checks.filter((c) => c.severity === 'error'),\n warning: report.checks.filter((c) => c.severity === 'warning'),\n info: report.checks.filter((c) => c.severity === 'info'),\n };\n\n for (const [severity, checks] of Object.entries(grouped)) {\n if (checks.length === 0) continue;\n for (const check of checks) {\n const icon = severityIcon(check.severity);\n output.log(` ${icon} ${check.message}`);\n if (check.fix && severity !== 'info') {\n output.log(chalk.dim(` → ${check.fix}`));\n }\n }\n }\n\n output.newline();\n\n const { errors, warnings } = report.summary;\n const parts: string[] = [];\n if (errors > 0) parts.push(chalk.red(`${errors} errors`));\n if (warnings > 0) parts.push(chalk.yellow(`${warnings} warnings`));\n if (errors === 0 && warnings === 0)\n parts.push(chalk.green('All checks passed'));\n output.log(` ${parts.join(', ')}`);\n\n if (report.deployable) {\n output.newline();\n output.log(\n chalk.green(' Ready to push! Run `rush-ai task push` to proceed.')\n );\n } else {\n output.newline();\n output.log(chalk.red(' Fix the errors above before deploying.'));\n }\n\n output.newline();\n}\n\nexport function runChecks(projectPath: string): DeployCheckReport {\n const framework = detectFramework(projectPath);\n const packageManager = detectPackageManager(projectPath);\n\n const checks: DeployCheck[] = [\n ...checkPackageJson(projectPath),\n ...checkFramework(projectPath),\n ...checkLockFile(projectPath),\n ...checkPortEnv(projectPath, framework),\n ...checkGitignore(projectPath),\n ];\n\n const { checks: dbChecks, info: dbInfo } = checkDatabase(\n projectPath,\n framework\n );\n checks.push(...dbChecks);\n\n const errors = checks.filter((c) => c.severity === 'error').length;\n const warnings = checks.filter((c) => c.severity === 'warning').length;\n const info = checks.filter((c) => c.severity === 'info').length;\n\n return {\n framework,\n packageManager,\n database: dbInfo,\n checks,\n summary: { errors, warnings, info },\n deployable: errors === 0,\n };\n}\n\nexport function registerCheckCommand(program: Command): void {\n program\n .command('check')\n .description('Check if the current project is ready for Rush deployment')\n .option('-p, --path <dir>', 'Project directory to check', '.')\n .action(async (opts: { path: string }) => {\n const projectPath = resolve(opts.path);\n const jsonMode = program.opts().json;\n\n const report = runChecks(projectPath);\n\n if (jsonMode) {\n output.log(JSON.stringify(report, null, 2));\n } else {\n displayResults(report);\n }\n\n if (!report.deployable) {\n process.exit(1);\n }\n });\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { DatabaseInfo, DeployCheck } from '../types.js';\nimport type { FrameworkType } from './framework.js';\n\nconst PLACEHOLDER_PATTERNS = [\n 'your-',\n 'placeholder',\n 'xxx',\n 'example',\n '<',\n '>',\n '${',\n 'todo',\n];\n\nfunction isPlaceholder(value: string): boolean {\n const lower = value.toLowerCase();\n return PLACEHOLDER_PATTERNS.some((p) => lower.includes(p));\n}\n\nfunction parseEnvFile(filePath: string): Record<string, string> {\n if (!existsSync(filePath)) return {};\n try {\n const content = readFileSync(filePath, 'utf-8');\n const vars: Record<string, string> = {};\n for (const line of content.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const match = trimmed.match(/^([^=]+)=(.*)$/);\n if (match) vars[match[1]] = match[2];\n }\n return vars;\n } catch {\n return {};\n }\n}\n\nexport function checkDatabase(\n projectPath: string,\n framework: FrameworkType\n): { checks: DeployCheck[]; info: DatabaseInfo } {\n const defaultInfo: DatabaseInfo = {\n detected: false,\n type: null,\n autoCreate: false,\n };\n\n if (framework !== 'nextjs') {\n return {\n checks: [],\n info: defaultInfo,\n };\n }\n\n const checks: DeployCheck[] = [];\n const envVars = parseEnvFile(join(projectPath, '.env.local'));\n\n const postgresUrl = envVars.USER_POSTGRESQL_URL;\n if (\n postgresUrl &&\n !isPlaceholder(postgresUrl) &&\n (postgresUrl.startsWith('postgresql://') ||\n postgresUrl.startsWith('postgres://'))\n ) {\n checks.push({\n name: 'database_config',\n severity: 'info',\n message:\n 'Valid PostgreSQL config found in .env.local — Rush will use your existing database',\n });\n return {\n checks,\n info: { detected: true, type: 'postgresql', autoCreate: false },\n };\n }\n\n const supabaseUrl = envVars.SUPABASE_URL;\n const supabaseKey = envVars.SUPABASE_SERVICE_ROLE_KEY || envVars.SUPABASE_KEY;\n if (\n supabaseUrl &&\n supabaseKey &&\n !isPlaceholder(supabaseUrl) &&\n !isPlaceholder(supabaseKey) &&\n supabaseUrl.startsWith('https://') &&\n supabaseKey.length > 20\n ) {\n checks.push({\n name: 'database_config',\n severity: 'info',\n message:\n 'Valid Supabase config found in .env.local — Rush will use your existing database',\n });\n return {\n checks,\n info: { detected: true, type: 'supabase', autoCreate: false },\n };\n }\n\n // Next.js project without valid database config → Rush will auto-create\n const hasPlaceholder =\n (postgresUrl && isPlaceholder(postgresUrl)) ||\n (supabaseUrl && isPlaceholder(supabaseUrl));\n\n if (hasPlaceholder) {\n checks.push({\n name: 'database_config',\n severity: 'warning',\n message:\n 'Database env vars in .env.local contain placeholder values — Rush will auto-create a Supabase database and overwrite them',\n fix: 'Either set real database credentials or remove the placeholder values',\n });\n } else {\n checks.push({\n name: 'database_config',\n severity: 'info',\n message:\n 'No database config detected — Rush will auto-create a Supabase database for this Next.js project',\n });\n }\n\n return {\n checks,\n info: { detected: false, type: null, autoCreate: true },\n };\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { DeployCheck } from '../types.js';\n\nexport type FrameworkType = 'nextjs' | 'vite' | 'unknown';\n\nconst NEXT_CONFIG_FILES = [\n 'next.config.js',\n 'next.config.ts',\n 'next.config.mjs',\n];\n\nconst VITE_CONFIG_FILES = ['vite.config.ts', 'vite.config.js'];\n\nexport function detectFramework(projectPath: string): FrameworkType {\n const pkgPath = join(projectPath, 'package.json');\n if (!existsSync(pkgPath)) return 'unknown';\n\n try {\n const raw = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n const deps = { ...raw.dependencies, ...raw.devDependencies };\n if (deps.next) return 'nextjs';\n if (\n deps.vite ||\n VITE_CONFIG_FILES.some((f) => existsSync(join(projectPath, f)))\n ) {\n return 'vite';\n }\n return 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\nexport function checkFramework(projectPath: string): DeployCheck[] {\n const checks: DeployCheck[] = [];\n const framework = detectFramework(projectPath);\n\n checks.push({\n name: 'framework_detected',\n severity: 'info',\n message: `Detected framework: ${framework}`,\n });\n\n if (framework === 'nextjs') {\n const hasConfig = NEXT_CONFIG_FILES.some((f) =>\n existsSync(join(projectPath, f))\n );\n if (!hasConfig) {\n checks.push({\n name: 'config_file',\n severity: 'warning',\n message: 'No next.config.js/ts/mjs found',\n fix: 'Create a next.config.js file',\n });\n }\n } else if (framework === 'vite') {\n const hasConfig = VITE_CONFIG_FILES.some((f) =>\n existsSync(join(projectPath, f))\n );\n if (hasConfig) {\n checks.push({\n name: 'config_file',\n severity: 'info',\n message: 'Vite config file found',\n });\n }\n\n if (existsSync(join(projectPath, 'index.html'))) {\n checks.push({\n name: 'index_html',\n severity: 'info',\n message: 'index.html found (Vite entry point)',\n });\n } else {\n checks.push({\n name: 'index_html',\n severity: 'warning',\n message:\n 'index.html not found — Vite projects typically need this as entry point',\n fix: 'Create an index.html at the project root',\n });\n }\n }\n\n return checks;\n}\n","/**\n * Gitignore check: `.env.local` 等敏感文件必须被 git 忽略,且不能已经被 tracked。\n *\n * Deploy 时 rush-ai 会对工作区跑 `git add -A` 然后 `git push ... --force`\n * 到 Rush 的 GitLab 仓库。一旦 dashboard 密码、service-role key 被带上去,\n * 就等于泄漏给仓库成员。\n *\n * 两类不安全状态:\n * 1. 文件存在但 `.gitignore` 没覆盖 → push 时被 `add -A` 带走\n * 2. 文件已被 git tracking(历史上 commit 过)→ 就算 `.gitignore` 现在\n * 覆盖了,已 tracked 的文件 push 照样带走\n *\n * 解法:用 git 自身的 `check-ignore` + `ls-files --error-unmatch` 判断,\n * 比解析 .gitignore 文本更可靠(支持负向模式、全局 ignore、.git/info/exclude)。\n *\n * 非 git 仓库场景:rush-ai deploy 会 `git init`;此时检查退化为只看\n * 文件是否存在 + .gitignore 文本匹配。\n */\n\nimport { execFileSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n isFileIgnoredByGit,\n isFileTrackedByGit,\n isGitRepo,\n} from '../../../util/git.js';\nimport type { DeployCheck } from '../types.js';\n\nconst SENSITIVE_ENV_FILES = ['.env.local', '.env.production.local'];\n\nfunction isGitAvailable(): boolean {\n try {\n execFileSync('git', ['--version'], { stdio: 'pipe' });\n return true;\n } catch {\n return false;\n }\n}\n\nfunction readGitignorePatterns(projectPath: string): string[] {\n const gitignorePath = join(projectPath, '.gitignore');\n if (!existsSync(gitignorePath)) return [];\n try {\n return readFileSync(gitignorePath, 'utf-8')\n .split('\\n')\n .map((l) => l.trim())\n .filter((l) => l && !l.startsWith('#'));\n } catch {\n return [];\n }\n}\n\n/**\n * 非 git 仓库时的 fallback:只看 .gitignore 文本,不能检测 tracked 状态。\n * 覆盖常见 pattern(完全匹配 / *前缀 / 后缀 / 单 * glob),足以覆盖 99% 场景。\n */\nfunction matchesGitignoreText(filename: string, patterns: string[]): boolean {\n for (const raw of patterns) {\n if (raw.startsWith('!')) continue; // 负向模式保守不处理\n const p = raw.replace(/^\\/+/, '').replace(/\\/+$/, '');\n if (!p) continue;\n\n if (p === filename) return true;\n if (p === `*${filename}`) return true;\n\n if (p.includes('*')) {\n const regex = new RegExp(\n `^${p.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&').replace(/\\*/g, '.*')}$`\n );\n if (regex.test(filename)) return true;\n }\n }\n return false;\n}\n\nexport function checkGitignore(projectPath: string): DeployCheck[] {\n const checks: DeployCheck[] = [];\n\n const envFilesPresent = SENSITIVE_ENV_FILES.filter((f) =>\n existsSync(join(projectPath, f))\n );\n if (envFilesPresent.length === 0) {\n return checks;\n }\n\n const gitRepo = isGitRepo(projectPath) && isGitAvailable();\n\n if (!gitRepo) {\n // 非 git 仓库(或 git 不可用):回退到 .gitignore 文本匹配\n const patterns = readGitignorePatterns(projectPath);\n const hasGitignore = existsSync(join(projectPath, '.gitignore'));\n const unignored = envFilesPresent.filter(\n (f) => !matchesGitignoreText(f, patterns)\n );\n if (unignored.length === 0) return checks;\n\n const joined = unignored.join(', ');\n const fix = hasGitignore\n ? `Add \"${unignored.join('\" and \"')}\" to .gitignore`\n : `Create a .gitignore with \"${unignored.join('\\n')}\" to exclude sensitive files`;\n checks.push({\n name: 'gitignore_env_local',\n severity: 'error',\n message: hasGitignore\n ? `${joined} is not ignored by .gitignore — secrets would be pushed to GitLab on deploy`\n : `Found ${joined} but no .gitignore — secrets would be pushed to GitLab on deploy`,\n fix,\n });\n return checks;\n }\n\n // Git 仓库:既要 git check-ignore 覆盖,也要确保没有被 tracked。\n const trackedSecrets = envFilesPresent.filter((f) =>\n isFileTrackedByGit(projectPath, f)\n );\n if (trackedSecrets.length > 0) {\n const joined = trackedSecrets.join(', ');\n checks.push({\n name: 'gitignore_env_local_tracked',\n severity: 'error',\n message: `${joined} is tracked by git — even if .gitignore covers it, push will still leak the secrets`,\n fix: `Remove from git but keep locally: \\`git rm --cached ${trackedSecrets.join(' ')}\\` then commit`,\n });\n }\n\n const unignored = envFilesPresent.filter(\n (f) => !trackedSecrets.includes(f) && !isFileIgnoredByGit(projectPath, f)\n );\n if (unignored.length > 0) {\n const joined = unignored.join(', ');\n checks.push({\n name: 'gitignore_env_local',\n severity: 'error',\n message: `${joined} is not ignored by git — secrets would be pushed to GitLab on deploy`,\n fix: `Add \"${unignored.join('\" and \"')}\" to .gitignore`,\n });\n }\n\n return checks;\n}\n","import { execFileSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport function isGitRepo(projectPath: string): boolean {\n return existsSync(join(projectPath, '.git'));\n}\n\nexport function gitInit(projectPath: string): void {\n execFileSync('git', ['init'], { cwd: projectPath, stdio: 'pipe' });\n}\n\nexport function hasUncommittedChanges(projectPath: string): boolean {\n try {\n const status = execFileSync('git', ['status', '--porcelain'], {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n return status.trim().length > 0;\n } catch {\n return false;\n }\n}\n\nexport function gitAddAndCommit(projectPath: string, message: string): void {\n execFileSync('git', ['add', '-A'], { cwd: projectPath, stdio: 'pipe' });\n execFileSync('git', ['commit', '-m', message, '--allow-empty'], {\n cwd: projectPath,\n stdio: 'pipe',\n env: {\n ...process.env,\n GIT_AUTHOR_NAME: process.env.GIT_AUTHOR_NAME || 'rush-ai',\n GIT_AUTHOR_EMAIL: process.env.GIT_AUTHOR_EMAIL || 'rush-ai@rush.dev',\n GIT_COMMITTER_NAME: process.env.GIT_COMMITTER_NAME || 'rush-ai',\n GIT_COMMITTER_EMAIL:\n process.env.GIT_COMMITTER_EMAIL || 'rush-ai@rush.dev',\n },\n });\n}\n\nexport function setRemote(\n projectPath: string,\n remoteName: string,\n url: string\n): void {\n try {\n const remotes = execFileSync('git', ['remote'], {\n cwd: projectPath,\n encoding: 'utf-8',\n });\n if (remotes.split('\\n').includes(remoteName)) {\n execFileSync('git', ['remote', 'set-url', remoteName, url], {\n cwd: projectPath,\n stdio: 'pipe',\n });\n } else {\n execFileSync('git', ['remote', 'add', remoteName, url], {\n cwd: projectPath,\n stdio: 'pipe',\n });\n }\n } catch {\n execFileSync('git', ['remote', 'add', remoteName, url], {\n cwd: projectPath,\n stdio: 'pipe',\n });\n }\n}\n\nexport function gitPushUrl(\n projectPath: string,\n url: string\n): { success: boolean; stderr: string } {\n try {\n // Use `HEAD` (no explicit destination) instead of `HEAD:main`.\n //\n // The rush GitLab templates use `master` as the default branch; pushing\n // to `HEAD:main` silently CREATES a new `main` branch on the remote\n // while leaving `master` — the branch the pod actually tracks — at the\n // old commit. `git push` exits 0, the CLI reports success, the pod's\n // `git pull origin master` no-ops, and the user's code never arrives\n // (#1158).\n //\n // Plain `HEAD` (no `:<dst>`) delegates branch resolution to git's\n // default push behaviour: it pushes the current local branch to the\n // remote branch of the *same name*. This is the same shape a user's\n // `git push <url> HEAD` produces — master checkouts push to master,\n // main checkouts push to main, no upstream-tracking config required.\n // `--force` is preserved — the original intent (overwrite pod-side\n // agent commits) is unchanged.\n execFileSync('git', ['push', url, 'HEAD', '--force'], {\n cwd: projectPath,\n stdio: 'pipe',\n timeout: 300_000,\n maxBuffer: 10 * 1024 * 1024,\n env: {\n ...process.env,\n // 失败时不要弹密码 / SSH key 提示,否则在非交互环境(CI / 测试)\n // 会一直 hang 到外层 timeout(300s)才返回。\n GIT_TERMINAL_PROMPT: '0',\n GIT_ASKPASS: 'true',\n SSH_ASKPASS: 'true',\n },\n });\n return { success: true, stderr: '' };\n } catch (err: unknown) {\n const rawStderr =\n err && typeof err === 'object' && 'stderr' in err\n ? String((err as { stderr: Buffer }).stderr)\n : 'unknown error';\n return { success: false, stderr: sanitizeGitOutput(rawStderr) };\n }\n}\n\nexport function sanitizeGitOutput(text: string): string {\n return text.replace(/https?:\\/\\/[^@]*@/g, 'https://***@');\n}\n\n/**\n * Read `remote.origin.url` from the project's git config. Returns `null` when\n * the directory is not a git repo, the remote is unset, or the command fails.\n *\n * Used by `task push` to identify the rush project from a checked-out repo\n * (see #1149) without requiring `.rush/env.md` to be present — the git remote\n * is the authoritative source of \"which rush project does this working tree\n * belong to\".\n */\nexport function getRemoteUrl(\n projectPath: string,\n remoteName = 'origin'\n): string | null {\n try {\n const url = execFileSync(\n 'git',\n ['config', '--get', `remote.${remoteName}.url`],\n { cwd: projectPath, encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] }\n ).trim();\n return url.length > 0 ? url : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract a rush projectId from a git remote URL that points at the rush\n * GitLab namespace. Accepts both HTTPS (with or without an embedded token)\n * and SSH forms:\n *\n * https://gitlab-ee.zhenguanyu.com/rush/dev/<id>[.git]\n * https://user:token@gitlab-ee.zhenguanyu.com/rush/dev/<id>[.git]\n * git@gitlab-ee.zhenguanyu.com:rush/dev/<id>[.git]\n *\n * Returns `null` on no match. projectIds are lowercase alphanumerics — the\n * strict character class guards against accidentally consuming query strings\n * or path suffixes as the id.\n */\nexport function extractRushProjectId(remoteUrl: string): string | null {\n const match = remoteUrl.match(\n /gitlab-ee\\.zhenguanyu\\.com[/:]rush\\/dev\\/([a-z0-9]+)(?:\\.git)?(?:[/?#]|$)/\n );\n return match ? match[1] : null;\n}\n\n/**\n * 判断 git 是否已经在跟踪该文件。\n *\n * 用途:敏感文件(.env.local 之类)有可能以前 commit 过,之后才加进\n * .gitignore;此时 `.gitignore` 文本看似覆盖,但 git push 还是会带走它。\n * 用 git 自身的 `ls-files --error-unmatch` 判断才可靠。\n *\n * @param projectPath 项目根\n * @param relativePath 相对项目根的文件路径\n * @returns true = 已被 git tracking;false = 未 track(或 git 命令失败)\n */\nexport function isFileTrackedByGit(\n projectPath: string,\n relativePath: string\n): boolean {\n try {\n execFileSync('git', ['ls-files', '--error-unmatch', relativePath], {\n cwd: projectPath,\n stdio: 'pipe',\n });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * 用 git 自身判断路径是否会被 gitignore 规则忽略。\n *\n * `git check-ignore` 原生理解 .gitignore / .git/info/exclude / 全局 gitignore /\n * 负向模式、glob 等,比解析 .gitignore 文本更可靠。\n *\n * @returns true = git 会忽略(即推送时不会带上);false = 不忽略或判断失败\n */\nexport function isFileIgnoredByGit(\n projectPath: string,\n relativePath: string\n): boolean {\n try {\n execFileSync('git', ['check-ignore', '--quiet', relativePath], {\n cwd: projectPath,\n stdio: 'pipe',\n });\n // exit 0 = ignored\n return true;\n } catch {\n // exit 1 = not ignored;exit 128 = fatal(比如非 git 仓库)\n return false;\n }\n}\n","import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { DeployCheck } from '../types.js';\n\nexport type PackageManagerType = 'pnpm' | 'yarn' | 'npm' | null;\n\nexport function detectPackageManager(projectPath: string): PackageManagerType {\n if (existsSync(join(projectPath, 'pnpm-lock.yaml'))) return 'pnpm';\n if (existsSync(join(projectPath, 'yarn.lock'))) return 'yarn';\n if (existsSync(join(projectPath, 'package-lock.json'))) return 'npm';\n return null;\n}\n\nexport function checkLockFile(projectPath: string): DeployCheck[] {\n const checks: DeployCheck[] = [];\n const pm = detectPackageManager(projectPath);\n\n if (pm) {\n checks.push({\n name: 'lock_file_exists',\n severity: 'info',\n message: `Package manager: ${pm}`,\n });\n } else {\n checks.push({\n name: 'lock_file_exists',\n severity: 'warning',\n message:\n 'No lock file found (pnpm-lock.yaml, yarn.lock, or package-lock.json)',\n fix: 'Run `npm install` to generate package-lock.json',\n autoFixable: true,\n });\n }\n\n return checks;\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { DeployCheck } from '../types.js';\n\nexport interface PackageJsonInfo {\n exists: boolean;\n scripts?: Record<string, string>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\nexport function readPackageJson(projectPath: string): PackageJsonInfo {\n const pkgPath = join(projectPath, 'package.json');\n if (!existsSync(pkgPath)) {\n return { exists: false };\n }\n try {\n const raw = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n return {\n exists: true,\n scripts: raw.scripts,\n dependencies: raw.dependencies,\n devDependencies: raw.devDependencies,\n };\n } catch {\n return { exists: false };\n }\n}\n\nexport function checkPackageJson(projectPath: string): DeployCheck[] {\n const checks: DeployCheck[] = [];\n const pkg = readPackageJson(projectPath);\n\n if (!pkg.exists) {\n checks.push({\n name: 'package_json_exists',\n severity: 'error',\n message: 'package.json not found',\n fix: 'Run `npm init -y` to create one',\n });\n return checks;\n }\n\n if (!pkg.scripts?.dev) {\n checks.push({\n name: 'scripts_dev',\n severity: 'error',\n message: 'Missing \"dev\" script in package.json',\n fix: 'Add a dev script (e.g., \"dev\": \"vite\" or \"dev\": \"next dev\")',\n autoFixable: true,\n });\n }\n\n if (!pkg.scripts?.build) {\n checks.push({\n name: 'scripts_build',\n severity: 'warning',\n message:\n 'Missing \"build\" script in package.json — deployment may skip build step',\n fix: 'Add a build script (e.g., \"build\": \"vite build\" or \"build\": \"next build\")',\n autoFixable: true,\n });\n }\n\n if (pkg.scripts?.build) {\n const build = pkg.scripts.build;\n if (/^(?:vue-)?tsc\\s.*&&/.test(build)) {\n checks.push({\n name: 'build_tsc_prefix',\n severity: 'info',\n message: `Build script starts with tsc: \"${build}\" — Rush will auto-strip the tsc check during build`,\n });\n }\n }\n\n return checks;\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { DeployCheck } from '../types.js';\nimport type { FrameworkType } from './framework.js';\n\nconst VITE_CONFIG_FILES = ['vite.config.ts', 'vite.config.js'];\n\nfunction readDevScript(projectPath: string): string | null {\n const pkgPath = join(projectPath, 'package.json');\n if (!existsSync(pkgPath)) return null;\n try {\n const raw = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n return raw.scripts?.dev ?? null;\n } catch {\n return null;\n }\n}\n\nfunction viteConfigReadsPort(projectPath: string): boolean {\n for (const file of VITE_CONFIG_FILES) {\n const filePath = join(projectPath, file);\n if (!existsSync(filePath)) continue;\n try {\n const content = readFileSync(filePath, 'utf-8');\n if (/process\\.env\\.PORT/.test(content)) return true;\n if (/env\\s*\\(\\s*['\"]PORT['\"]\\s*\\)/.test(content)) return true;\n } catch {\n // ignore read errors\n }\n }\n return false;\n}\n\nfunction devScriptReadsPort(devScript: string): boolean {\n if (/\\$PORT|\\$\\{PORT\\}|%PORT%/.test(devScript)) return true;\n if (/--port\\s+\\$/.test(devScript)) return true;\n return false;\n}\n\nexport function checkPortEnv(\n projectPath: string,\n framework: FrameworkType\n): DeployCheck[] {\n const checks: DeployCheck[] = [];\n\n if (framework === 'nextjs') {\n checks.push({\n name: 'port_env',\n severity: 'info',\n message: 'Next.js natively supports PORT env — no config change needed',\n });\n return checks;\n }\n\n if (framework === 'vite') {\n if (viteConfigReadsPort(projectPath)) {\n checks.push({\n name: 'port_env',\n severity: 'info',\n message: 'Vite config reads process.env.PORT',\n });\n return checks;\n }\n\n const devScript = readDevScript(projectPath);\n if (devScript && devScriptReadsPort(devScript)) {\n checks.push({\n name: 'port_env',\n severity: 'info',\n message: 'Dev script references PORT',\n });\n return checks;\n }\n\n checks.push({\n name: 'port_env',\n severity: 'error',\n message:\n 'Vite config does not read process.env.PORT — Rush Pod passes PORT=8000',\n fix: 'Add `server: { port: parseInt(process.env.PORT || \"5173\") }` to vite.config.ts',\n autoFixable: true,\n });\n return checks;\n }\n\n // Unknown framework — check dev script heuristically\n const devScript = readDevScript(projectPath);\n if (devScript && devScriptReadsPort(devScript)) {\n checks.push({\n name: 'port_env',\n severity: 'info',\n message: 'Dev script references PORT',\n });\n } else {\n checks.push({\n name: 'port_env',\n severity: 'error',\n message: 'Dev server must read PORT env var — Rush Pod passes PORT=8000',\n fix: 'Ensure your dev server reads process.env.PORT or accepts --port $PORT',\n autoFixable: false,\n });\n }\n\n return checks;\n}\n","import { existsSync } from 'node:fs';\nimport { appendFile, mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, dirname, join } from 'node:path';\nimport type { Command } from 'commander';\nimport { output } from '../../output/logger.js';\n\nconst MARKER_BEGIN = '###-begin-rush-ai-completion-###';\nconst MARKER_END = '###-end-rush-ai-completion-###';\n\nconst COMPLETION_SCRIPTS: Record<string, string> = {\n bash: `\n${MARKER_BEGIN}\n_rush_cli_completion() {\n local cur=\"\\${COMP_WORDS[COMP_CWORD]}\"\n local prev=\"\\${COMP_WORDS[COMP_CWORD-1]}\"\n local cmds=\"auth agent task mcp plugin completion config doctor\"\n\n case \"$prev\" in\n rush-ai) COMPREPLY=( $(compgen -W \"$cmds\" -- \"$cur\") ) ;;\n auth) COMPREPLY=( $(compgen -W \"login logout status\" -- \"$cur\") ) ;;\n agent) COMPREPLY=( $(compgen -W \"list info\" -- \"$cur\") ) ;;\n task) COMPREPLY=( $(compgen -W \"run create status result list watch cancel\" -- \"$cur\") ) ;;\n plugin) COMPREPLY=( $(compgen -W \"install list status update uninstall\" -- \"$cur\") ) ;;\n completion) COMPREPLY=( $(compgen -W \"install bash zsh fish\" -- \"$cur\") ) ;;\n config) COMPREPLY=( $(compgen -W \"show set use create delete list\" -- \"$cur\") ) ;;\n esac\n}\ncomplete -F _rush_cli_completion rush-ai\n${MARKER_END}`.trimStart(),\n\n zsh: `\n${MARKER_BEGIN}\n_rush_cli_completion() {\n local -a cmds\n cmds=(\n 'auth:Authentication commands'\n 'agent:Agent management'\n 'task:Task operations'\n 'mcp:MCP server'\n 'plugin:Plugin management'\n 'completion:Shell completion'\n 'config:Configuration management'\n 'doctor:System diagnostics'\n )\n _describe 'command' cmds\n}\ncompdef _rush_cli_completion rush-ai\n${MARKER_END}`.trimStart(),\n\n fish: `\n${MARKER_BEGIN}\ncomplete -c rush-ai -n '__fish_use_subcommand' -a auth -d 'Authentication'\ncomplete -c rush-ai -n '__fish_use_subcommand' -a agent -d 'Agent management'\ncomplete -c rush-ai -n '__fish_use_subcommand' -a task -d 'Task operations'\ncomplete -c rush-ai -n '__fish_use_subcommand' -a mcp -d 'MCP server'\ncomplete -c rush-ai -n '__fish_use_subcommand' -a plugin -d 'Plugin management'\ncomplete -c rush-ai -n '__fish_use_subcommand' -a completion -d 'Shell completion'\ncomplete -c rush-ai -n '__fish_use_subcommand' -a config -d 'Configuration management'\ncomplete -c rush-ai -n '__fish_use_subcommand' -a doctor -d 'System diagnostics'\ncomplete -c rush-ai -n '__fish_seen_subcommand_from auth' -a 'login logout status'\ncomplete -c rush-ai -n '__fish_seen_subcommand_from agent' -a 'list info'\ncomplete -c rush-ai -n '__fish_seen_subcommand_from task' -a 'run create status result list watch cancel'\ncomplete -c rush-ai -n '__fish_seen_subcommand_from plugin' -a 'install list status update uninstall'\ncomplete -c rush-ai -n '__fish_seen_subcommand_from completion' -a 'install bash zsh fish'\ncomplete -c rush-ai -n '__fish_seen_subcommand_from config' -a 'show set use create delete list'\n${MARKER_END}`.trimStart(),\n};\n\nfunction detectShell(): string | null {\n const shell = process.env.SHELL;\n if (!shell) return null;\n const name = basename(shell);\n return COMPLETION_SCRIPTS[name] ? name : null;\n}\n\nfunction getShellRcFile(shell: string): string {\n const home = homedir();\n switch (shell) {\n case 'bash':\n return existsSync(join(home, '.bashrc'))\n ? join(home, '.bashrc')\n : join(home, '.bash_profile');\n case 'zsh':\n return join(home, '.zshrc');\n case 'fish':\n return join(home, '.config', 'fish', 'completions', 'rush-ai.fish');\n default:\n throw new Error(`Unsupported shell: ${shell}`);\n }\n}\n\nexport function registerCompletionCommand(program: Command): void {\n const completion = program\n .command('completion')\n .description('Shell completion utilities');\n\n // Print completion script to stdout\n completion\n .command('bash')\n .description('Print bash completion script')\n .action(() => {\n output.log(COMPLETION_SCRIPTS.bash);\n });\n\n completion\n .command('zsh')\n .description('Print zsh completion script')\n .action(() => {\n output.log(COMPLETION_SCRIPTS.zsh);\n });\n\n completion\n .command('fish')\n .description('Print fish completion script')\n .action(() => {\n output.log(COMPLETION_SCRIPTS.fish);\n });\n\n // Auto-install subcommand\n completion\n .command('install')\n .description('Auto-install shell completion to your shell rc file')\n .option('--shell <shell>', 'Shell type (auto-detect if omitted)')\n .action(async (opts: { shell?: string }) => {\n const shell = opts.shell ?? detectShell();\n if (!shell) {\n output.error('Could not detect shell. Use --shell <bash|zsh|fish>.');\n process.exit(1);\n }\n\n if (!COMPLETION_SCRIPTS[shell]) {\n output.error(\n `Unsupported shell: ${shell}. Supported: bash, zsh, fish.`\n );\n process.exit(1);\n }\n\n const script = COMPLETION_SCRIPTS[shell];\n const rcFile = getShellRcFile(shell);\n\n // Fish: write standalone completion file\n if (shell === 'fish') {\n try {\n await mkdir(dirname(rcFile), { recursive: true });\n await writeFile(rcFile, script + '\\n');\n output.success(`Fish completion installed to ${rcFile}`);\n } catch {\n output.error(`Failed to write to ${rcFile}`);\n output.dim(\n ' You can manually add the following to your fish completions:'\n );\n output.log(script);\n }\n return;\n }\n\n // Bash/Zsh: append to rc file (idempotent)\n try {\n const existing = await readFile(rcFile, 'utf-8');\n if (existing.includes(MARKER_BEGIN)) {\n output.info(`Completion already installed in ${rcFile}`);\n return;\n }\n } catch {\n // File doesn't exist — will be created by appendFile\n }\n\n try {\n await appendFile(rcFile, '\\n' + script + '\\n');\n output.success(`Completion installed to ${rcFile}`);\n output.dim(\n ` Run \\`source ${rcFile}\\` or restart your shell to activate.`\n );\n } catch {\n output.error(`Failed to write to ${rcFile}`);\n output.dim(\n ' You can manually add the following to your shell rc file:'\n );\n output.log(script);\n }\n });\n}\n","import type { Command } from 'commander';\nimport { formatTable } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport {\n checkAuthSourceMismatch,\n createProfile,\n deleteProfile,\n getActiveProfile,\n getAuthConfig,\n getConfigDir,\n getGlobalConfig,\n getProfileAuth,\n getProfileConfig,\n listProfiles,\n setActiveProfile,\n setGlobalConfig,\n} from '../../util/config.js';\n\nconst SETTABLE_KEYS = ['api', 'collectMetrics', 'currentTeam'] as const;\ntype SettableKey = (typeof SETTABLE_KEYS)[number];\n\nfunction maskToken(token: string | null): string {\n if (!token) return '(none)';\n if (token.length <= 8) return '****';\n return `${token.slice(0, 4)}...****`;\n}\n\nexport function registerConfigCommand(program: Command): void {\n const config = program\n .command('config')\n .description('Manage CLI configuration and profiles');\n\n config\n .command('show')\n .description('Show current configuration')\n .action(() => {\n const jsonMode = program.opts().json;\n const profile = getActiveProfile();\n const globalConf = getGlobalConfig();\n const authConf = getAuthConfig();\n const configDir = getConfigDir();\n\n const apiOverride = process.env.RUSH_API_URL;\n const profileOverride = process.env.RUSH_PROFILE;\n\n if (jsonMode) {\n output.log(\n JSON.stringify(\n {\n profile,\n api: apiOverride ?? globalConf.api,\n apiOverridden: Boolean(apiOverride),\n profileOverridden: Boolean(profileOverride),\n authMethod: authConf.method,\n collectMetrics: globalConf.collectMetrics,\n currentTeam: globalConf.currentTeam,\n configDir,\n },\n null,\n 2\n )\n );\n return;\n }\n\n output.log(output.bold('Configuration'));\n output.newline();\n\n const profileLabel = profileOverride\n ? `${profile} (overridden by RUSH_PROFILE env)`\n : profile;\n output.log(` Active profile: ${profileLabel}`);\n\n const apiLabel = apiOverride\n ? `${apiOverride} (overridden by RUSH_API_URL env)`\n : globalConf.api;\n output.log(` API: ${apiLabel}`);\n\n output.log(\n ` Auth method: ${authConf.method ?? '(not authenticated)'}`\n );\n output.log(` Token: ${maskToken(authConf.token)}`);\n output.log(\n ` Metrics: ${globalConf.collectMetrics ? 'enabled' : 'disabled'}`\n );\n output.log(` Config dir: ${configDir}`);\n });\n\n config\n .command('set')\n .description('Set a configuration value')\n .argument('<key>', `Config key (${SETTABLE_KEYS.join(', ')})`)\n .argument('<value>', 'Config value')\n .action((key: string, value: string) => {\n const jsonMode = program.opts().json;\n const profile = getActiveProfile();\n\n if (!SETTABLE_KEYS.includes(key as SettableKey)) {\n output.error(\n `Unknown config key '${key}'. Valid keys: ${SETTABLE_KEYS.join(', ')}`\n );\n process.exit(1);\n }\n\n let typedValue: unknown = value;\n\n if (key === 'collectMetrics') {\n if (value !== 'true' && value !== 'false') {\n output.error(\"'collectMetrics' must be 'true' or 'false'.\");\n process.exit(1);\n }\n typedValue = value === 'true';\n }\n\n if (key === 'currentTeam' && value === 'null') {\n typedValue = null;\n }\n\n // Compute the mismatch against the NEW URL BEFORE writing so the\n // comparison uses the same normalization (trailing slash, case) as\n // `requireAuth()`'s own check — see #832. Passing `overrideCurrentUrl`\n // is the simpler path than mutating state and then comparing.\n const mismatch =\n key === 'api' ? checkAuthSourceMismatch(String(typedValue)) : null;\n\n setGlobalConfig({ [key]: typedValue });\n\n if (jsonMode) {\n output.log(JSON.stringify({ key, value: typedValue, profile }));\n } else {\n output.success(\n `Updated '${key}' to '${String(typedValue)}' (profile: ${profile})`\n );\n\n if (mismatch) {\n output.newline();\n output.warn(\n `Stored token was issued against ${mismatch.stored} — it may not work on ${mismatch.current}.`\n );\n output.dim('Run `rush-ai auth login` to re-authenticate.');\n }\n }\n });\n\n config\n .command('use')\n .description('Switch active profile')\n .argument('<profile>', 'Profile name to switch to')\n .action((profileName: string) => {\n const jsonMode = program.opts().json;\n\n try {\n setActiveProfile(profileName);\n } catch (err) {\n output.error(\n err instanceof Error ? err.message : 'Failed to switch profile'\n );\n const available = listProfiles();\n if (!jsonMode) {\n output.dim(`Available profiles: ${available.join(', ')}`);\n output.dim(\n `Run \\`rush-ai config create ${profileName}\\` to create it.`\n );\n }\n process.exit(1);\n }\n\n const globalConf = getGlobalConfig();\n\n if (jsonMode) {\n output.log(\n JSON.stringify({\n profile: profileName,\n api: globalConf.api,\n })\n );\n } else {\n output.success(`Switched to profile '${profileName}'`);\n output.dim(`API: ${globalConf.api}`);\n }\n });\n\n config\n .command('create')\n .description('Create a new profile')\n .argument('<profile>', 'Profile name')\n .option('--api <url>', 'API base URL')\n .action((profileName: string, options: { api?: string }) => {\n const jsonMode = program.opts().json;\n\n try {\n createProfile(\n profileName,\n options.api ? { api: options.api } : undefined\n );\n } catch (err) {\n output.error(\n err instanceof Error ? err.message : 'Failed to create profile'\n );\n process.exit(1);\n }\n\n if (jsonMode) {\n output.log(JSON.stringify({ profile: profileName, created: true }));\n } else {\n output.success(`Created profile '${profileName}'`);\n output.dim(\n `Run \\`rush-ai config use ${profileName}\\` to switch to it.`\n );\n }\n });\n\n config\n .command('delete')\n .description('Delete a profile')\n .argument('<profile>', 'Profile name to delete')\n .action((profileName: string) => {\n const jsonMode = program.opts().json;\n\n try {\n deleteProfile(profileName);\n } catch (err) {\n output.error(\n err instanceof Error ? err.message : 'Failed to delete profile'\n );\n process.exit(1);\n }\n\n if (jsonMode) {\n output.log(JSON.stringify({ profile: profileName, deleted: true }));\n } else {\n output.success(`Deleted profile '${profileName}'`);\n }\n });\n\n config\n .command('list')\n .alias('ls')\n .description('List all profiles')\n .action(() => {\n const jsonMode = program.opts().json;\n const profiles = listProfiles();\n const active = getActiveProfile();\n\n if (jsonMode) {\n const data = profiles.map((name) => {\n const conf = getProfileConfig(name);\n const auth = getProfileAuth(name);\n return {\n name,\n active: name === active,\n api: conf?.api ?? '',\n authMethod: auth.method,\n loggedIn: Boolean(auth.token),\n };\n });\n output.log(JSON.stringify(data, null, 2));\n return;\n }\n\n if (profiles.length === 0) {\n output.info('No profiles configured.');\n return;\n }\n\n const rows = profiles.map((name) => {\n const conf = getProfileConfig(name);\n const auth = getProfileAuth(name);\n const marker = name === active ? '*' : ' ';\n const authStatus = auth.token\n ? `${auth.method ?? 'unknown'} (logged in)`\n : 'not authenticated';\n return {\n ' ': marker,\n Profile: name,\n API: conf?.api ?? '',\n Auth: authStatus,\n };\n });\n\n output.log(formatTable(rows));\n });\n}\n","import chalk from 'chalk';\nimport type { Command } from 'commander';\nimport { output } from '../../output/logger.js';\nimport { checkAuth } from './checks/auth.js';\nimport { checkConfig } from './checks/config.js';\nimport { checkConnectivity } from './checks/connectivity.js';\nimport { checkEnvironment } from './checks/environment.js';\nimport { checkPlugins } from './checks/plugins.js';\nimport type { CheckRunner, DoctorCheck } from './types.js';\n\nconst CHECK_RUNNERS: CheckRunner[] = [\n checkEnvironment,\n checkAuth,\n checkConnectivity,\n checkConfig,\n checkPlugins,\n];\n\nasync function runAllChecks(): Promise<DoctorCheck[]> {\n const checks: DoctorCheck[] = [];\n for (const runner of CHECK_RUNNERS) {\n try {\n const results = await runner();\n checks.push(...results);\n } catch (err) {\n // Isolate runner failures so remaining checks still run\n checks.push({\n name: 'runner_error',\n group: 'Environment',\n status: 'fail',\n label: 'Check error',\n value: `A check group failed: ${err instanceof Error ? err.message : 'unknown error'}`,\n });\n }\n }\n return checks;\n}\n\nfunction getSummary(checks: DoctorCheck[]): {\n pass: number;\n warn: number;\n fail: number;\n} {\n let pass = 0;\n let warn = 0;\n let fail = 0;\n for (const c of checks) {\n if (c.status === 'pass') pass++;\n else if (c.status === 'warn') warn++;\n else if (c.status === 'fail') fail++;\n // info is excluded from summary\n }\n return { pass, warn, fail };\n}\n\nfunction statusIcon(status: DoctorCheck['status']): string {\n switch (status) {\n case 'pass':\n return chalk.green('✓');\n case 'warn':\n return chalk.yellow('!');\n case 'fail':\n return chalk.red('✗');\n case 'info':\n return chalk.dim('·');\n }\n}\n\nconst LABEL_WIDTH = 20;\n\nfunction displayResults(checks: DoctorCheck[]): void {\n // Group checks\n const groups = new Map<string, DoctorCheck[]>();\n for (const check of checks) {\n const existing = groups.get(check.group) ?? [];\n existing.push(check);\n groups.set(check.group, existing);\n }\n\n output.newline();\n\n for (const [groupName, groupChecks] of groups) {\n output.log(` ${chalk.bold(groupName)}`);\n for (const check of groupChecks) {\n const icon = statusIcon(check.status);\n const label = check.label.padEnd(LABEL_WIDTH);\n output.log(` ${icon} ${label} ${check.value}`);\n }\n output.log('');\n }\n\n const summary = getSummary(checks);\n const parts: string[] = [];\n parts.push(chalk.green(`${summary.pass} passed`));\n if (summary.warn > 0) parts.push(chalk.yellow(`${summary.warn} warnings`));\n if (summary.fail > 0) parts.push(chalk.red(`${summary.fail} failed`));\n if (summary.warn === 0 && summary.fail === 0) {\n parts.push('0 warnings');\n parts.push('0 failed');\n }\n output.log(` ${chalk.bold('Summary:')} ${parts.join(', ')}`);\n\n if (summary.fail > 0 || summary.warn > 0) {\n const fixable = checks.filter(\n (c) => c.status !== 'pass' && c.status !== 'info' && c.autoFix\n );\n if (fixable.length > 0) {\n output.log(\n chalk.dim(` Run \\`rush-ai doctor --fix\\` to attempt auto-fixes.`)\n );\n }\n }\n\n output.log('');\n}\n\nfunction outputJson(checks: DoctorCheck[]): void {\n const summary = getSummary(checks);\n const result = {\n checks: checks.map((c) => ({\n name: c.name,\n group: c.group,\n status: c.status,\n label: c.label,\n value: c.value,\n ...(c.fix ? { fix: c.fix } : {}),\n })),\n summary,\n };\n output.log(JSON.stringify(result, null, 2));\n}\n\nasync function runAutoFixes(checks: DoctorCheck[]): Promise<void> {\n const fixable = checks.filter(\n (c) => c.status !== 'pass' && c.status !== 'info' && c.autoFix\n );\n\n if (fixable.length === 0) {\n output.info('No auto-fixable issues found.');\n return;\n }\n\n output.newline();\n for (const check of fixable) {\n output.info(`Fixing: ${check.label}...`);\n try {\n await check.autoFix!();\n output.success(`Fixed: ${check.label}`);\n } catch (err) {\n output.error(\n `Failed to fix ${check.label}: ${err instanceof Error ? err.message : 'unknown error'}`\n );\n }\n }\n output.newline();\n}\n\nexport function registerDoctorCommand(program: Command): void {\n program\n .command('doctor')\n .description('Diagnose environment, auth, and connectivity issues')\n .option('--fix', 'Attempt to auto-fix detected issues')\n .action(async (opts: { fix?: boolean }) => {\n const jsonMode = program.opts().json;\n\n let checks = await runAllChecks();\n\n if (opts.fix) {\n await runAutoFixes(checks);\n // Re-run all checks after fixes\n checks = await runAllChecks();\n }\n\n if (jsonMode) {\n outputJson(checks);\n } else {\n displayResults(checks);\n }\n\n const hasFail = checks.some((c) => c.status === 'fail');\n if (hasFail) {\n process.exit(1);\n }\n });\n}\n","import { getAuthMethod } from '../../../util/auth.js';\nimport { getAuthConfig } from '../../../util/config.js';\nimport type { CheckRunner, DoctorCheck } from '../types.js';\n\nexport const checkAuth: CheckRunner = async () => {\n const checks: DoctorCheck[] = [];\n const method = getAuthMethod();\n\n // Auth method\n if (method === 'platform_token') {\n checks.push({\n name: 'auth_method',\n group: 'Authentication',\n status: 'pass',\n label: 'Auth method',\n value: 'Platform Token (browser login)',\n });\n } else if (method === 'cas') {\n checks.push({\n name: 'auth_method',\n group: 'Authentication',\n status: 'pass',\n label: 'Auth method',\n value: 'CAS PKCE',\n });\n } else if (method === 'api_key') {\n checks.push({\n name: 'auth_method',\n group: 'Authentication',\n status: 'pass',\n label: 'Auth method',\n value: 'API Key',\n });\n } else {\n checks.push({\n name: 'auth_method',\n group: 'Authentication',\n status: 'warn',\n label: 'Auth method',\n value: 'Not authenticated',\n fix: 'Run `rush-ai auth login` to authenticate',\n });\n }\n\n // Token status — depends on auth method\n if (method === 'api_key') {\n checks.push({\n name: 'token_status',\n group: 'Authentication',\n status: 'info',\n label: 'Token status',\n value: 'Using API key auth',\n });\n } else if (method === 'cas' || method === 'platform_token') {\n const auth = getAuthConfig();\n if (auth.token && auth.expiresAt) {\n const remaining = auth.expiresAt - Date.now();\n if (remaining > 0) {\n const hours = Math.floor(remaining / (1000 * 60 * 60));\n const minutes = Math.floor(\n (remaining % (1000 * 60 * 60)) / (1000 * 60)\n );\n const timeStr = hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;\n checks.push({\n name: 'token_status',\n group: 'Authentication',\n status: 'pass',\n label: 'Token status',\n value: `Valid (expires in ${timeStr})`,\n });\n } else {\n checks.push({\n name: 'token_status',\n group: 'Authentication',\n status: 'warn',\n label: 'Token status',\n value: 'Expired',\n fix: 'Run `rush-ai auth login` to re-authenticate',\n });\n }\n } else if (auth.token) {\n // Token exists but no expiry info\n checks.push({\n name: 'token_status',\n group: 'Authentication',\n status: 'pass',\n label: 'Token status',\n value: 'Valid (no expiry set)',\n });\n } else {\n checks.push({\n name: 'token_status',\n group: 'Authentication',\n status: 'warn',\n label: 'Token status',\n value: 'No token found',\n fix: 'Run `rush-ai auth login` to authenticate',\n });\n }\n } else {\n // Not authenticated\n checks.push({\n name: 'token_status',\n group: 'Authentication',\n status: 'warn',\n label: 'Token status',\n value: 'Not authenticated',\n fix: 'Run `rush-ai auth login` to authenticate',\n });\n }\n\n // API Key env\n const apiKey = process.env.RUSH_API_KEY;\n checks.push({\n name: 'api_key',\n group: 'Authentication',\n status: 'info',\n label: 'API Key',\n value: apiKey ? 'Set (RUSH_API_KEY)' : 'Not set',\n });\n\n return checks;\n};\n","import { accessSync, constants, existsSync, mkdirSync } from 'node:fs';\nimport {\n getAuthConfig,\n getConfigDir,\n getGlobalConfig,\n} from '../../../util/config.js';\nimport type { CheckRunner, DoctorCheck } from '../types.js';\n\nexport const checkConfig: CheckRunner = async () => {\n const checks: DoctorCheck[] = [];\n const configDir = getConfigDir();\n\n // Config directory existence + writability\n const dirExists = existsSync(configDir);\n let writable = false;\n if (dirExists) {\n try {\n accessSync(configDir, constants.W_OK);\n writable = true;\n } catch {\n // not writable\n }\n }\n\n if (dirExists && writable) {\n checks.push({\n name: 'config_dir',\n group: 'Configuration',\n status: 'pass',\n label: 'Config directory',\n value: configDir,\n });\n } else if (dirExists && !writable) {\n checks.push({\n name: 'config_dir',\n group: 'Configuration',\n status: 'fail',\n label: 'Config directory',\n value: `${configDir} (not writable)`,\n fix: `Check permissions on ${configDir}`,\n });\n } else {\n checks.push({\n name: 'config_dir',\n group: 'Configuration',\n status: 'fail',\n label: 'Config directory',\n value: `${configDir} (not found)`,\n fix: `Run \\`rush-ai doctor --fix\\` to create it`,\n autoFix: async () => {\n mkdirSync(configDir, { recursive: true });\n },\n });\n }\n\n // Config files validity\n try {\n getGlobalConfig();\n getAuthConfig();\n checks.push({\n name: 'config_validity',\n group: 'Configuration',\n status: 'pass',\n label: 'Config files',\n value: 'Valid',\n });\n } catch (err) {\n checks.push({\n name: 'config_validity',\n group: 'Configuration',\n status: 'fail',\n label: 'Config files',\n value: `Error: ${err instanceof Error ? err.message : 'unknown'}`,\n fix: 'Check config files in ' + configDir,\n });\n }\n\n // API URL\n try {\n const config = getGlobalConfig();\n checks.push({\n name: 'api_url',\n group: 'Configuration',\n status: 'info',\n label: 'API URL',\n value: config.api,\n });\n } catch {\n checks.push({\n name: 'api_url',\n group: 'Configuration',\n status: 'warn',\n label: 'API URL',\n value: 'Unable to read',\n });\n }\n\n return checks;\n};\n","import { getGlobalConfig } from '../../../util/config.js';\nimport type { CheckRunner, DoctorCheck } from '../types.js';\n\nexport const checkConnectivity: CheckRunner = async () => {\n const checks: DoctorCheck[] = [];\n const config = getGlobalConfig();\n const apiUrl = process.env.RUSH_API_URL ?? config.api;\n\n // API endpoint — raw fetch without auth, measure RTT\n try {\n const start = Date.now();\n const response = await fetch(apiUrl, {\n method: 'HEAD',\n signal: AbortSignal.timeout(5000),\n });\n const rtt = Date.now() - start;\n\n // Any HTTP response (even 401/403) means the server is reachable\n checks.push({\n name: 'api_endpoint',\n group: 'Connectivity',\n status: 'pass',\n label: 'API endpoint',\n value: `${apiUrl} (${rtt}ms, HTTP ${response.status})`,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Unknown error';\n const isTimeout = message.includes('timeout') || message.includes('abort');\n checks.push({\n name: 'api_endpoint',\n group: 'Connectivity',\n status: 'fail',\n label: 'API endpoint',\n value: isTimeout\n ? `${apiUrl} (timeout after 5s)`\n : `${apiUrl} (${message})`,\n fix: 'Check your network connection and API URL in config',\n });\n }\n\n // Proxy environment variables\n const httpProxy = process.env.HTTP_PROXY || process.env.http_proxy;\n const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy;\n\n if (httpProxy || httpsProxy) {\n const parts: string[] = [];\n if (httpProxy) parts.push(`HTTP_PROXY=${maskUrl(httpProxy)}`);\n if (httpsProxy) parts.push(`HTTPS_PROXY=${maskUrl(httpsProxy)}`);\n checks.push({\n name: 'proxy',\n group: 'Connectivity',\n status: 'info',\n label: 'Proxy',\n value: parts.join(', '),\n });\n } else {\n checks.push({\n name: 'proxy',\n group: 'Connectivity',\n status: 'info',\n label: 'Proxy',\n value: 'Not configured',\n });\n }\n\n return checks;\n};\n\n/** Mask proxy URL credentials: http://user:pass@host → http://***@host */\nfunction maskUrl(url: string): string {\n try {\n const parsed = new URL(url);\n if (parsed.username || parsed.password) {\n parsed.username = '***';\n parsed.password = '';\n return parsed.toString().replace(/\\/$/, '');\n }\n // URL parsed but no standard credentials detected — check for @ in raw string\n // (non-standard schemes like `foo:user@host` won't expose username/password)\n if (url.includes('@')) {\n return url.replace(/[^/@]+@/, '***@');\n }\n return parsed.toString().replace(/\\/$/, '');\n } catch {\n // If URL parsing fails, strip anything that looks like credentials\n if (url.includes('@')) {\n return url.replace(/[^/@]+@/, '***@');\n }\n return url;\n }\n}\n","import { execFileSync } from 'node:child_process';\nimport { arch, platform, release, type } from 'node:os';\nimport { VERSION } from '../../../version.js';\nimport type { CheckRunner, DoctorCheck } from '../types.js';\n\nfunction formatOS(): string {\n const p = platform();\n const r = release();\n const a = arch();\n\n let osName: string;\n switch (p) {\n case 'darwin':\n osName = 'macOS';\n break;\n case 'win32':\n osName = 'Windows';\n break;\n case 'linux':\n osName = 'Linux';\n break;\n default:\n osName = type();\n }\n\n return `${osName} ${r} (${p} ${a})`;\n}\n\nconst KNOWN_SHELLS = ['bash', 'zsh', 'fish', 'sh', 'dash', 'ksh', 'tcsh'];\n\nfunction detectShell(): { name: string; version: string | null } {\n const shell = process.env.SHELL;\n if (!shell) {\n return { name: 'unknown', version: null };\n }\n\n const name = shell.split('/').pop() ?? shell;\n\n // Only execute known shells to avoid running arbitrary binaries\n if (!KNOWN_SHELLS.includes(name)) {\n return { name, version: null };\n }\n\n let version: string | null = null;\n\n try {\n const raw = execFileSync(shell, ['--version'], {\n timeout: 3000,\n stdio: ['pipe', 'pipe', 'pipe'],\n encoding: 'utf-8',\n });\n // Extract first line, try to find version number\n const firstLine = raw.split('\\n')[0] ?? '';\n const match = firstLine.match(/(\\d+\\.\\d+[\\d.]*)/);\n version = match?.[1] ?? null;\n } catch {\n // Some shells (e.g. fish) use --version differently, or may fail\n }\n\n return { name, version };\n}\n\nexport const checkEnvironment: CheckRunner = async () => {\n const checks: DoctorCheck[] = [];\n\n // CLI version\n checks.push({\n name: 'cli_version',\n group: 'Environment',\n status: 'info',\n label: 'CLI version',\n value: VERSION,\n });\n\n // Node.js version\n const nodeVersion = process.version;\n const major = parseInt(nodeVersion.slice(1), 10);\n const nodeOk = major >= 18;\n checks.push({\n name: 'node_version',\n group: 'Environment',\n status: nodeOk ? 'pass' : 'fail',\n label: 'Node.js version',\n value: `${nodeVersion}${nodeOk ? '' : ' (>=18.0.0 required)'}`,\n fix: nodeOk ? undefined : 'Upgrade Node.js to version 18 or higher',\n });\n\n // OS\n checks.push({\n name: 'os',\n group: 'Environment',\n status: 'info',\n label: 'OS',\n value: formatOS(),\n });\n\n // Shell\n const shell = detectShell();\n const shellValue =\n shell.name === 'unknown'\n ? 'Unknown'\n : shell.version\n ? `${shell.name} ${shell.version}`\n : shell.name;\n checks.push({\n name: 'shell',\n group: 'Environment',\n status: shell.name === 'unknown' ? 'warn' : 'pass',\n label: 'Shell',\n value: shellValue,\n });\n\n // Terminal\n const terminal = process.env.TERM_PROGRAM ?? 'Unknown';\n checks.push({\n name: 'terminal',\n group: 'Environment',\n status: 'info',\n label: 'Terminal',\n value: terminal,\n });\n\n return checks;\n};\n","import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { resolve } from 'node:path';\nimport { getGlobalConfig } from '../../../util/config.js';\nimport type { CheckRunner, DoctorCheck } from '../types.js';\n\ninterface PluginManifest {\n name: string;\n version: string;\n type: 'claude-code' | 'cursor';\n installedAt: string;\n}\n\ninterface InstalledPlugins {\n plugins: Record<string, PluginManifest>;\n}\n\nconst PLUGINS_FILE = resolve(homedir(), '.rush', 'plugins', 'installed.json');\n\n/** Map plugin type to its MCP config file path */\nconst MCP_CONFIG_PATHS: Record<string, string> = {\n 'claude-code': resolve(homedir(), '.claude', 'settings.json'),\n cursor: resolve(homedir(), '.cursor', 'mcp.json'),\n};\n\nfunction safeReadJson<T>(filePath: string, fallback: T): T {\n if (!existsSync(filePath)) return fallback;\n try {\n return JSON.parse(readFileSync(filePath, 'utf-8')) as T;\n } catch {\n return fallback;\n }\n}\n\nfunction injectMcpConfig(configPath: string): void {\n const config = safeReadJson<Record<string, unknown>>(configPath, {});\n const mcpServers = (config.mcpServers ?? {}) as Record<string, unknown>;\n const globalConfig = getGlobalConfig();\n\n mcpServers.rush = {\n command: 'rush-ai',\n args: ['mcp', 'serve'],\n env: {\n RUSH_API_URL: globalConfig.api,\n },\n };\n config.mcpServers = mcpServers;\n writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');\n}\n\nfunction checkMcpForPlugin(\n name: string,\n manifest: PluginManifest\n): DoctorCheck {\n const configPath = MCP_CONFIG_PATHS[manifest.type];\n if (!configPath) {\n return {\n name: `plugin_${name}`,\n group: 'Plugins',\n status: 'warn',\n label: name,\n value: `Installed (v${manifest.version}), unknown type: ${manifest.type}`,\n };\n }\n\n if (!existsSync(configPath)) {\n return {\n name: `plugin_${name}`,\n group: 'Plugins',\n status: 'warn',\n label: name,\n value: `Installed, MCP config missing (${configPath})`,\n fix: `Run \\`rush-ai plugin install ${name}\\``,\n autoFix: async () => {\n injectMcpConfig(configPath);\n },\n };\n }\n\n try {\n const config = JSON.parse(readFileSync(configPath, 'utf-8'));\n const servers = config.mcpServers as Record<string, unknown> | undefined;\n\n if (!servers || !('rush' in servers)) {\n return {\n name: `plugin_${name}`,\n group: 'Plugins',\n status: 'warn',\n label: name,\n value: 'Installed, MCP server not configured',\n fix: `Run \\`rush-ai doctor --fix\\` to inject MCP config`,\n autoFix: async () => {\n injectMcpConfig(configPath);\n },\n };\n }\n\n const rushServer = servers.rush as Record<string, unknown>;\n const command = rushServer?.command as string | undefined;\n\n if (command !== 'rush-ai') {\n return {\n name: `plugin_${name}`,\n group: 'Plugins',\n status: 'warn',\n label: name,\n value: `Installed, MCP command is \"${command}\" (expected \"rush-ai\")`,\n fix: `Run \\`rush-ai plugin update ${name}\\``,\n };\n }\n\n return {\n name: `plugin_${name}`,\n group: 'Plugins',\n status: 'pass',\n label: name,\n value: `Installed (v${manifest.version}), MCP config valid`,\n };\n } catch {\n return {\n name: `plugin_${name}`,\n group: 'Plugins',\n status: 'warn',\n label: name,\n value: `Installed, failed to parse MCP config`,\n fix: `Check ${configPath} for JSON syntax errors`,\n };\n }\n}\n\nexport const checkPlugins: CheckRunner = async () => {\n const data = safeReadJson<InstalledPlugins>(PLUGINS_FILE, { plugins: {} });\n const plugins =\n data &&\n typeof data === 'object' &&\n data.plugins &&\n typeof data.plugins === 'object'\n ? data.plugins\n : {};\n const entries = Object.entries(plugins);\n\n if (entries.length === 0) {\n return [\n {\n name: 'plugins_none',\n group: 'Plugins',\n status: 'info',\n label: 'Plugins',\n value: 'No plugins installed',\n },\n ];\n }\n\n return entries.map(([name, manifest]) => checkMcpForPlugin(name, manifest));\n};\n","/**\n * 本地 marketplace cache 目录管理。\n *\n * 文件布局(默认 `~/.rush/marketplaces/`,测试可注入):\n * ```\n * <cacheBaseDir>/\n * ├── <mkt-name-1>/\n * │ ├── .rush-marketplace-source.json ← rush-ai 维护的元数据文件\n * │ ├── .claude-plugin/marketplace.json (clone/copy 下来的)\n * │ ├── .git/ (仅 github: / git: source)\n * │ └── ... (其他 plugin 目录)\n * └── <mkt-name-2>/ ...\n * ```\n *\n * `.rush-marketplace-source.json` 是 rush-ai 记录\"这个 cache 来自哪个 source\"的\n * 元数据文件,方便 `list()` / `update()` 不依赖其他配置还原 source。\n *\n * API(对齐 `specs/cli-commands.md` §1):\n * - `add(source, {name?, runner?})` → 把 source 落到 cache,返回 ResolvedMarketplace\n * - `remove(name)` → 删 cache 目录\n * - `update(name, {runner?})` → 重新 fetch(仅对可更新的 source)\n * - `list()` → 枚举当前 cache 里的 marketplace\n * - `get(name)` → 读单个 marketplace\n *\n * **测试铁律**:所有操作通过 `cacheBaseDir` 或 `home` 注入临时目录,禁止触碰真实 `~/.rush/`。\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { constants as fsConstants } from 'node:fs';\nimport {\n access,\n mkdir,\n readdir,\n readFile,\n rename,\n rm,\n writeFile,\n} from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, resolve } from 'node:path';\nimport {\n ensureDirectoryExists,\n resolveDirectoryMarketplace,\n} from './directory.js';\nimport {\n cloneGithubMarketplace,\n type GitRunner,\n updateGithubMarketplace,\n} from './github.js';\nimport { readMarketplaceJson } from './manifest.js';\nimport {\n defaultNameFromSource,\n MarketplaceError,\n NotImplementedError,\n parseSource,\n} from './source.js';\nimport type {\n CachedMarketplace,\n MarketplaceSource,\n ResolvedMarketplace,\n} from './types.js';\n\n/**\n * Cache root 相对 HOME 的路径(`~/.rush/marketplaces/`)。\n *\n * 暴露常量便于 CLI 测试断言。\n */\nexport const MARKETPLACE_CACHE_RELATIVE_PATH = '.rush/marketplaces' as const;\n\n/**\n * Cache 目录里 rush-ai 自己维护的 source 元数据文件名。\n */\nexport const CACHE_META_FILENAME = '.rush-marketplace-source.json' as const;\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\n/**\n * 同名 marketplace 已存在于 cache 且 source 与请求不一致。\n *\n * 注意字段命名:用 `marketplaceName`(不是 `name`),避免和 `Error.name`\n * 冲突——父类构造里 `this.name = 'MarketplaceAlreadyExistsError'` 会\n * 覆盖同名 public 参数属性,导致调用方拿不到真实 marketplace 名。\n */\nexport class MarketplaceAlreadyExistsError extends MarketplaceError {\n constructor(\n public readonly marketplaceName: string,\n public readonly existingSourceRaw: string,\n public readonly requestedSourceRaw: string\n ) {\n super(\n `Marketplace '${marketplaceName}' already exists with source '${existingSourceRaw}'; ` +\n `refusing to overwrite with '${requestedSourceRaw}'. ` +\n `Use 'rush-ai marketplace remove ${marketplaceName}' first, or pass --as <different-name>.`\n );\n this.name = 'MarketplaceAlreadyExistsError';\n }\n}\n\n/**\n * cache 里找不到指定 marketplace。同样用 `marketplaceName` 命名字段避免\n * 与 `Error.name` 冲突。\n */\nexport class MarketplaceNotFoundError extends MarketplaceError {\n constructor(public readonly marketplaceName: string) {\n super(`Marketplace '${marketplaceName}' not found in local cache.`);\n this.name = 'MarketplaceNotFoundError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface MarketplaceCacheOptions {\n /**\n * 注入 HOME 目录(测试用,默认 `os.homedir()`)。\n *\n * 和 `cacheBaseDir` 二选一——优先 `cacheBaseDir`。\n */\n readonly home?: string;\n /**\n * 直接注入 cache 根目录绝对路径(优先级高于 `home`)。\n */\n readonly cacheBaseDir?: string;\n /**\n * 注入 git runner(测试 mock;生产走 defaultGitRunner)。\n *\n * 作用于整个 store 的所有 github/git 操作;单次 add/update 也可以临时覆盖。\n */\n readonly gitRunner?: GitRunner;\n}\n\nexport interface AddOptions {\n /** 自定义 marketplace 名(覆盖 source 推导出来的默认名) */\n readonly as?: string;\n /** 本次调用专属 gitRunner(覆盖 store 级别注入) */\n readonly gitRunner?: GitRunner;\n /**\n * `--force`:若同名 cache 存在且 source 不同,仍覆盖。\n *\n * 默认 false → 抛 `MarketplaceAlreadyExistsError`,避免误删用户数据。\n */\n readonly force?: boolean;\n}\n\nexport interface UpdateOptions {\n readonly gitRunner?: GitRunner;\n}\n\n// ---------------------------------------------------------------------------\n// MarketplaceCache class\n// ---------------------------------------------------------------------------\n\n/**\n * Local marketplace cache 的唯一入口。\n *\n * 典型用法:\n * ```ts\n * const cache = new MarketplaceCache(); // 默认 ~/.rush/marketplaces\n * await cache.add(parseSource('github:a/b')); // add\n * const list = await cache.list(); // list\n * await cache.update('b'); // update\n * await cache.remove('b'); // remove\n * ```\n *\n * 所有操作都是 async;单次调用内部保证\"add 完整落地再返回 / remove 完整清理再返回\",\n * 不留半成品。\n *\n * 本 class 本身**不**持有锁——调用方(CLI)负责序列化,同一时刻不并发 add 同名。\n */\nexport class MarketplaceCache {\n public readonly cacheBaseDir: string;\n private readonly gitRunner?: GitRunner;\n\n constructor(opts: MarketplaceCacheOptions = {}) {\n if (opts.cacheBaseDir) {\n this.cacheBaseDir = resolve(opts.cacheBaseDir);\n } else {\n const home = opts.home ?? homedir();\n this.cacheBaseDir = resolve(home, MARKETPLACE_CACHE_RELATIVE_PATH);\n }\n if (opts.gitRunner) {\n this.gitRunner = opts.gitRunner;\n }\n }\n\n /** 给定 name 得到其 cache 目录绝对路径(可能不存在)。 */\n pathFor(name: string): string {\n if (!name || name.includes('/') || name.includes('..')) {\n throw new MarketplaceError(\n `Invalid marketplace name '${name}'. Names must be non-empty and cannot contain '/' or '..'.`\n );\n }\n return resolve(this.cacheBaseDir, name);\n }\n\n /**\n * 添加一个 marketplace 到本地 cache。\n *\n * 行为:\n * - `directory:` source:不 cache,但仍记录 meta(方便 list + remove 时去注册)\n * - `github:` source:shallow clone 到 `<cacheBaseDir>/<name>/`\n * - 其他 source:抛 NotImplementedError\n *\n * 幂等性:\n * - 同名 + 同 source 已存在 → 直接返回现有 ResolvedMarketplace(no-op)\n * - 同名 + 不同 source → `MarketplaceAlreadyExistsError`(除非 `force=true`)\n */\n async add(\n source: MarketplaceSource,\n opts: AddOptions = {}\n ): Promise<ResolvedMarketplace> {\n const name = opts.as ?? defaultNameFromSource(source);\n const targetDir = this.pathFor(name);\n\n // 检查是否已存在同名 cache。注意:**不吞** readMeta 抛出的错误——\n // corrupt/shape-invalid meta 应该让 add 失败(避免误覆盖用户数据)。\n // 只有\"meta 文件不存在\"才返回 null 走新建分支。\n const existingMeta = await this.readMeta(name);\n if (existingMeta) {\n if (existingMeta.source.raw === source.raw) {\n // 幂等:相同 source 直接返回现有\n return this.resolveExisting(name, existingMeta.source);\n }\n if (!opts.force) {\n throw new MarketplaceAlreadyExistsError(\n name,\n existingMeta.source.raw,\n source.raw\n );\n }\n // force:删掉旧 cache 再继续\n await this.remove(name);\n }\n\n // 按 source kind 分派\n const gitRunner = opts.gitRunner ?? this.gitRunner;\n\n switch (source.kind) {\n case 'directory': {\n // directory: 不 cache;但仍验证 + 写 meta(meta 文件放在 cacheBaseDir 里\n // 的 <name>/ 下,不污染用户的 directory: path)\n await ensureDirectoryExists(source);\n const resolved = await resolveDirectoryMarketplace(source, name);\n await this.writeMeta(name, source);\n return resolved;\n }\n case 'github': {\n await mkdir(dirname(targetDir), { recursive: true });\n const resolved = await cloneGithubMarketplace(\n source,\n targetDir,\n name,\n gitRunner ? { runner: gitRunner } : {}\n );\n await this.writeMeta(name, source);\n return resolved;\n }\n case 'git':\n case 'rush':\n case 'npm':\n throw new NotImplementedError(source.kind, source.raw);\n }\n }\n\n /**\n * 删除 cache 里指定 marketplace。\n *\n * - directory: source:只删 meta 目录(<cacheBaseDir>/<name>/),不触碰用户目录\n * - github: source:删整个 <cacheBaseDir>/<name>/\n * - 不存在 → no-op(不报错;CLI 层决定是否提示)\n */\n async remove(name: string): Promise<void> {\n const target = this.pathFor(name);\n if (!(await pathExists(target))) {\n return;\n }\n await rm(target, { recursive: true, force: true });\n }\n\n /**\n * 重新 fetch 远端 source(仅对 github: / git: / rush:// / npm:)。\n *\n * - directory: source → no-op(返回现有 ResolvedMarketplace)\n * - github: source → fetch + checkout\n * - 不存在 → MarketplaceNotFoundError\n */\n async update(\n name: string,\n opts: UpdateOptions = {}\n ): Promise<ResolvedMarketplace> {\n const meta = await this.readMeta(name);\n if (!meta) {\n throw new MarketplaceNotFoundError(name);\n }\n const runner = opts.gitRunner ?? this.gitRunner;\n\n switch (meta.source.kind) {\n case 'directory':\n // directory: 不 cache,update 是 no-op;重新 resolve 确保还在\n return resolveDirectoryMarketplace(meta.source, name);\n case 'github': {\n const cacheDir = this.pathFor(name);\n const resolved = await updateGithubMarketplace(\n meta.source,\n cacheDir,\n name,\n runner ? { runner } : {}\n );\n await this.writeMeta(name, meta.source); // 更新 cachedAt\n return resolved;\n }\n case 'git':\n case 'rush':\n case 'npm':\n throw new NotImplementedError(meta.source.kind, meta.source.raw);\n }\n }\n\n /**\n * 列出所有 cache 里已注册的 marketplace。\n *\n * 读 cacheBaseDir 下每个子目录的 meta 文件;**缺 meta 或 meta 损坏**的子目录\n * 跳过(宽容枚举语义)——避免单个坏 cache 阻塞 `marketplace list` 整体命令。\n * `add()` / `has()` / `get()` 路径仍然对损坏 meta 严格抛错,以保护写操作。\n * v1 暂不单列损坏项;若未来需要,可加 `options.includeCorrupt` 返回 warning。\n */\n async list(): Promise<CachedMarketplace[]> {\n if (!(await pathExists(this.cacheBaseDir))) {\n return [];\n }\n const entries = await readdir(this.cacheBaseDir, { withFileTypes: true });\n const result: CachedMarketplace[] = [];\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n // 宽容:吞 corrupt meta 错误(list 语义),其他路径(add/has/get)不吞\n const meta = await this.readMeta(entry.name).catch(() => null);\n if (!meta) continue;\n result.push({\n name: entry.name,\n path: resolve(this.cacheBaseDir, entry.name),\n source: meta.source,\n cachedAt: meta.cachedAt,\n });\n }\n // 字典序便于人眼扫\n result.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));\n return result;\n }\n\n /**\n * 查询单个 marketplace,解析 + 返回完整 ResolvedMarketplace。\n *\n * 不存在 → `MarketplaceNotFoundError`。\n */\n async get(name: string): Promise<ResolvedMarketplace> {\n const meta = await this.readMeta(name);\n if (!meta) {\n throw new MarketplaceNotFoundError(name);\n }\n return this.resolveExisting(name, meta.source);\n }\n\n /**\n * 判断 marketplace 是否存在于 cache(不 resolve,纯看 meta 文件是否存在)。\n *\n * 注意:**不吞** meta 损坏错误——若 meta 文件存在但损坏/shape 错误,抛\n * `MarketplaceError`,让调用方能区分\"不存在\"和\"cache 需要修复\"。\n * 仅当 meta 文件缺失时返回 false。\n */\n async has(name: string): Promise<boolean> {\n const meta = await this.readMeta(name);\n return meta !== null;\n }\n\n // -------------------------------------------------------------------------\n // Internals\n // -------------------------------------------------------------------------\n\n private async resolveExisting(\n name: string,\n source: MarketplaceSource\n ): Promise<ResolvedMarketplace> {\n switch (source.kind) {\n case 'directory':\n return resolveDirectoryMarketplace(source, name);\n case 'github': {\n const cacheDir = this.pathFor(name);\n const manifest = await readMarketplaceJson(cacheDir);\n return { name, rootDir: cacheDir, source, manifest };\n }\n case 'git':\n case 'rush':\n case 'npm':\n throw new NotImplementedError(source.kind, source.raw);\n }\n }\n\n private metaPathFor(name: string): string {\n return resolve(this.pathFor(name), CACHE_META_FILENAME);\n }\n\n private async readMeta(\n name: string\n ): Promise<{ source: MarketplaceSource; cachedAt: string } | null> {\n const metaPath = this.metaPathFor(name);\n if (!(await pathExists(metaPath))) {\n return null;\n }\n const raw = await readFile(metaPath, 'utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new MarketplaceError(\n `Marketplace cache meta at '${metaPath}' is corrupt: ${\n (err as Error).message\n }`\n );\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new MarketplaceError(\n `Marketplace cache meta at '${metaPath}' has wrong shape (expected object)`\n );\n }\n const obj = parsed as Record<string, unknown>;\n if (typeof obj.sourceRaw !== 'string') {\n throw new MarketplaceError(\n `Marketplace cache meta at '${metaPath}' missing 'sourceRaw' string`\n );\n }\n if (typeof obj.cachedAt !== 'string') {\n throw new MarketplaceError(\n `Marketplace cache meta at '${metaPath}' missing 'cachedAt' string`\n );\n }\n const source = parseSource(obj.sourceRaw);\n return { source, cachedAt: obj.cachedAt };\n }\n\n private async writeMeta(\n name: string,\n source: MarketplaceSource\n ): Promise<void> {\n const metaPath = this.metaPathFor(name);\n await mkdir(dirname(metaPath), { recursive: true });\n const payload = {\n sourceRaw: source.raw,\n cachedAt: new Date().toISOString(),\n // 冗余字段便于人工检查,不被 readMeta 消费\n kind: source.kind,\n };\n await atomicWriteJson(metaPath, payload);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p, fsConstants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function atomicWriteJson(\n filePath: string,\n payload: unknown\n): Promise<void> {\n const content = `${JSON.stringify(payload, null, 2)}\\n`;\n const tmp = `${filePath}.${randomUUID()}.tmp`;\n try {\n await writeFile(tmp, content, { encoding: 'utf8' });\n await rename(tmp, filePath);\n } catch (err) {\n await rm(tmp, { force: true }).catch(() => {});\n throw err;\n }\n}\n","/**\n * `directory:` source 的 IO 实现。\n *\n * Phase 1 特性:**不做 cache、不 copy、不 symlink**——直接引用用户给的绝对路径,\n * 验证目录存在 + 读 `.claude-plugin/marketplace.json` 即可。\n *\n * 适用场景:\n * - 本地开发 marketplace(边改边测)\n * - CI / test fixture\n * - 企业内网挂载的 NFS 路径\n */\n\nimport { stat as fsStat } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { parseMarketplaceJson, readMarketplaceJson } from './manifest.js';\nimport {\n InvalidSourceError,\n MarketplaceError,\n NotImplementedError,\n} from './source.js';\nimport type {\n DirectoryMarketplaceSource,\n MarketplaceManifest,\n ResolvedMarketplace,\n} from './types.js';\n\n/**\n * Marketplace 源目录不存在或不可读。\n */\nexport class MarketplaceDirectoryNotFoundError extends MarketplaceError {\n constructor(public readonly path: string) {\n super(\n `Marketplace directory not found or unreadable: '${path}'. ` +\n `Verify the path exists and the current user has read permission.`\n );\n this.name = 'MarketplaceDirectoryNotFoundError';\n }\n}\n\n/**\n * 给定 `directory:` source,解析为 ResolvedMarketplace。\n *\n * 步骤:\n * 1. `stat` 目录 → 不存在 / 非目录 / 权限不够 → MarketplaceDirectoryNotFoundError\n * 2. 读 `<path>/.claude-plugin/marketplace.json`(由 `manifest.ts` 负责)\n * - 文件不存在 → MarketplaceCorruptError(reason='missing')\n * - 文件存在但 JSON 损坏 / 缺 `plugins` 字段 → MarketplaceCorruptError(reason='corrupt')\n * 3. 返回 `{ name, rootDir: path, source, manifest }`\n *\n * 错误类型一览(方便 CLI 错误映射):\n * - `MarketplaceDirectoryNotFoundError`:目录层问题(路径不存在/不是目录)\n * - `MarketplaceCorruptError`:marketplace.json 层问题(缺失或损坏),见 `manifest.ts`\n *\n * 注意:本函数不 mutate cache,不写 registry——只是 resolve。CLI 层再决定是否\n * 要把 source 登记到 cache(`cache.ts` 的 add / remove)。\n */\nexport async function resolveDirectoryMarketplace(\n source: DirectoryMarketplaceSource,\n name: string\n): Promise<ResolvedMarketplace> {\n if (!name || typeof name !== 'string') {\n throw new InvalidSourceError(\n source.raw,\n 'marketplace name must be non-empty string'\n );\n }\n\n const rootDir = resolve(source.path);\n let stats: Awaited<ReturnType<typeof fsStat>>;\n try {\n stats = await fsStat(rootDir);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT' || code === 'ENOTDIR' || code === 'EACCES') {\n throw new MarketplaceDirectoryNotFoundError(rootDir);\n }\n throw err;\n }\n\n if (!stats.isDirectory()) {\n throw new MarketplaceDirectoryNotFoundError(rootDir);\n }\n\n const manifest = await readMarketplaceJson(rootDir);\n\n return {\n name,\n rootDir,\n source,\n manifest,\n };\n}\n\n/**\n * 专给 parser 侧 \"validate-only\" 使用——只检查目录存在,不解析 JSON。\n *\n * 测试里偶尔需要只验证路径而不触发 JSON 解析;生产 CLI 路径请用\n * `resolveDirectoryMarketplace`。\n */\nexport async function ensureDirectoryExists(\n source: DirectoryMarketplaceSource\n): Promise<void> {\n try {\n const stats = await fsStat(source.path);\n if (!stats.isDirectory()) {\n throw new MarketplaceDirectoryNotFoundError(source.path);\n }\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT' || code === 'ENOTDIR' || code === 'EACCES') {\n throw new MarketplaceDirectoryNotFoundError(source.path);\n }\n throw err;\n }\n}\n\n// Re-export 便于 barrel 引用(避免 import 路径两跳)\nexport { parseMarketplaceJson };\nexport type { MarketplaceManifest };\n\n// 确保 NotImplementedError 可从本文件也导出(CLI 拦截 Phase 2 source 时需要)\nexport { NotImplementedError };\n","/**\n * `.claude-plugin/marketplace.json` 读取与校验。\n *\n * 读取策略(spec §3 \"最小必要字段\"原则):\n * - 必需:顶层是 object + 有 `plugins` 数组\n * - 其他字段原样透传,不做类型校验(上游作者可能有私有字段)\n * - JSON 损坏 / 缺 `plugins` → 抛 `MarketplaceCorruptError`(**不 swallow**,对齐 task 验收)\n *\n * 本文件被 `directory.ts` / `github.ts` / `cache.ts` 共享。\n */\n\nimport { constants as fsConstants } from 'node:fs';\nimport { access, readFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { MarketplaceError } from './source.js';\nimport type { MarketplaceManifest } from './types.js';\n\nexport const MARKETPLACE_MANIFEST_RELATIVE_PATH =\n '.claude-plugin/marketplace.json' as const;\n\n/**\n * marketplace.json 损坏或不存在的错误。\n *\n * 细分两种:\n * - `reason='missing'`:文件不存在\n * - `reason='corrupt'`:JSON 解析失败 / 顶层不是 object / 缺 plugins 数组\n */\nexport class MarketplaceCorruptError extends MarketplaceError {\n constructor(\n public readonly filePath: string,\n public readonly reason: 'missing' | 'corrupt',\n public readonly detail: string\n ) {\n super(\n reason === 'missing'\n ? `marketplace.json not found at '${filePath}'. Is this a valid marketplace directory?`\n : `marketplace.json at '${filePath}' is corrupt: ${detail}. Please fix the file or contact the marketplace author.`\n );\n this.name = 'MarketplaceCorruptError';\n }\n}\n\n/**\n * 从 marketplace 根目录读取 `.claude-plugin/marketplace.json`。\n *\n * @param rootDir marketplace 根目录绝对路径\n * @returns 解析 + 校验后的 MarketplaceManifest\n * @throws {MarketplaceCorruptError}\n */\nexport async function readMarketplaceJson(\n rootDir: string\n): Promise<MarketplaceManifest> {\n const filePath = resolve(rootDir, MARKETPLACE_MANIFEST_RELATIVE_PATH);\n try {\n await access(filePath, fsConstants.R_OK);\n } catch {\n throw new MarketplaceCorruptError(\n filePath,\n 'missing',\n 'file does not exist or is not readable'\n );\n }\n\n const raw = await readFile(filePath, 'utf8');\n return parseMarketplaceJson(raw, filePath);\n}\n\n/**\n * 从字符串解析 + 校验 marketplace.json。\n *\n * 抽出此函数便于测试里直接传字符串,不依赖磁盘。\n */\nexport function parseMarketplaceJson(\n raw: string,\n sourcePathForError: string\n): MarketplaceManifest {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new MarketplaceCorruptError(\n sourcePathForError,\n 'corrupt',\n (err as Error).message\n );\n }\n\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new MarketplaceCorruptError(\n sourcePathForError,\n 'corrupt',\n 'root must be a JSON object'\n );\n }\n\n const obj = parsed as Record<string, unknown>;\n if (!Array.isArray(obj.plugins)) {\n throw new MarketplaceCorruptError(\n sourcePathForError,\n 'corrupt',\n \"missing 'plugins' array at root\"\n );\n }\n\n // 每个 plugin 条目至少要有 name;shape 不严格(保留作者原字段)\n for (const [idx, entry] of obj.plugins.entries()) {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {\n throw new MarketplaceCorruptError(\n sourcePathForError,\n 'corrupt',\n `plugins[${idx}] must be an object`\n );\n }\n const e = entry as Record<string, unknown>;\n if (typeof e.name !== 'string' || e.name.length === 0) {\n throw new MarketplaceCorruptError(\n sourcePathForError,\n 'corrupt',\n `plugins[${idx}].name must be a non-empty string`\n );\n }\n }\n\n return obj as unknown as MarketplaceManifest;\n}\n","/**\n * Source 字符串解析器 + 专用错误类型。\n *\n * Source of truth: `specs/cli-commands.md` §1.1 `marketplace add <source>`。\n *\n * 支持 5 种 prefix:\n * - `directory:/abs/path` ✅ Phase 1\n * - `github:owner/repo[#ref]` ✅ Phase 1\n * - `git:<url>` ⏳ Phase 1.5 → NotImplementedError\n * - `rush://host` ⏳ Phase 2 → NotImplementedError\n * - `npm:@scope/pkg` / `npm:pkg` ⏳ Phase 2 → NotImplementedError\n *\n * 解析器不做任何 IO;只做字符串层面的 shape 验证。\n * IO 发生在 `directory.ts` / `github.ts` 层面。\n */\n\nimport { isAbsolute } from 'node:path';\nimport type {\n DirectoryMarketplaceSource,\n GithubMarketplaceSource,\n GitMarketplaceSource,\n MarketplaceSource,\n MarketplaceSourceKind,\n NpmMarketplaceSource,\n RushMarketplaceSource,\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Error 类型\n// ---------------------------------------------------------------------------\n\n/** 基类 —— 便于 CLI 层统一 `instanceof MarketplaceError` catch。 */\nexport class MarketplaceError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'MarketplaceError';\n }\n}\n\n/** Source 字符串格式非法(prefix 缺失、shape 错误、空字段等)。 */\nexport class InvalidSourceError extends MarketplaceError {\n constructor(\n public readonly raw: string,\n reason: string\n ) {\n super(`Invalid marketplace source '${raw}': ${reason}`);\n this.name = 'InvalidSourceError';\n }\n}\n\n/**\n * 识别为合法 prefix 但 Phase 1 暂未实现。\n *\n * 错误消息明确引导用户\"Phase 2 会支持\",避免误以为是代码 bug。\n */\nexport class NotImplementedError extends MarketplaceError {\n constructor(\n public readonly kind: MarketplaceSourceKind,\n public readonly raw: string\n ) {\n super(\n `Source kind '${kind}:' is not implemented in Phase 1 (source='${raw}'). ` +\n `Phase 2 will support git: / rush:// / npm: — for now, please use ` +\n `'directory:<abs-path>' or 'github:<owner>/<repo>[#<ref>]'.`\n );\n this.name = 'NotImplementedError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// 解析器\n// ---------------------------------------------------------------------------\n\n/**\n * 从 raw 字符串解析出 MarketplaceSource。\n *\n * **只做字符串 shape 验证**(是否有前缀、字段是否非空、github owner/repo 形状),\n * **不**做任何 IO(不 stat 目录、不 clone)。IO 由 `directory.ts` / `github.ts`\n * 里的 fetch 函数负责。\n *\n * @throws {InvalidSourceError} 格式不合法\n * @throws {NotImplementedError} 合法但 Phase 1 未实现(git: / rush:// / npm:)\n */\nexport function parseSource(raw: string): MarketplaceSource {\n if (typeof raw !== 'string') {\n throw new InvalidSourceError(String(raw), 'source must be a string');\n }\n const trimmed = raw.trim();\n if (trimmed.length === 0) {\n throw new InvalidSourceError(raw, 'source must be non-empty');\n }\n\n if (trimmed.startsWith('directory:')) {\n return parseDirectory(trimmed);\n }\n if (trimmed.startsWith('github:')) {\n return parseGithub(trimmed);\n }\n if (trimmed.startsWith('rush://')) {\n return parseRush(trimmed);\n }\n if (trimmed.startsWith('git:')) {\n return parseGit(trimmed);\n }\n if (trimmed.startsWith('npm:')) {\n return parseNpm(trimmed);\n }\n\n throw new InvalidSourceError(\n raw,\n \"missing recognized prefix. Expected one of 'directory:' / 'github:' / 'git:' / 'rush://' / 'npm:'\"\n );\n}\n\n/**\n * 从 source 推导默认 marketplace 名(`--as` 省略时使用)。\n *\n * 规则对齐 `specs/cli-commands.md` §1.1:\n * - `directory:/abs/path` → 路径最后一段\n * - `github:owner/repo` → repo 名\n * - `git:<url>` → URL 里解析出 repo 名\n * - `rush://host` → `rush-marketplace`\n * - `npm:@scope/pkg` → pkg 名(去 @scope/)\n *\n * Phase 1 只对 `directory` / `github` 起实际作用;其他 kind 的实现虽然保留,\n * 也会在下游 `add()` 时被 `NotImplementedError` 拦住。这里仍然填完整映射,\n * 便于 CLI 在错误输出里展示\"如果实现了 name 会是啥\"。\n */\nexport function defaultNameFromSource(source: MarketplaceSource): string {\n switch (source.kind) {\n case 'directory': {\n const segments = source.path.split('/').filter((s) => s.length > 0);\n const last = segments[segments.length - 1];\n if (!last) {\n throw new InvalidSourceError(\n source.raw,\n 'cannot derive marketplace name from empty path'\n );\n }\n return last;\n }\n case 'github':\n return source.repo;\n case 'git': {\n // 从 URL 里提出最后一段的 repo 名\n const withoutQuery = source.url.split(/[?#]/)[0] ?? source.url;\n const tail =\n withoutQuery\n .replace(/\\.git$/, '')\n .split('/')\n .pop() ?? '';\n if (!tail) {\n throw new InvalidSourceError(\n source.raw,\n 'cannot derive marketplace name from git URL'\n );\n }\n return tail;\n }\n case 'rush':\n return 'rush-marketplace';\n case 'npm': {\n // @scope/pkg → pkg;pkg → pkg\n const parts = source.pkg.split('/');\n const last = parts[parts.length - 1];\n if (!last) {\n throw new InvalidSourceError(\n source.raw,\n 'cannot derive marketplace name from npm package'\n );\n }\n return last;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Per-kind 解析\n// ---------------------------------------------------------------------------\n\nfunction parseDirectory(raw: string): DirectoryMarketplaceSource {\n const path = raw.slice('directory:'.length);\n if (path.length === 0) {\n throw new InvalidSourceError(raw, 'directory: path is empty');\n }\n if (!isAbsolute(path)) {\n throw new InvalidSourceError(\n raw,\n `directory: path must be absolute (got '${path}')`\n );\n }\n return { kind: 'directory', raw, path };\n}\n\nfunction parseGithub(raw: string): GithubMarketplaceSource {\n const body = raw.slice('github:'.length);\n if (body.length === 0) {\n throw new InvalidSourceError(raw, 'github: body is empty');\n }\n\n // body shape: owner/repo[#ref]\n let pathPart = body;\n let ref: string | undefined;\n const hashIdx = body.indexOf('#');\n if (hashIdx !== -1) {\n pathPart = body.slice(0, hashIdx);\n ref = body.slice(hashIdx + 1);\n if (ref.length === 0) {\n throw new InvalidSourceError(raw, \"github: '#' present but ref is empty\");\n }\n if (!isValidGitRef(ref)) {\n throw new InvalidSourceError(raw, `github: ref '${ref}' is not valid`);\n }\n }\n\n const slashIdx = pathPart.indexOf('/');\n if (slashIdx === -1) {\n throw new InvalidSourceError(\n raw,\n \"github: body must be 'owner/repo' (missing '/')\"\n );\n }\n const owner = pathPart.slice(0, slashIdx);\n const repo = pathPart.slice(slashIdx + 1);\n\n if (!isValidGithubSegment(owner)) {\n throw new InvalidSourceError(raw, `github: owner '${owner}' is not valid`);\n }\n if (!isValidGithubSegment(repo)) {\n throw new InvalidSourceError(raw, `github: repo '${repo}' is not valid`);\n }\n // 禁止嵌套路径:owner/repo 之后不能再有 '/'\n if (repo.includes('/')) {\n throw new InvalidSourceError(\n raw,\n \"github: body must be 'owner/repo' (extra '/' found)\"\n );\n }\n\n return { kind: 'github', raw, owner, repo, ...(ref ? { ref } : {}) };\n}\n\nfunction parseGit(raw: string): GitMarketplaceSource {\n const url = raw.slice('git:'.length);\n if (url.length === 0) {\n throw new InvalidSourceError(raw, 'git: url is empty');\n }\n return { kind: 'git', raw, url };\n}\n\nfunction parseRush(raw: string): RushMarketplaceSource {\n const host = raw.slice('rush://'.length);\n if (host.length === 0) {\n throw new InvalidSourceError(raw, 'rush:// host is empty');\n }\n return { kind: 'rush', raw, host };\n}\n\nfunction parseNpm(raw: string): NpmMarketplaceSource {\n const pkg = raw.slice('npm:'.length);\n if (pkg.length === 0) {\n throw new InvalidSourceError(raw, 'npm: package is empty');\n }\n return { kind: 'npm', raw, pkg };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * GitHub owner / repo 段的合法字符集(RFC 与 GitHub 实际约定取最严的)。\n *\n * - 不允许空串\n * - 只含字母/数字/`-` / `.` / `_`\n * - 不以 `-` 开头或结尾(避免和 git 命令行 `--` flag 混淆)\n */\nfunction isValidGithubSegment(segment: string): boolean {\n if (segment.length === 0) return false;\n if (segment.startsWith('-') || segment.endsWith('-')) return false;\n return /^[A-Za-z0-9._-]+$/.test(segment);\n}\n\n/**\n * git ref(分支 / tag / sha)最小合法性校验。\n *\n * 对齐 `git check-ref-format --branch` 的**常见禁用字符**(我们不做完整 RFC 校验,\n * 以免拒绝合法名字),只拦明显危险:空格、`..`、控制字符、以 `-` 开头。\n *\n * - 不允许空串\n * - 不允许 `..`、`~`、`^`、`:`、`?`、`*`、`[`、`\\`、空格\n * - 不允许以 `-` 开头(和 git 命令行 flag 冲突)\n * - 不允许以 `.lock` 结尾(git 内部 refs 规则)\n */\nfunction isValidGitRef(ref: string): boolean {\n if (ref.length === 0) return false;\n if (ref.startsWith('-')) return false;\n if (ref.includes('..')) return false;\n if (ref.endsWith('.lock')) return false;\n // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional control-char rejection\n if (/[\\s~^:?*[\\]\\\\\\x00-\\x1f]/.test(ref)) return false;\n return true;\n}\n","/**\n * `github:` source 的 IO 实现。\n *\n * 关键策略(spec §1 + TASK 验收):\n * - 使用 `git clone --depth 1 [--branch <ref>] https://github.com/<owner>/<repo>.git <cache-dir>`\n * - **不**依赖 GitHub CLI (`gh`),避免引入额外工具\n * - 测试通过注入 `gitRunner` 函数 mock clone,**不真实打网络 / 写用户 HOME**\n *\n * Update 策略(已 clone 过之后的重新抓取):\n * - 如果 cache 目录已存在且是 git 仓库 → `git -C <cache-dir> fetch --depth 1 origin <ref>` → `git -C <cache-dir> checkout FETCH_HEAD`\n * - 如果 cache 目录存在但不是 git 仓库 → rm -rf 后重新 clone(保证状态干净)\n */\n\nimport type { SpawnOptions } from 'node:child_process';\nimport { spawn } from 'node:child_process';\nimport { constants as fsConstants } from 'node:fs';\nimport { access, rm } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { readMarketplaceJson } from './manifest.js';\nimport { MarketplaceError } from './source.js';\nimport type {\n GithubMarketplaceSource,\n MarketplaceManifest,\n ResolvedMarketplace,\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class GitCloneFailedError extends MarketplaceError {\n constructor(\n public readonly source: GithubMarketplaceSource,\n public readonly exitCode: number | null,\n public readonly stderr: string\n ) {\n super(\n `git clone failed for 'github:${source.owner}/${source.repo}${\n source.ref ? `#${source.ref}` : ''\n }' (exit=${exitCode ?? 'null'}).\\n${stderr.slice(0, 2000)}`\n );\n this.name = 'GitCloneFailedError';\n }\n}\n\nexport class GitBinaryMissingError extends MarketplaceError {\n constructor() {\n super(\n \"git executable not found on PATH. Please install git to use 'github:' sources.\"\n );\n this.name = 'GitBinaryMissingError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Git runner (注入点)\n// ---------------------------------------------------------------------------\n\n/**\n * git 调用结果(由 GitRunner 返回)。\n */\nexport interface GitRunnerResult {\n /** 进程退出码(null = killed by signal) */\n readonly exitCode: number | null;\n /** stdout 完整内容(通常用不到,保留便于 debug) */\n readonly stdout: string;\n /** stderr 完整内容(失败时用来构造错误消息) */\n readonly stderr: string;\n}\n\n/**\n * 抽象掉 `git` 子进程调用——测试里传 mock,生产走默认 `spawn` 实现。\n *\n * 约定:`cwd` 如有给就 spawn 里用;`args` 是完整 argv(不含 `git` 本身)。\n *\n * 实现必须处理 `ENOENT`(git not found)并抛 `GitBinaryMissingError`。\n */\nexport type GitRunner = (\n args: readonly string[],\n opts?: { cwd?: string }\n) => Promise<GitRunnerResult>;\n\n/**\n * 默认 git runner——spawn 真实 `git` 可执行文件。\n *\n * 测试禁止使用(会打网络 + 写 HOME),生产 CLI 用此实现。\n */\nexport const defaultGitRunner: GitRunner = async (args, opts) => {\n return await new Promise<GitRunnerResult>((resolvePromise, rejectPromise) => {\n const spawnOpts: SpawnOptions = { stdio: ['ignore', 'pipe', 'pipe'] };\n if (opts?.cwd) spawnOpts.cwd = opts.cwd;\n const proc = spawn('git', args, spawnOpts);\n let stdout = '';\n let stderr = '';\n proc.stdout?.on('data', (chunk) => {\n stdout += String(chunk);\n });\n proc.stderr?.on('data', (chunk) => {\n stderr += String(chunk);\n });\n proc.on('error', (err) => {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n rejectPromise(new GitBinaryMissingError());\n return;\n }\n rejectPromise(err);\n });\n proc.on('close', (code) => {\n resolvePromise({ exitCode: code, stdout, stderr });\n });\n });\n};\n\n// ---------------------------------------------------------------------------\n// Clone / update 逻辑\n// ---------------------------------------------------------------------------\n\nexport interface CloneOptions {\n /** 注入的 git runner(测试用 mock) */\n readonly runner?: GitRunner;\n}\n\n/**\n * 把 `github:` source 落地到指定 cache 目录,返回 ResolvedMarketplace。\n *\n * 行为:\n * - cache 目录不存在 → shallow clone 到该目录\n * - cache 目录存在但不是 git 仓库 → rm -rf 后重新 clone\n * - cache 目录存在且是 git 仓库 → fetch + checkout(update 路径)\n *\n * @throws {GitBinaryMissingError}\n * @throws {GitCloneFailedError}\n * @throws {MarketplaceCorruptError} 如果 clone 后目录里缺 `.claude-plugin/marketplace.json`\n */\nexport async function cloneGithubMarketplace(\n source: GithubMarketplaceSource,\n cacheDir: string,\n name: string,\n opts?: CloneOptions\n): Promise<ResolvedMarketplace> {\n const runner = opts?.runner ?? defaultGitRunner;\n\n const url = `https://github.com/${source.owner}/${source.repo}.git`;\n\n const existing = await detectCacheState(cacheDir);\n\n if (existing === 'missing') {\n await runClone(runner, url, cacheDir, source);\n } else if (existing === 'not-git') {\n await rm(cacheDir, { recursive: true, force: true });\n await runClone(runner, url, cacheDir, source);\n } else {\n // existing === 'git' —— update 路径\n await runUpdate(runner, cacheDir, source);\n }\n\n const manifest = await readMarketplaceJson(cacheDir);\n return {\n name,\n rootDir: resolve(cacheDir),\n source,\n manifest,\n };\n}\n\n/**\n * 仅做 update(要求 cache 目录已是 git 仓库)。\n *\n * 供 cache.ts `update()` 调用——上层已确认目录存在。\n */\nexport async function updateGithubMarketplace(\n source: GithubMarketplaceSource,\n cacheDir: string,\n name: string,\n opts?: CloneOptions\n): Promise<ResolvedMarketplace> {\n const runner = opts?.runner ?? defaultGitRunner;\n const state = await detectCacheState(cacheDir);\n if (state !== 'git') {\n // fallthrough 到 clone 逻辑,保证最终状态正确\n return cloneGithubMarketplace(source, cacheDir, name, opts);\n }\n await runUpdate(runner, cacheDir, source);\n const manifest = await readMarketplaceJson(cacheDir);\n return {\n name,\n rootDir: resolve(cacheDir),\n source,\n manifest,\n };\n}\n\n// ---------------------------------------------------------------------------\n// internals\n// ---------------------------------------------------------------------------\n\nasync function runClone(\n runner: GitRunner,\n url: string,\n cacheDir: string,\n source: GithubMarketplaceSource\n): Promise<void> {\n const args: string[] = ['clone', '--depth', '1'];\n if (source.ref) {\n args.push('--branch', source.ref);\n }\n args.push('--', url, cacheDir);\n\n const result = await runner(args);\n if (result.exitCode !== 0) {\n throw new GitCloneFailedError(source, result.exitCode, result.stderr);\n }\n}\n\nasync function runUpdate(\n runner: GitRunner,\n cacheDir: string,\n source: GithubMarketplaceSource\n): Promise<void> {\n // shallow fetch 最新 HEAD/ref\n const refArg = source.ref ?? 'HEAD';\n const fetchResult = await runner(\n ['fetch', '--depth', '1', 'origin', refArg],\n { cwd: cacheDir }\n );\n if (fetchResult.exitCode !== 0) {\n throw new GitCloneFailedError(\n source,\n fetchResult.exitCode,\n fetchResult.stderr\n );\n }\n\n // checkout 到 FETCH_HEAD 让 working tree 指向刚 fetch 的 commit\n const checkoutResult = await runner(\n ['checkout', '--quiet', '--detach', 'FETCH_HEAD'],\n { cwd: cacheDir }\n );\n if (checkoutResult.exitCode !== 0) {\n throw new GitCloneFailedError(\n source,\n checkoutResult.exitCode,\n checkoutResult.stderr\n );\n }\n}\n\nasync function detectCacheState(\n cacheDir: string\n): Promise<'missing' | 'not-git' | 'git'> {\n try {\n await access(cacheDir, fsConstants.F_OK);\n } catch {\n return 'missing';\n }\n // 判断是否 git 仓库——检查 .git(目录或文件)\n try {\n await access(resolve(cacheDir, '.git'), fsConstants.F_OK);\n return 'git';\n } catch {\n return 'not-git';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Manifest helpers re-export(方便 barrel)\n// ---------------------------------------------------------------------------\n\nexport { readMarketplaceJson };\nexport type { MarketplaceManifest };\n","/**\n * `rush-ai marketplace add <source> [--as <name>]`。\n *\n * Source of truth: `specs/cli-commands.md` §1.1\n *\n * 行为(v1 简化,详见 `./index.ts` 决策说明):\n * 1. 解析 source 字符串(`parseSource`)\n * 2. 调用 `MarketplaceCache.add(source, { as })`——落到 `~/.rush/marketplaces/<name>/`\n * - directory: 源:验证目录 + 写 meta,不 cache 拷贝\n * - github: 源:shallow clone 到 cache\n * - 其他: 抛 NotImplementedError(v1 仅 Phase 1 两家)\n * 3. **不**写 `known_marketplaces.json` / `config.toml`——延到 `plugin install`\n *\n * 退出码(对齐 spec §1.1):\n * - 0 成功(含幂等)\n * - 1 通用错误:source 无效、已存在同名不同 source、clone 失败、未实现 source\n * - 2 marketplace.json 解析失败(`MarketplaceCorruptError`)\n */\n\nimport type { Command } from 'commander';\nimport {\n defaultNameFromSource,\n InvalidSourceError,\n MarketplaceAlreadyExistsError,\n MarketplaceCache,\n MarketplaceCorruptError,\n NotImplementedError,\n parseSource,\n type ResolvedMarketplace,\n} from '../../marketplaces/index.js';\nimport { output } from '../../output/logger.js';\nimport { RushError } from '../../util/errors.js';\n\n/**\n * pure runner 的输入。测试从这里注入 home / cache 实例。\n */\nexport interface AddCommandInput {\n /** 原始 source 字符串(`directory:/abs/path` / `github:owner/repo[#ref]` ...) */\n source: string;\n /** `--as <name>`(可选) */\n as?: string;\n /** 注入自定义 home(测试用)。生产默认 `os.homedir()` */\n home?: string;\n /** 直接注入 MarketplaceCache 实例(测试 mock GitRunner 时用) */\n cache?: MarketplaceCache;\n}\n\nexport interface AddCommandResult {\n /** 最终落地的 marketplace 视图 */\n resolved: ResolvedMarketplace;\n /** 是否幂等 no-op(同 source 同名已存在) */\n idempotent: boolean;\n}\n\n/**\n * 纯函数形式的业务实现——无 console I/O,供单测 / CLI 复用。\n *\n * 失败:\n * - `RushError(code='INVALID_SOURCE', exitCode=1)` — source 格式错\n * - `RushError(code='SOURCE_NOT_IMPLEMENTED', exitCode=1)` — Phase 1 未实现的 source kind\n * - `RushError(code='MARKETPLACE_EXISTS', exitCode=1)` — 已存在同名不同 source\n * - `RushError(code='MARKETPLACE_CORRUPT', exitCode=2)` — marketplace.json 解析失败\n * - 其他错误(clone 失败、IO 错)以 RushError(exitCode=1) 透传\n */\nexport async function runAdd(\n input: AddCommandInput\n): Promise<AddCommandResult> {\n let source: ReturnType<typeof parseSource>;\n try {\n source = parseSource(input.source);\n } catch (err) {\n if (err instanceof InvalidSourceError) {\n throw new RushError(\n err.message,\n { raw: input.source },\n 'INVALID_SOURCE',\n 1\n );\n }\n throw err;\n }\n\n const cache =\n input.cache ??\n new MarketplaceCache(input.home !== undefined ? { home: input.home } : {});\n\n // 幂等 detect:add() 内部已实现\"同 source 同名 → 直接返回 existing\"\n const prior = await detectIdempotent(cache, source, input.as);\n\n try {\n const resolved = await cache.add(\n source,\n input.as !== undefined ? { as: input.as } : {}\n );\n return { resolved, idempotent: prior };\n } catch (err) {\n if (err instanceof NotImplementedError) {\n throw new RushError(\n err.message,\n { kind: err.kind },\n 'SOURCE_NOT_IMPLEMENTED',\n 1\n );\n }\n if (err instanceof MarketplaceAlreadyExistsError) {\n throw new RushError(\n err.message,\n {\n marketplaceName: err.marketplaceName,\n existingSource: err.existingSourceRaw,\n requestedSource: err.requestedSourceRaw,\n },\n 'MARKETPLACE_EXISTS',\n 1\n );\n }\n if (err instanceof MarketplaceCorruptError) {\n // spec §1.1 退出码 2\n throw new RushError(\n err.message,\n { filePath: err.filePath, reason: err.reason },\n 'MARKETPLACE_CORRUPT',\n 2\n );\n }\n if (err instanceof Error) {\n throw new RushError(err.message, {}, 'MARKETPLACE_ADD_FAILED', 1);\n }\n throw err;\n }\n}\n\n/** 判断本次 add 是否会触发幂等(仅当同名且同 source 已存在) */\nasync function detectIdempotent(\n cache: MarketplaceCache,\n source: ReturnType<typeof parseSource>,\n as: string | undefined\n): Promise<boolean> {\n const name = as ?? safeDefaultName(source);\n if (name === null) return false;\n try {\n if (!(await cache.has(name))) return false;\n const existing = await cache.get(name);\n return existing.source.raw === source.raw;\n } catch {\n // has/get 抛错(比如 meta 损坏)当作\"非幂等\"让 add 主流程处理真正的错\n return false;\n }\n}\n\n/** 不抛错的 defaultNameFromSource;失败返回 null。 */\nfunction safeDefaultName(\n source: ReturnType<typeof parseSource>\n): string | null {\n try {\n return defaultNameFromSource(source);\n } catch {\n return null;\n }\n}\n\n// -- commander wrapper --------------------------------------------------------\n\nexport function registerAddCommand(group: Command, _root: Command): void {\n group\n .command('add')\n .description(\n \"Register a marketplace source (e.g. 'github:owner/repo', 'directory:/abs/path')\"\n )\n .argument('<source>', 'marketplace source string')\n .option('--as <name>', 'override auto-derived marketplace name')\n .action(async (source: string, opts: { as?: string }) => {\n const result = await runAdd({\n source,\n ...(opts.as !== undefined ? { as: opts.as } : {}),\n });\n\n // 输出(stderr 为主;生产 stdout 保留给 JSON 等可管道数据,但本命令\n // 目前不走结构化 stdout,用 status 行提示即可)\n if (result.idempotent) {\n output.dim(\n `Marketplace '${result.resolved.name}' already registered with the same source; no change.`\n );\n } else {\n output.success(\n `Added marketplace '${result.resolved.name}' (from ${result.resolved.source.raw})`\n );\n output.dim(` → Cached at ${result.resolved.rootDir}`);\n output.dim(\n ' ⓘ IDE marketplace registration deferred to `rush-ai plugin install`.'\n );\n }\n });\n}\n","/**\n * rush-ai Registry 读-改-写实现(~/.rush/plugins/registry.json)。\n *\n * Source of truth: `specs/installer-interface.md` §3。\n *\n * 关键契约(spec §3.3):\n * - 所有写入走 write-temp-rename 原子替换\n * - 读前 `fs.stat` 拿 mtime;写前比较 mtime,冲突 → retry 一次,再变报错\n * - 文件不存在视为空 registry\n * - JSON 解析失败不尝试修复,直接抛错\n * - schemaVersion 不匹配只读不写,抛错让 CLI 层提示升级\n *\n * 本模块职责边界(spec §4 / plan §10):\n * - **只有 CLI 层**调用本模块写 registry,Installer 不写\n * - 一个 CLI 进程内可以多次 save(幂等),但并发进程间靠 mtime 检测\n *\n * 命名说明:spec §3.4 的示意 class 名为 `RushRegistry`,但 types.ts 已\n * `export interface RushRegistry` 作为数据结构。为避免同名冲突,这里的\n * class 命名为 `RushRegistryStore`(Store = 数据的 owner / 读写入口),\n * 语义上等价于 spec §3.4 的 class,方法签名与 spec 保持一致。\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { constants as fsConstants } from 'node:fs';\nimport {\n access,\n mkdir,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, resolve } from 'node:path';\nimport type {\n MigrationRecord,\n PluginRef,\n PluginRefString,\n RushRegistry as RushRegistryData,\n RushRegistryEntry,\n} from './types.js';\n\n/**\n * 默认 registry 文件路径(`~/.rush/plugins/registry.json`)。\n *\n * 测试必须通过 `{ home }` 注入临时目录,**禁止**写用户真实 `~/.rush/`。\n */\nexport const REGISTRY_RELATIVE_PATH = '.rush/plugins/registry.json' as const;\n\n/**\n * 当前 schema 版本。升级时 bump 这里 + 在 `load()` 里加迁移分支。\n */\nexport const REGISTRY_SCHEMA_VERSION = 1 as const;\n\n// ---------------------------------------------------------------------------\n// Error 类型\n// ---------------------------------------------------------------------------\n\n/** 基类 —— 便于 CLI 统一 `instanceof RushRegistryError` catch。 */\nexport class RushRegistryError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'RushRegistryError';\n }\n}\n\n/**\n * JSON 损坏 / 结构不合法时抛出。spec §3.3:不尝试修复,请用户手动处理。\n */\nexport class RushRegistryCorruptError extends RushRegistryError {\n constructor(\n public readonly filePath: string,\n public readonly cause: unknown\n ) {\n super(\n `rush-ai registry.json 解析失败(${filePath})。请备份后删除或手工修复。原因:${String(\n (cause as Error | undefined)?.message ?? cause\n )}`\n );\n this.name = 'RushRegistryCorruptError';\n }\n}\n\n/**\n * 读到旧版本 schema —— 只读不写,CLI 层应提示升级 rush-ai。\n */\nexport class RushRegistrySchemaMismatchError extends RushRegistryError {\n constructor(\n public readonly filePath: string,\n public readonly foundVersion: number,\n public readonly expectedVersion: number\n ) {\n super(\n `rush-ai registry schemaVersion=${foundVersion},当前 rush-ai 仅支持 schemaVersion=${expectedVersion}。请升级 rush-ai 或备份后迁移。`\n );\n this.name = 'RushRegistrySchemaMismatchError';\n }\n}\n\n/**\n * 并发写冲突(mtime 在读后发生了变化,即便 retry 一次也没稳定)。\n * CLI 层提示用户重试。\n */\nexport class RushRegistryConflictError extends RushRegistryError {\n constructor(public readonly filePath: string) {\n super(\n `rush-ai registry 并发写冲突:另一个进程在我们读-改-写期间修改了 ${filePath}。请重试。`\n );\n this.name = 'RushRegistryConflictError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** PluginRef ↔ key 互转。spec §1.1 约定 `name@marketplace`。 */\nexport function pluginRefToKey(ref: PluginRef): PluginRefString {\n return `${ref.name}@${ref.marketplace}` as PluginRefString;\n}\n\n/**\n * 从 `name@marketplace` 字符串解析出 PluginRef。仅内部使用(list 方法)。\n *\n * 注意:marketplace 自身不允许再含 `@`(避免歧义)。只按**第一个** `@` 劈。\n */\nfunction keyToPluginRef(key: PluginRefString): PluginRef {\n const at = key.indexOf('@');\n if (at <= 0 || at === key.length - 1) {\n throw new RushRegistryError(\n `registry 键 \"${key}\" 非法(要求 \\`name@marketplace\\`,name/marketplace 非空)`\n );\n }\n return { name: key.slice(0, at), marketplace: key.slice(at + 1) };\n}\n\n/**\n * 判断 raw JSON 是否符合我们期望的顶层 shape。不做 deep 校验(每个 entry 的\n * 字段类型由 InstallResult 生产方保证),只做 `schemaVersion` + `plugins` 壳子。\n */\nfunction assertRegistryShape(\n raw: unknown,\n filePath: string\n): asserts raw is RushRegistryData {\n if (!raw || typeof raw !== 'object') {\n throw new RushRegistryCorruptError(\n filePath,\n new Error('registry root 必须是 object')\n );\n }\n const obj = raw as Record<string, unknown>;\n if (typeof obj.schemaVersion !== 'number') {\n throw new RushRegistryCorruptError(\n filePath,\n new Error('registry.schemaVersion 必须是数字')\n );\n }\n if (\n obj.plugins !== undefined &&\n (typeof obj.plugins !== 'object' ||\n Array.isArray(obj.plugins) ||\n obj.plugins === null)\n ) {\n throw new RushRegistryCorruptError(\n filePath,\n new Error('registry.plugins 必须是 object')\n );\n }\n if (\n obj.migrations !== undefined &&\n (typeof obj.migrations !== 'object' ||\n Array.isArray(obj.migrations) ||\n obj.migrations === null)\n ) {\n throw new RushRegistryCorruptError(\n filePath,\n new Error('registry.migrations 必须是 object(如存在)')\n );\n }\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p, fsConstants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * write-temp-rename 原子替换。`rename` 在 POSIX 同一文件系统里是原子的。\n *\n * tmp 文件名用 uuid 避免并发冲突(即便同进程多次 save 也安全)。\n * 失败时 best-effort 清掉 tmp,不覆盖原始错误。\n */\nasync function atomicWrite(filePath: string, content: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n const tmp = `${filePath}.${randomUUID()}.tmp`;\n try {\n await writeFile(tmp, content, { encoding: 'utf8', flag: 'w' });\n await rename(tmp, filePath);\n } catch (err) {\n // best-effort cleanup;吞掉 cleanup 错误,保留原始错误\n await rm(tmp, { force: true }).catch(() => {});\n throw err;\n }\n}\n\n/**\n * 从给定路径读 registry 文件。抽出便于单元测试和 `reloadAndMerge` 复用。\n *\n * 不处理 HOME 解析——调用方负责传绝对路径。\n *\n * schemaVersion 不匹配时**不抛错**——load 仍返回解析后的数据,但把\n * `schemaVersion` 原样保留(本 Phase 1 仅支持 1)。save 时会在此基础上检测并\n * 拒绝写入,对齐 spec §3.3 \"读旧版但不写入\"。\n */\nasync function loadFromPath(\n filePath: string\n): Promise<{ data: RushRegistryData; mtimeMs: number | null }> {\n if (!(await pathExists(filePath))) {\n return {\n data: { schemaVersion: REGISTRY_SCHEMA_VERSION, plugins: {} },\n mtimeMs: null,\n };\n }\n\n const stats = await stat(filePath);\n const raw = await readFile(filePath, 'utf8');\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new RushRegistryCorruptError(filePath, err);\n }\n\n assertRegistryShape(parsed, filePath);\n\n // 注意:schemaVersion 不匹配**不**在此处抛错。按 spec §3.3 语义:\n // \"读旧版但不写入,退出时提示用户升级 rush-ai\"。save 里再检测。\n //\n // 这样下游 CLI 即便遇到旧 schema 也可以列出 plugins 做展示,只是写路径会报错。\n const data: RushRegistryData = {\n // 类型标注为 1,但运行时可能存储 parsed.schemaVersion(不同值)——\n // save 时会用 REGISTRY_SCHEMA_VERSION 常量比对。\n schemaVersion: parsed.schemaVersion as RushRegistryData['schemaVersion'],\n plugins: parsed.plugins ?? {},\n };\n if (parsed.migrations !== undefined) {\n data.migrations = parsed.migrations;\n }\n\n return { data, mtimeMs: stats.mtimeMs };\n}\n\n// ---------------------------------------------------------------------------\n// RushRegistryStore —— spec §3.4 的 class 实现\n// ---------------------------------------------------------------------------\n\nexport interface RushRegistryStoreOptions {\n /**\n * 注入的 HOME 目录(仅测试用)。生产代码传 `undefined`,默认走 `os.homedir()`。\n *\n * **测试铁律**:所有使用本 store 的单测都必须通过此参数注入 `os.tmpdir()` 下\n * 的临时目录,禁止写用户真实 `~/.rush/`。\n */\n home?: string;\n}\n\n/**\n * rush-ai registry(账本)的读-改-写入口。\n *\n * 使用模式:\n * ```ts\n * const store = await RushRegistryStore.load();\n * store.set(ref, entry);\n * await store.save();\n * ```\n *\n * 并发安全:`load()` 记录文件 mtime;`save()` 写入前比较 mtime,变了 → reload\n * 合并我们的 diff 重试一次;再变则抛 `RushRegistryConflictError`。\n */\nexport class RushRegistryStore {\n /**\n * Registry 文件的绝对路径(例如 `/Users/x/.rush/plugins/registry.json`)。\n */\n public readonly filePath: string;\n\n /** 内存中的数据视图。直接修改后调用 `save()` 持久化。 */\n private data: RushRegistryData;\n\n /**\n * load 时记录的文件 mtime(ms)。null 代表文件当时不存在。save 前会比对。\n */\n private loadedMtimeMs: number | null;\n\n /**\n * 本进程显式 delete 的 plugin keys(delete tombstone)。\n *\n * 并发冲突 reload+merge 时需要把这些 key 从 disk 副本里移除,否则 merge 会\n * \"复活\" B 进程留下的旧条目(违反 read-modify-write 语义)。\n *\n * set 时 tombstone 自动清除(先 delete 再 set 视为复活该条目)。\n */\n private deletedPluginKeys: Set<PluginRefString> = new Set();\n\n private constructor(\n filePath: string,\n data: RushRegistryData,\n loadedMtimeMs: number | null\n ) {\n this.filePath = filePath;\n this.data = data;\n this.loadedMtimeMs = loadedMtimeMs;\n }\n\n /**\n * 计算 registry 文件路径(根据 `home` 注入或默认 `os.homedir()`)。\n */\n static resolvePath(opts?: RushRegistryStoreOptions): string {\n const home = opts?.home ?? homedir();\n return resolve(home, REGISTRY_RELATIVE_PATH);\n }\n\n /**\n * 读取 registry 文件。\n *\n * - 文件不存在 → 返回空 registry(`{schemaVersion: 1, plugins: {}}`),`loadedMtimeMs = null`\n * - JSON 损坏 / 顶层 shape 错 → `RushRegistryCorruptError`\n * - schemaVersion 不等于 `REGISTRY_SCHEMA_VERSION` → **不抛**,原样读入内存\n * (对齐 spec §3.3 \"读旧版但不写入\")。后续调用 `save()` 时才抛\n * `RushRegistrySchemaMismatchError`。\n * - schemaVersion 为本支持版本 → 正常返回\n */\n static async load(\n opts?: RushRegistryStoreOptions\n ): Promise<RushRegistryStore> {\n const filePath = RushRegistryStore.resolvePath(opts);\n const { data, mtimeMs } = await loadFromPath(filePath);\n return new RushRegistryStore(filePath, data, mtimeMs);\n }\n\n /**\n * 原子写入当前内存状态到磁盘。\n *\n * mtime 冲突检测(spec §3.3):\n * 1. 写前 stat 文件,对比 `loadedMtimeMs`\n * 2. 不匹配 → retry 一次:reload 最新磁盘状态、把内存 diff re-apply 再 save\n * 3. 再不匹配 → `RushRegistryConflictError`\n *\n * diff re-apply 策略:将当前内存里的 plugins/migrations 条目在最新磁盘版本上 merge\n * (**内存里的条目覆盖磁盘**——本进程是唯一 writer 的场景下这是正确的;并发\n * writer 场景 coordinator 明确 spec §4 \"只有 CLI 层写\" 不允许,靠异常兜底)。\n */\n async save(): Promise<void> {\n // spec §3.3:schemaVersion 不匹配 → 读旧版但不写入。\n // load 阶段已经允许把旧 schema 读进内存,save 时在每个写盘点前都要检查一次\n // (reloadAndMerge 可能把 schemaVersion 从 disk 替换进来,必须重新把关)。\n this.assertWriteableSchema();\n await this.saveWithRetry(0);\n }\n\n /**\n * 断言当前内存 schemaVersion 是本 CLI 能写入的版本。\n *\n * 任何写盘点前都应调用——包括 reloadAndMerge 之后(避免\"A load 时是 v1,B 抢\n * 写 v999,A 重试时用 B 的 v999 写盘\"的路径)。\n */\n private assertWriteableSchema(): void {\n if (this.data.schemaVersion !== REGISTRY_SCHEMA_VERSION) {\n throw new RushRegistrySchemaMismatchError(\n this.filePath,\n this.data.schemaVersion as unknown as number,\n REGISTRY_SCHEMA_VERSION\n );\n }\n }\n\n private async saveWithRetry(attempt: number): Promise<void> {\n // reloadAndMerge 可能已把磁盘上更高 schemaVersion 塞进 this.data,这里再把关。\n this.assertWriteableSchema();\n\n // 首次写入(文件本不存在):只检查现在是否仍不存在;存在了意味着别的进程抢写了\n if (this.loadedMtimeMs === null) {\n if (await pathExists(this.filePath)) {\n if (attempt === 0) {\n await this.reloadAndMerge();\n return this.saveWithRetry(1);\n }\n throw new RushRegistryConflictError(this.filePath);\n }\n // 正常首次写\n await atomicWrite(this.filePath, this.serialize());\n // 更新 loadedMtime 方便同一进程里继续操作\n const stats = await stat(this.filePath);\n this.loadedMtimeMs = stats.mtimeMs;\n // 写入成功 → 清空 tombstone(本批次 delete 已持久化到磁盘)\n this.deletedPluginKeys.clear();\n return;\n }\n\n // 非首次:比对 mtime\n const stats = await stat(this.filePath).catch(() => null);\n if (stats === null) {\n // 文件消失(被手工删),当作冲突(需要 reload)\n if (attempt === 0) {\n await this.reloadAndMerge();\n return this.saveWithRetry(1);\n }\n throw new RushRegistryConflictError(this.filePath);\n }\n\n if (stats.mtimeMs !== this.loadedMtimeMs) {\n if (attempt === 0) {\n await this.reloadAndMerge();\n return this.saveWithRetry(1);\n }\n throw new RushRegistryConflictError(this.filePath);\n }\n\n await atomicWrite(this.filePath, this.serialize());\n const afterStats = await stat(this.filePath);\n this.loadedMtimeMs = afterStats.mtimeMs;\n // 写入成功 → 清空 tombstone\n this.deletedPluginKeys.clear();\n }\n\n /**\n * 磁盘被别人改了 → 把别人的改动 load 进来,再把本进程的内存改动 merge 上去。\n *\n * merge 规则:\n * - 磁盘条目作为 base\n * - 本进程的 delete tombstone(`deletedPluginKeys` / `deletedMigrationKeys`)\n * 从 base 里移除对应 key(避免磁盘上 B 进程的旧条目被复活)\n * - 本进程内存里的 plugins/migrations 条目覆盖同 key(ours wins)\n *\n * 注意:直接读 `this.filePath`,不走 `load()` 的 HOME resolution,避免二次路径计算。\n */\n private async reloadAndMerge(): Promise<void> {\n const ourPlugins = this.data.plugins;\n const ourMigrations = this.data.migrations;\n const ourDeletedPlugins = this.deletedPluginKeys;\n\n const disk = await loadFromPath(this.filePath);\n\n // 1. 从 disk 副本移除我们主动 delete 的 key(避免复活)\n const mergedPlugins: Record<string, RushRegistryEntry> = {};\n for (const [key, entry] of Object.entries(disk.data.plugins)) {\n if (!ourDeletedPlugins.has(key as PluginRefString)) {\n mergedPlugins[key] = entry;\n }\n }\n // 2. 叠加 ours(同 key 覆盖)\n for (const [key, entry] of Object.entries(ourPlugins)) {\n mergedPlugins[key] = entry;\n }\n\n // migrations append-only 语义(本 spec 范围内没有 deleteMigration API),\n // 直接 disk + ours 合并;ours 覆盖同 key。\n let mergedMigrations: RushRegistryData['migrations'] | undefined;\n const hasAnyMigration =\n (ourMigrations && Object.keys(ourMigrations).length > 0) ||\n (disk.data.migrations && Object.keys(disk.data.migrations).length > 0);\n if (hasAnyMigration) {\n mergedMigrations = {\n ...(disk.data.migrations ?? {}),\n ...(ourMigrations ?? {}),\n };\n }\n\n this.data = {\n schemaVersion: disk.data.schemaVersion,\n plugins: mergedPlugins,\n ...(mergedMigrations ? { migrations: mergedMigrations } : {}),\n };\n this.loadedMtimeMs = disk.mtimeMs;\n // tombstone 已被 apply,save 成功后由 saveWithRetry 末尾清空\n }\n\n /** 序列化成 JSON 字符串(2-space 缩进,便于人工审查)。 */\n private serialize(): string {\n // 稳定输出:plugins / migrations key 按字典序\n const plugins = Object.fromEntries(\n Object.entries(this.data.plugins).sort(([a], [b]) =>\n a < b ? -1 : a > b ? 1 : 0\n )\n );\n const out: RushRegistryData = {\n schemaVersion: this.data.schemaVersion,\n plugins,\n };\n if (this.data.migrations) {\n out.migrations = Object.fromEntries(\n Object.entries(this.data.migrations).sort(([a], [b]) =>\n a < b ? -1 : a > b ? 1 : 0\n )\n );\n }\n return `${JSON.stringify(out, null, 2)}\\n`;\n }\n\n // -------------------------------------------------------------------------\n // spec §3.4 的 API\n // -------------------------------------------------------------------------\n\n /** 获取某插件的 registry 条目。未安装返回 undefined。 */\n get(ref: PluginRef): RushRegistryEntry | undefined {\n return this.data.plugins[pluginRefToKey(ref)];\n }\n\n /**\n * 设置某插件的 registry 条目(覆盖或新增)。\n *\n * 若该 key 曾被 `delete()` 打过 tombstone,本次 set 视为\"复活\"——清除 tombstone,\n * 以免 reload+merge 时把 disk 上同 key 条目和本次 set 都清掉。\n */\n set(ref: PluginRef, entry: RushRegistryEntry): void {\n const key = pluginRefToKey(ref);\n this.data.plugins[key] = entry;\n this.deletedPluginKeys.delete(key);\n }\n\n /**\n * 删除某插件的 registry 条目(不存在是 no-op)。\n *\n * 同时把 key 加入 `deletedPluginKeys` tombstone 集合——保证并发 reload+merge\n * 时不会被 disk 上其它进程留下的同 key 旧条目\"复活\"。\n */\n delete(ref: PluginRef): void {\n const key = pluginRefToKey(ref);\n delete this.data.plugins[key];\n this.deletedPluginKeys.add(key);\n }\n\n /** 列出所有已记录插件,顺序按 key 字典序。 */\n list(): Array<[PluginRef, RushRegistryEntry]> {\n return Object.entries(this.data.plugins)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([key, entry]) => [keyToPluginRef(key as PluginRefString), entry]);\n }\n\n /**\n * 读取迁移记录(M4 task-12 使用)。spec §3.2 允许 migrations 缺省。\n */\n getMigration(key: string): MigrationRecord | undefined {\n return this.data.migrations?.[key];\n }\n\n /**\n * 写迁移记录。若 `migrations` 字段尚未初始化则自动创建。\n */\n setMigration(key: string, record: MigrationRecord): void {\n if (!this.data.migrations) {\n this.data.migrations = {};\n }\n this.data.migrations[key] = record;\n }\n\n /**\n * 当前内存快照(deep-clone,便于测试断言或 debug dump)。\n * 修改返回值不影响内部状态。\n */\n snapshot(): RushRegistryData {\n return structuredClone(this.data);\n }\n}\n","/**\n * `rush-ai marketplace list`。\n *\n * Source of truth: `specs/cli-commands.md` §1.3\n *\n * 输出(人看模式):\n * ```\n * NAME SOURCE INSTALLED PLUGINS\n * rush-marketplace github:kanyun/rush-plugins 3 (rush, yuanli-infra, octo)\n * official directory:/abs/path (empty)\n * ```\n *\n * 数据源:\n * - NAME / SOURCE:`MarketplaceCache.list()`\n * - INSTALLED PLUGINS:`RushRegistryStore` 里按 `ref.marketplace === name` 聚合;\n * registry 还没记录时显示 `(empty)`\n *\n * JSON 模式(`--json`):\n * ```json\n * { \"marketplaces\": [{ \"name\": \"...\", \"source\": \"...\", \"installedPlugins\": [\"...\"] }] }\n * ```\n */\n\nimport type { Command } from 'commander';\nimport { RushRegistryStore } from '../../installers/registry.js';\nimport { MarketplaceCache } from '../../marketplaces/index.js';\nimport { formatOutput, resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\n\nexport interface ListCommandInput {\n home?: string;\n cache?: MarketplaceCache;\n registry?: RushRegistryStore;\n}\n\nexport interface ListEntry {\n name: string;\n /** raw source 字符串(如 `github:owner/repo`) */\n source: string;\n /** 已装插件名列表(仅 name,不含 `@mkt` 后缀——mkt 已经在外层 NAME 列) */\n installedPlugins: string[];\n}\n\nexport interface ListCommandResult {\n marketplaces: ListEntry[];\n}\n\n/**\n * 纯函数实现。空 cache + 空 registry → 返回 `{ marketplaces: [] }`(不报错)。\n */\nexport async function runList(\n input: ListCommandInput = {}\n): Promise<ListCommandResult> {\n const homeOpts = input.home !== undefined ? { home: input.home } : {};\n const cache = input.cache ?? new MarketplaceCache(homeOpts);\n const registry = input.registry ?? (await RushRegistryStore.load(homeOpts));\n\n const cached = await cache.list();\n\n // 聚合 registry 按 marketplace 分组\n const byMkt = new Map<string, string[]>();\n for (const [ref] of registry.list()) {\n const arr = byMkt.get(ref.marketplace) ?? [];\n arr.push(ref.name);\n byMkt.set(ref.marketplace, arr);\n }\n\n const marketplaces: ListEntry[] = cached.map((entry) => ({\n name: entry.name,\n source: entry.source.raw,\n installedPlugins: byMkt.get(entry.name) ?? [],\n }));\n\n return { marketplaces };\n}\n\n// -- commander wrapper --------------------------------------------------------\n\nexport function registerListCommand(group: Command, root: Command): void {\n group\n .command('list')\n .alias('ls')\n .description(\n 'List locally registered marketplaces and their installed plugins'\n )\n .action(async () => {\n const format = resolveFormat(root.opts());\n const result = await runList();\n\n if (format === 'json') {\n output.log(JSON.stringify(result, null, 2));\n return;\n }\n\n if (result.marketplaces.length === 0) {\n output.log('(no marketplaces registered)');\n output.dim(\n \" Add one with 'rush-ai marketplace add <source>' (e.g. github:owner/repo)\"\n );\n return;\n }\n\n const rows = result.marketplaces.map((m) => ({\n NAME: m.name,\n SOURCE: m.source,\n 'INSTALLED PLUGINS':\n m.installedPlugins.length === 0\n ? '(empty)'\n : `${m.installedPlugins.length} (${m.installedPlugins.join(', ')})`,\n }));\n output.log(formatOutput(rows, format));\n });\n}\n","/**\n * `rush-ai marketplace remove <name>`。\n *\n * Source of truth: `specs/cli-commands.md` §1.2\n *\n * 行为(v1 简化,见 `./index.ts` 决策说明):\n * 1. 检查 rush-ai registry(`~/.rush/plugins/registry.json`)里是否仍有插件装自该 marketplace\n * - 有 → **拒绝删除**,提示先 `plugin uninstall`\n * - 无 → 进入步骤 2\n * 2. 调用 `MarketplaceCache.remove(name)`——从 `~/.rush/marketplaces/<name>/` 清掉 cache\n * - directory: source:只删 meta 目录,不触碰用户 path\n * - github: source:删整个 cache 目录\n * - 不存在:no-op(静默成功;CLI 层单独提示)\n * 3. **不**清 `known_marketplaces.json` / `config.toml`——由 `plugin uninstall` 时各 Installer\n * 自行维护(v1 简化)。\n *\n * 退出码:\n * - 0 成功(含 no-op)\n * - 1 通用错误:仍有插件装自该 marketplace、IO 错\n */\n\nimport type { Command } from 'commander';\nimport {\n pluginRefToKey,\n RushRegistryStore,\n} from '../../installers/registry.js';\nimport { MarketplaceCache } from '../../marketplaces/index.js';\nimport { output } from '../../output/logger.js';\nimport { RushError } from '../../util/errors.js';\n\nexport interface RemoveCommandInput {\n /** marketplace 名 */\n name: string;\n /** 注入自定义 home(测试用) */\n home?: string;\n /** 直接注入 cache 实例 */\n cache?: MarketplaceCache;\n /** 直接注入 registry store(测试可 pre-seed 插件) */\n registry?: RushRegistryStore;\n /**\n * `--force`:即使 registry 里还有插件装自该 marketplace,也强删(不推荐;\n * 仅供用户应急恢复损坏状态)。对齐 spec 把失败做成主路径 + 明确 flag 绕过。\n */\n force?: boolean;\n}\n\nexport interface RemoveCommandResult {\n /** 本次是否真的有东西可删(cache 目录本来就不在 → noOp=true) */\n noOp: boolean;\n /** 若强删且 registry 里有残留插件,列出来给用户看(CLI 可展示警告) */\n orphanedPlugins: string[];\n}\n\n/**\n * 纯函数实现。\n *\n * 失败:\n * - `RushError(code='MARKETPLACE_IN_USE', exitCode=1)` — 有插件装自该 marketplace,拒绝删\n * - 其他 IO 错:以 RushError(exitCode=1) 透传\n */\nexport async function runRemove(\n input: RemoveCommandInput\n): Promise<RemoveCommandResult> {\n const homeOpts = input.home !== undefined ? { home: input.home } : {};\n const cache = input.cache ?? new MarketplaceCache(homeOpts);\n const registry = input.registry ?? (await RushRegistryStore.load(homeOpts));\n\n // 找出所有装自此 marketplace 的插件\n const dependents = registry\n .list()\n .filter(([ref]) => ref.marketplace === input.name);\n const dependentKeys = dependents.map(([ref]) => pluginRefToKey(ref));\n\n if (dependents.length > 0 && !input.force) {\n throw new RushError(\n `Cannot remove marketplace '${input.name}': ${\n dependents.length\n } installed plugin(s) still depend on it (${dependentKeys.join(\n ', '\n )}). Run 'rush-ai plugin uninstall <name>@${input.name}' for each before removing the marketplace, or pass --force to ignore.`,\n {\n marketplaceName: input.name,\n dependents: dependentKeys,\n },\n 'MARKETPLACE_IN_USE',\n 1\n );\n }\n\n const exists = await cache.has(input.name).catch(() => false);\n await cache.remove(input.name);\n\n return {\n noOp: !exists,\n orphanedPlugins: dependentKeys,\n };\n}\n\n// -- commander wrapper --------------------------------------------------------\n\nexport function registerRemoveCommand(group: Command, _root: Command): void {\n group\n .command('remove')\n .description('Remove a marketplace from the local cache')\n .argument('<name>', 'marketplace name')\n .option(\n '--force',\n 'remove even when installed plugins still depend on this marketplace'\n )\n .action(async (name: string, opts: { force?: boolean }) => {\n const result = await runRemove({\n name,\n ...(opts.force ? { force: true } : {}),\n });\n\n if (result.noOp) {\n output.warn(\n `Marketplace '${name}' was not in the local cache; nothing to remove.`\n );\n } else {\n output.success(`Removed marketplace '${name}' from local cache.`);\n }\n\n // Orphan 警告在 noOp / 非 noOp 两种路径下都要展示——用户即使\"什么都没删\",\n // 只要 registry 里仍有 stale 插件条目装自此 mkt,也需要被明确引导去清理。\n if (result.orphanedPlugins.length > 0) {\n output.warn(\n `${result.orphanedPlugins.length} plugin(s) orphaned in registry (removed with --force): ${result.orphanedPlugins.join(', ')}`\n );\n output.dim(\n \" Run 'rush-ai plugin uninstall <name>@<mkt>' for each orphan to clean registry.\"\n );\n }\n if (!result.noOp) {\n output.dim(\n ' ⓘ IDE marketplace deregistration deferred to `rush-ai plugin uninstall`.'\n );\n }\n });\n}\n","/**\n * `rush-ai marketplace update [<name>]`。\n *\n * Source of truth: `specs/cli-commands.md` §1.4\n *\n * 行为:\n * - 省略 `<name>` → 更新 cache 里所有 marketplace\n * - 带 `<name>` → 只更这一个\n * - directory: source:no-op(源直接引用)\n * - github: source:`git pull` / re-clone(由 `MarketplaceCache.update` 实现)\n * - 未找到的 name:抛 `RushError(code='MARKETPLACE_NOT_FOUND', exitCode=1)`\n *\n * **不**自动重装插件(spec §1.4 语义,重装走 `plugin update`)。\n */\n\nimport type { Command } from 'commander';\nimport {\n MarketplaceCache,\n MarketplaceNotFoundError,\n NotImplementedError,\n} from '../../marketplaces/index.js';\nimport { output } from '../../output/logger.js';\nimport { RushError } from '../../util/errors.js';\n\nexport interface UpdateCommandInput {\n /** 指定 marketplace 名;undefined → 更新全部 */\n name?: string;\n home?: string;\n cache?: MarketplaceCache;\n /**\n * `--dry-run`:不写磁盘,只返回\"会 update 哪些 marketplace\"。\n * spec §3.5 要求 `marketplace update` 支持该 flag。\n */\n dryRun?: boolean;\n}\n\nexport interface UpdateEntry {\n name: string;\n /** 被跳过(directory: source 或异常可接受跳过) */\n skipped: boolean;\n /** 本条是 dry-run 模式下的计划(不代表已执行) */\n dryRun?: boolean;\n /** 失败原因(如 git pull 失败) */\n error?: string;\n}\n\nexport interface UpdateCommandResult {\n results: UpdateEntry[];\n}\n\n/**\n * 纯函数实现。\n *\n * - 单 name 找不到 → `RushError(code='MARKETPLACE_NOT_FOUND', exitCode=1)`\n * - 全量 update 时某个失败 → 不中断,继续更下一个;结果在 `results[i].error` 里\n * - directory: source → skipped=true\n * - 未实现 source (git:/rush://npm:) → skipped=true + error 说明\n */\nexport async function runUpdate(\n input: UpdateCommandInput = {}\n): Promise<UpdateCommandResult> {\n const homeOpts = input.home !== undefined ? { home: input.home } : {};\n const cache = input.cache ?? new MarketplaceCache(homeOpts);\n\n const targets: string[] = [];\n if (input.name) {\n if (!(await cache.has(input.name))) {\n throw new RushError(\n `Marketplace '${input.name}' is not in the local cache. Run 'rush-ai marketplace list' to see registered marketplaces.`,\n { marketplaceName: input.name },\n 'MARKETPLACE_NOT_FOUND',\n 1\n );\n }\n targets.push(input.name);\n } else {\n const all = await cache.list();\n for (const entry of all) targets.push(entry.name);\n }\n\n const results: UpdateEntry[] = [];\n for (const name of targets) {\n try {\n // 先看 source kind 决定要不要 update(避免 directory 的 no-op 被当作\"真更新\")\n const existing = await cache.get(name);\n if (existing.source.kind === 'directory') {\n results.push({\n name,\n skipped: true,\n ...(input.dryRun ? { dryRun: true } : {}),\n });\n continue;\n }\n if (input.dryRun) {\n // dry-run:不写磁盘,只标记\"会 update\"。退出码仍按真跑一致(无错即 0)。\n results.push({ name, skipped: false, dryRun: true });\n continue;\n }\n await cache.update(name);\n results.push({ name, skipped: false });\n } catch (err) {\n if (err instanceof MarketplaceNotFoundError) {\n // 理论上 has() 已挡;保留以防竞态\n results.push({\n name,\n skipped: false,\n error: `not found (was it removed concurrently?)`,\n });\n continue;\n }\n if (err instanceof NotImplementedError) {\n results.push({\n name,\n skipped: true,\n error: err.message,\n });\n continue;\n }\n const message = err instanceof Error ? err.message : String(err);\n results.push({ name, skipped: false, error: message });\n }\n }\n\n return { results };\n}\n\n// -- commander wrapper --------------------------------------------------------\n\nexport function registerUpdateCommand(group: Command, _root: Command): void {\n group\n .command('update')\n .description(\n 'Refresh local marketplace cache (git pull for github: sources; directory: is a no-op)'\n )\n .argument('[name]', 'marketplace name; omit to update all')\n .option(\n '--dry-run',\n \"don't touch disk; print the marketplaces that would be updated\"\n )\n .action(async (name: string | undefined, opts: { dryRun?: boolean }) => {\n const result = await runUpdate({\n ...(name !== undefined ? { name } : {}),\n ...(opts.dryRun ? { dryRun: true } : {}),\n });\n\n if (result.results.length === 0) {\n output.warn('No marketplaces to update.');\n output.dim(\" Add one with 'rush-ai marketplace add <source>' first.\");\n return;\n }\n\n let failed = 0;\n for (const entry of result.results) {\n if (entry.skipped) {\n output.dim(\n ` ↷ ${entry.name} skipped${entry.error ? ` (${entry.error})` : ' (no-op)'}`\n );\n } else if (entry.error) {\n output.error(` ✗ ${entry.name}: ${entry.error}`);\n failed += 1;\n } else if (entry.dryRun) {\n output.dim(` → would update ${entry.name}`);\n } else {\n output.success(`Updated ${entry.name}`);\n }\n }\n\n if (opts.dryRun) {\n output.dim(' (dry-run: nothing was written to disk)');\n }\n\n if (failed > 0) {\n // 使 exit code 非 0(通过抛 RushError 让 index.ts 统一处理)\n throw new RushError(\n `${failed} marketplace(s) failed to update; see output above for details.`,\n { failed },\n 'MARKETPLACE_UPDATE_PARTIAL',\n 1\n );\n }\n });\n}\n","/**\n * `rush-ai marketplace` 命令组入口(task-9)。\n *\n * Source of truth: `specs/cli-commands.md` §1\n *\n * 子命令:\n * - `add <source> [--as <name>]` 见 add.ts\n * - `remove <name>` 见 remove.ts\n * - `list` 见 list.ts\n * - `update [<name>]` 见 update.ts\n *\n * 设计原则:\n * - 命令 handler 只做\"参数解析 + 调用 runXxx(ctx) + 格式化输出\",业务逻辑全部在\n * pure runner 函数里实现,方便单测直接调用(不用 parseAsync)\n * - 所有磁盘操作通过 `home` 参数注入(生产默认 `os.homedir()`,测试强制传 tmp dir)\n * - **v1 简化决策**(见 progress/agent-cli-marketplace.md):marketplace add/remove/update\n * 阶段**只写 rush-ai 自己的 cache**(`~/.rush/marketplaces/`)。IDE 侧的 marketplace\n * 注册(Claude Code 的 `known_marketplaces.json`、Codex 的 `config.toml` 下\n * `[marketplaces.*]`)延到 `plugin install` 命令(task-10)中由各 Installer 统一写。\n * 原因:Installer 尚未在 task-6/7/8 实现,此处若写 IDE 配置会提前耦合未落地的接口;\n * 同时 spec §1.1 的 \"写入 known_marketplaces.json / config.toml\" 步骤与 Installer\n * 职责高度重叠,延到 install 时机处理能自然复用。\n */\n\nimport type { Command } from 'commander';\nimport { registerAddCommand } from './add.js';\nimport { registerListCommand } from './list.js';\nimport { registerRemoveCommand } from './remove.js';\nimport { registerUpdateCommand } from './update.js';\n\nexport function registerMarketplaceCommand(program: Command): void {\n const marketplace = program\n .command('marketplace')\n .description(\n 'Manage plugin marketplaces (local cache under ~/.rush/marketplaces/)'\n );\n\n registerAddCommand(marketplace, program);\n registerRemoveCommand(marketplace, program);\n registerListCommand(marketplace, program);\n registerUpdateCommand(marketplace, program);\n}\n\nexport type { AddCommandInput, AddCommandResult } from './add.js';\n// -- Re-export pure runners for integration / advanced consumers --------------\nexport { runAdd } from './add.js';\nexport type { ListCommandInput, ListCommandResult, ListEntry } from './list.js';\nexport { runList } from './list.js';\nexport type { RemoveCommandInput, RemoveCommandResult } from './remove.js';\nexport { runRemove } from './remove.js';\nexport type {\n UpdateCommandInput,\n UpdateCommandResult,\n UpdateEntry,\n} from './update.js';\nexport { runUpdate } from './update.js';\n","import type { Command } from 'commander';\nimport { formatOutput, resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport { createClient } from '../../util/client.js';\nimport { ApiError } from '../../util/errors.js';\n\n/** MCP server as returned by GET /api/mcp-registry */\nexport interface McpServer {\n id: string;\n name: string;\n displayName: string;\n description: string;\n transportType: string;\n category: string;\n tags: string[];\n author: string;\n tools: Array<{ name: string; description: string }> | null;\n validationStatus: {\n status: string;\n errorCode?: string;\n errorMessage?: string;\n lastCheckedAt?: string;\n } | null;\n visibility: string;\n starCount: number;\n isBuiltin: boolean;\n}\n\n/** GET /api/mcp-registry response */\ninterface McpListApiResponse {\n success: boolean;\n data: McpServer[];\n meta?: {\n pagination?: {\n currentPage: number;\n totalPages: number;\n totalItems: number;\n itemsPerPage: number;\n hasNext: boolean;\n hasPrevious: boolean;\n };\n };\n}\n\n/** GET /api/mcp-registry/[id] response */\ninterface McpDetailApiResponse {\n success: boolean;\n data: McpServer;\n}\n\nfunction formatValidationStatus(vs: McpServer['validationStatus']): string {\n if (!vs) return '-';\n switch (vs.status) {\n case 'verified':\n return '✓ verified';\n case 'pending':\n return '… pending';\n case 'credential_required':\n return '⚠ credential required';\n case 'failed':\n return `✗ ${vs.errorCode || 'failed'}`;\n default:\n return vs.status;\n }\n}\n\nfunction truncate(str: string, max: number): string {\n if (str.length <= max) return str;\n return `${str.slice(0, max - 3)}...`;\n}\n\nexport function registerMcpCommand(program: Command): void {\n const mcp = program\n .command('mcp')\n .description('MCP server and platform MCP discovery');\n\n mcp\n .command('serve')\n .description('Start the MCP stdio server')\n .action(async () => {\n const { startMcpServer } = await import('./server.js');\n await startMcpServer();\n });\n\n mcp\n .command('list')\n .alias('ls')\n .description('List available MCP servers on the platform')\n .option('-c, --category <category>', 'Filter by category')\n .option('-t, --tag <tag>', 'Filter by tag')\n .option('-s, --search <query>', 'Search by name or description')\n .option('--transport <type>', 'Filter by transport type (stdio|sse|http)')\n .option(\n '-l, --limit <number>',\n 'Max results per page (default: 50, max: 100)',\n '50'\n )\n .option('-p, --page <number>', 'Page number (default: 1)', '1')\n .action(async (opts) => {\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n const params = new URLSearchParams();\n if (opts.category) params.set('category', opts.category);\n if (opts.tag) params.set('tag', opts.tag);\n if (opts.search) params.set('search', opts.search);\n if (opts.transport) params.set('transport', opts.transport);\n const parsedLimit = parseInt(opts.limit, 10);\n params.set(\n 'limit',\n String(\n Math.min(\n Math.max(Number.isFinite(parsedLimit) ? parsedLimit : 50, 1),\n 100\n )\n )\n );\n const parsedPage = parseInt(opts.page, 10);\n params.set(\n 'page',\n String(Math.max(Number.isFinite(parsedPage) ? parsedPage : 1, 1))\n );\n\n const { data } = await client.get<McpListApiResponse>(\n `/api/mcp-registry?${params.toString()}`\n );\n\n const servers = data.data;\n const pagination = data.meta?.pagination;\n\n if (format === 'json') {\n output.log(JSON.stringify(data, null, 2));\n return;\n }\n\n if (servers.length === 0) {\n output.info('No MCP servers found.');\n return;\n }\n\n const totalItems = pagination?.totalItems ?? servers.length;\n output.log(output.bold(`MCP Servers (${totalItems} total):`));\n output.newline();\n\n const rows = servers.map((m) => ({\n ID: m.id,\n 'Display Name': truncate(m.displayName, 30),\n Category: m.category || '-',\n Transport: m.transportType || '-',\n Tools: String(m.tools?.length ?? 0),\n Status: formatValidationStatus(m.validationStatus),\n }));\n\n output.log(\n formatOutput(rows, format, {\n columns: { 'Display Name': { maxWidth: 30 } },\n })\n );\n\n // Show pagination hint if there are more pages\n if (pagination && pagination.hasNext) {\n output.newline();\n output.dim(\n `Page ${pagination.currentPage} of ${pagination.totalPages}. Use --page ${pagination.currentPage + 1} to see more.`\n );\n }\n });\n\n mcp\n .command('list-tools <server-id>')\n .description('List tools provided by an MCP server')\n .action(async (serverId: string) => {\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n let data: McpDetailApiResponse;\n try {\n ({ data } = await client.get<McpDetailApiResponse>(\n `/api/mcp-registry/${encodeURIComponent(serverId)}`\n ));\n } catch (err) {\n if (err instanceof ApiError && err.status === 404) {\n output.error(`MCP server '${serverId}' not found.`);\n process.exitCode = 1;\n return;\n }\n throw err;\n }\n\n const server = data.data;\n\n if (format === 'json') {\n output.log(JSON.stringify(server, null, 2));\n return;\n }\n\n // Server summary header\n output.log(output.bold(`${server.displayName} (${server.id})`));\n output.log(` Category: ${server.category || '-'}`);\n output.log(` Transport: ${server.transportType || '-'}`);\n output.log(` Author: ${server.author || '-'}`);\n output.log(\n ` Status: ${formatValidationStatus(server.validationStatus)}`\n );\n output.newline();\n\n const tools = server.tools ?? [];\n if (tools.length === 0) {\n output.info('No tools defined for this MCP server.');\n return;\n }\n\n output.log(output.bold(`Tools (${tools.length}):`));\n output.newline();\n\n const rows = tools.map((t) => ({\n 'Tool Name': t.name || '-',\n Description: truncate(t.description || '', 60),\n }));\n\n output.log(\n formatOutput(rows, format, {\n columns: { Description: { maxWidth: 60 } },\n })\n );\n });\n\n mcp\n .command('install <mcp-id>')\n .description(\n 'Install an MCP server from Rush registry to Claude Desktop / Claude Code'\n )\n .option('-y, --yes', 'Skip confirmation prompts (use defaults)')\n .option(\n '--target <targets>',\n 'Comma-separated targets: claude-desktop,claude-code (default: both)'\n )\n .option('--allow-unverified', 'Allow installing unverified MCP servers')\n .option(\n '--set <key=value...>',\n 'Set credential values (e.g. --set TOKEN=xxx KEY=yyy)'\n )\n .action(async (mcpId: string, opts) => {\n const format = resolveFormat(program.opts());\n const { runMcpInstall, printMcpInstallSummary } = await import(\n './install.js'\n );\n\n // Parse --set key=value pairs\n let setValues: Record<string, string> | undefined;\n if (opts.set) {\n setValues = {};\n const raw: unknown[] = Array.isArray(opts.set) ? opts.set : [opts.set];\n for (const rawItem of raw) {\n const item = String(rawItem);\n const eqIdx = item.indexOf('=');\n if (eqIdx === -1) {\n const msg = `Invalid --set format: \"${item}\". Use --set KEY=VALUE`;\n if (format === 'json') {\n output.log(JSON.stringify({ error: msg }, null, 2));\n } else {\n output.error(msg);\n }\n process.exitCode = 1;\n return;\n }\n setValues[item.slice(0, eqIdx)] = item.slice(eqIdx + 1);\n }\n }\n\n try {\n const result = await runMcpInstall({\n mcpId,\n yes: opts.yes,\n targetRaw: opts.target,\n allowUnverified: opts.allowUnverified,\n setValues,\n });\n\n const anyOk = result.targets.some((t) => t.status === 'ok');\n const anyError = result.targets.some((t) => t.status === 'error');\n if (anyError || !anyOk) process.exitCode = 1;\n\n if (format === 'json') {\n output.log(JSON.stringify(result, null, 2));\n return;\n }\n\n printMcpInstallSummary(result);\n } catch (err) {\n if (format === 'json') {\n output.log(\n JSON.stringify(\n {\n error: err instanceof Error ? err.message : String(err),\n },\n null,\n 2\n )\n );\n } else {\n output.error(err instanceof Error ? err.message : String(err));\n }\n process.exitCode = 1;\n }\n });\n\n mcp\n .command('uninstall <mcp-id>')\n .description(\n 'Remove an MCP server from Claude Desktop / Claude Code config'\n )\n .option(\n '--target <targets>',\n 'Comma-separated targets: claude-desktop,claude-code (default: both)'\n )\n .action(async (mcpId: string, opts) => {\n const format = resolveFormat(program.opts());\n const { runMcpUninstall, printMcpUninstallSummary } = await import(\n './install.js'\n );\n\n try {\n const result = await runMcpUninstall({\n mcpId,\n targetRaw: opts.target,\n });\n\n const anyError = result.targets.some((t) => t.error);\n if (anyError) process.exitCode = 1;\n\n if (format === 'json') {\n output.log(JSON.stringify(result, null, 2));\n return;\n }\n\n printMcpUninstallSummary(result);\n } catch (err) {\n if (format === 'json') {\n output.log(\n JSON.stringify(\n {\n error: err instanceof Error ? err.message : String(err),\n },\n null,\n 2\n )\n );\n } else {\n output.error(err instanceof Error ? err.message : String(err));\n }\n process.exitCode = 1;\n }\n });\n}\n","/**\n * ClaudeCodeInstaller — 原生 plugin 写入器(task-6 产物)。\n *\n * Source of truth:\n * - `specs/plugin-schemas.md` §1 Claude Code 原生 plugin 格式\n * - `specs/installer-interface.md` §1-§4 Installer 接口 + artifacts 契约\n *\n * 职责:\n * - `detect()`:检查 `<home>/.claude/` 目录是否存在\n * - `install()`:把 `ResolvedPlugin` 写入 Claude Code 原生 plugin 结构\n * - cache 目录:`<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/`\n * - plugin.json:内嵌 mcpServers(对 rush 自己的插件规范化 command 绝对路径)\n * - known_marketplaces.json:V1 marketplace 注册表\n * - installed_plugins.json:V2 多 scope 数组\n * - settings.json.enabledPlugins:布尔 true\n * - `uninstall()`:反向清理(cache 目录**保留**,和 Claude Code 原生行为一致)\n * - `isInstalled()` / `list()`:查 installed_plugins.json\n *\n * 契约不变式:\n * - 所有 JSON 写入走 atomic `writeJsonFile`(write .tmp → rename)\n * - 失败**完整回滚**:删除本次写入的 cache 目录 + 恢复 3 个 JSON 到 install 前状态\n * - `install.artifacts.files`:本次写入/更新的绝对路径清单(含 cache dir + 3 个 JSON)\n * - `install.artifacts.mcpKeys`:plugin.json 里声明的 MCP server key 清单\n * - 幂等:同版本同 scope 重复 install → no-op(artifacts 空数组,status=ok)\n * - `force: true` 才覆盖;`dryRun: true` 不写磁盘\n */\n\nimport { randomUUID } from 'node:crypto';\nimport {\n copyFile,\n lstat,\n mkdir,\n readdir,\n readFile,\n readlink,\n rename,\n rm,\n stat,\n symlink,\n unlink,\n} from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join, resolve } from 'node:path';\nimport type {\n InstalledPlugin,\n InstallOptions,\n InstallResult,\n PluginCapability,\n PluginInstaller,\n PluginRef,\n ResolvedPlugin,\n UninstallOptions,\n UninstallResult,\n} from '../types.js';\nimport {\n AtomicJsonConflictError,\n pathExists,\n readJsonFile,\n writeJsonFile,\n} from './atomic-json.js';\nimport { defaultRushBinaryResolver, normalizeClaudeMcpServers } from './mcp.js';\nimport { CAPABILITY_DIRS, ClaudeCodePaths, pluginKey } from './paths.js';\n\n// ---------------------------------------------------------------------------\n// JSON shape(Claude Code 原生格式)\n// ---------------------------------------------------------------------------\n\n/** `known_marketplaces.json` shape(spec §1.2) */\ninterface KnownMarketplacesFile {\n [name: string]: KnownMarketplaceEntry;\n}\n\ninterface KnownMarketplaceEntry {\n source: {\n source: 'github' | 'git' | 'directory';\n repo?: string;\n url?: string;\n path?: string;\n };\n installLocation: string;\n lastUpdated: string;\n autoUpdate: boolean;\n}\n\n/** `installed_plugins.json` V2 shape(spec §1.3) */\ninterface InstalledPluginsFile {\n version: 2;\n plugins: Record<string, InstalledPluginScopeEntry[]>;\n}\n\ninterface InstalledPluginScopeEntry {\n scope: 'user' | 'project';\n installPath: string;\n version: string;\n installedAt: string;\n lastUpdated: string;\n gitCommitSha?: string;\n}\n\n/** `settings.json` 我们关心的字段子集。保留 `[key: string]: unknown` 透传未知字段 */\ninterface SettingsFile {\n enabledPlugins?: Record<string, boolean>;\n [key: string]: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/**\n * marketplace 来源描述——用于写入 `known_marketplaces.json.source` 字段\n * + 让 Installer 知道去哪里拿完整 marketplace repo 来布置 `plugins/marketplaces/<mkt>/`。\n *\n * Claude Code 期望 `installLocation` 指向**完整 marketplace repo 目录**(含\n * `.claude-plugin/marketplace.json`)。rush-ai 本地 cache(`~/.rush/marketplaces/<mkt>/`)\n * 就是该 repo;Installer 把它 symlink 到 `~/.claude/plugins/marketplaces/<mkt>/`\n * (symlink 失败时 fallback 到递归 copy)。\n *\n * 为什么 rush-ai 不直接把 Claude Code 期望的目录作为自己的 cache:保留独立\n * cache(`~/.rush/marketplaces/`)方便 Cursor/Codex 共享同一份 repo 源。\n */\nexport interface ClaudeCodeMarketplaceSource {\n /**\n * Marketplace 完整 repo 的绝对路径。内含 `.claude-plugin/marketplace.json`。\n *\n * 生产场景 = `~/.rush/marketplaces/<mkt>/`(`MarketplaceCache.pathFor(name)`);\n * 测试场景 = tmp 目录下构造的 fake repo。\n */\n rootDir: string;\n /**\n * 原始 source 类型——对应 `known_marketplaces.json.source` 字段。\n *\n * - `directory`:用户给的本地目录;`installLocation` 与 `path` 一致\n * - `github`:`owner/repo[#ref]`;`installLocation` 为 plugins/marketplaces/<mkt>\n * - `git`:完整 URL;同上\n */\n descriptor:\n | { kind: 'directory'; path: string }\n | { kind: 'github'; repo: string; ref?: string }\n | { kind: 'git'; url: string };\n}\n\nexport interface ClaudeCodeInstallerOptions {\n /**\n * 注入 HOME 目录(**测试必填**,生产代码省略默认 `os.homedir()`)。\n *\n * 测试铁律:所有单测必须用 `os.tmpdir()` 下的临时目录,**禁止**写用户真实 `~/.claude/`。\n */\n home?: string;\n /**\n * 覆盖 `which rush-ai` 的解析逻辑(测试注入用)。\n * 默认实现:`defaultRushBinaryResolver`(仅 `which rush-ai`,失败返回 undefined)。\n */\n rushBinaryResolver?: () => string | undefined;\n /**\n * 覆盖\"当前时间\"的来源(测试里注入固定时间)。\n * 默认 `() => new Date().toISOString()`。\n */\n now?: () => string;\n /**\n * 本次 install 的 marketplace 源(bug #4 修复)。\n *\n * **生产路径必须传**——CLI `defaultInstallerFactory` 从 `ResolvedMarketplace` 构造;\n * 不传时 Installer 退化到\"只写 installLocation 指向 plugins/marketplaces/<mkt>/,\n * 不布置 marketplace 目录\"的兼容模式(旧单测路径)。\n *\n * 退化模式下 installLocation 指向的目录不存在 → Claude Code 仍会报\n * `Plugin not found in marketplace`,所以生产禁用。\n */\n marketplaceSource?: ClaudeCodeMarketplaceSource;\n /**\n * 覆盖 symlink 创建行为(测试注入用;生产走 `fs.symlink`)。\n *\n * 返回 `{ kind: 'ok' }` 表示 symlink 成功;`{ kind: 'fallback', reason }` 让\n * Installer 退化到 copy;抛错则 install 整体 failed。\n */\n symlinkRunner?: (\n target: string,\n linkPath: string\n ) => Promise<{ kind: 'ok' } | { kind: 'fallback'; reason: string }>;\n}\n\n// ---------------------------------------------------------------------------\n// Installer 主体\n// ---------------------------------------------------------------------------\n\nexport class ClaudeCodeInstaller implements PluginInstaller {\n readonly target = 'claude-code' as const;\n readonly paths: ClaudeCodePaths;\n private readonly rushBinaryResolver: () => string | undefined;\n private readonly now: () => string;\n private readonly marketplaceSource?: ClaudeCodeMarketplaceSource;\n private readonly symlinkRunner: (\n target: string,\n linkPath: string\n ) => Promise<{ kind: 'ok' } | { kind: 'fallback'; reason: string }>;\n\n constructor(opts: ClaudeCodeInstallerOptions = {}) {\n const home = opts.home ?? homedir();\n this.paths = new ClaudeCodePaths(home);\n this.rushBinaryResolver =\n opts.rushBinaryResolver ?? defaultRushBinaryResolver;\n this.now = opts.now ?? (() => new Date().toISOString());\n if (opts.marketplaceSource) {\n this.marketplaceSource = opts.marketplaceSource;\n }\n this.symlinkRunner = opts.symlinkRunner ?? defaultSymlinkRunner;\n }\n\n // -------------------------------------------------------------------------\n // detect / isInstalled / list\n // -------------------------------------------------------------------------\n\n async detect(): Promise<boolean> {\n return dirExists(this.paths.claudeDir);\n }\n\n async isInstalled(ref: PluginRef): Promise<boolean> {\n if (!(await pathExists(this.paths.installedPluginsJson))) {\n return false;\n }\n const { data } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const entries = data.plugins?.[pluginKey(ref)];\n return Array.isArray(entries) && entries.length > 0;\n }\n\n async list(): Promise<InstalledPlugin[]> {\n if (!(await pathExists(this.paths.installedPluginsJson))) {\n return [];\n }\n const { data } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const out: InstalledPlugin[] = [];\n for (const [key, scopeEntries] of Object.entries(data.plugins ?? {})) {\n if (!Array.isArray(scopeEntries) || scopeEntries.length === 0) continue;\n const ref = parsePluginKey(key);\n if (!ref) continue;\n // 一个 plugin 可能多 scope,取第一个(user 优先)作为展示版本\n const pick =\n scopeEntries.find((e) => e.scope === 'user') ?? scopeEntries[0];\n if (!pick) continue;\n out.push({\n ref,\n version: pick.version,\n installedAt: pick.installedAt,\n targets: [this.target],\n });\n }\n return out;\n }\n\n // -------------------------------------------------------------------------\n // install\n // -------------------------------------------------------------------------\n\n async install(\n plugin: ResolvedPlugin,\n opts: InstallOptions = {}\n ): Promise<InstallResult> {\n // spec §2.1:install() 首行必须 detect()=false 短路\n if (!(await this.detect())) {\n return {\n target: this.target,\n status: 'skipped',\n included: [],\n skipped: [],\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n\n const { ref, version } = plugin;\n const pluginVersionDir = this.paths.pluginVersionDir(ref, version);\n const key = pluginKey(ref);\n\n // 幂等检查:同版本同 scope 已装 + !force → no-op\n // 注:isAlreadyInstalledAtVersion 读损坏 JSON 会抛,统一用 try/catch 转 failed\n if (!opts.force) {\n let already = false;\n try {\n already = await this.isAlreadyInstalledAtVersion(ref, version);\n } catch (err) {\n return this.buildFailedResult(err);\n }\n if (already) {\n return {\n target: this.target,\n status: 'ok',\n included: this.computeIncluded(plugin),\n skipped: this.computeSkipped(plugin),\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n }\n\n // dry-run:计算预期 artifacts 不写磁盘\n if (opts.dryRun) {\n try {\n return await this.computeDryRun(plugin);\n } catch (err) {\n return this.buildFailedResult(err);\n }\n }\n\n // ---- 真实写入(带回滚) ----\n\n // Rollback 状态:capture 所有 JSON 的 pre-install 快照 + cache dir 是否新建\n // + 若 pluginVersionDir 预先存在(force 重装场景),先整体 rename 到 backup\n // 路径,失败时原样复位——避免\"部分覆盖\"状态\n //\n // captureRollback 本身放 try/catch 内——若 JSON 损坏等预期错误抛出,按\n // failed 返回;captureRollback 内部把 rename 放在\"所有可能抛错操作之后\",\n // 因此 rename 发生后就不会再抛错\n let rollback: RollbackSnapshot;\n try {\n rollback = await this.captureRollback(ref, version);\n } catch (err) {\n return this.buildFailedResult(err);\n }\n\n try {\n // 1. 复制 capability 目录 + 生成 plugin.json\n const writtenFiles: string[] = [];\n await mkdir(pluginVersionDir, { recursive: true });\n writtenFiles.push(pluginVersionDir);\n\n // 复制每个 capability 子目录(源目录若不存在则跳过)\n // copyDirTracked 返回本次写入的文件绝对路径清单,累加到 writtenFiles\n for (const cap of CAPABILITY_DIRS) {\n const srcDir = resolve(plugin.sourceDir, cap);\n if (!(await dirExists(srcDir))) continue;\n const dstDir = this.paths.capabilityDir(ref, version, cap);\n await mkdir(dstDir, { recursive: true });\n writtenFiles.push(dstDir);\n const copied = await copyDirTracked(srcDir, dstDir);\n writtenFiles.push(...copied);\n }\n\n // 生成 plugin.json(内嵌 mcpServers)\n const manifestPath = this.paths.pluginManifestPath(ref, version);\n const normalizedMcp = normalizeClaudeMcpServers(\n ref,\n plugin.manifest,\n this.rushBinaryResolver\n );\n const pluginJsonContent = buildPluginJson(plugin, normalizedMcp);\n await writeJsonFile(manifestPath, pluginJsonContent);\n writtenFiles.push(manifestPath);\n\n const mcpKeys = normalizedMcp ? Object.keys(normalizedMcp) : [];\n\n // 1.5. 布置 `~/.claude/plugins/marketplaces/<mkt>/`(bug #4 修复):\n // Claude Code 期望 `known_marketplaces.json.installLocation` 指向\n // 完整 marketplace repo 目录。我们从 `marketplaceSource.rootDir`\n // symlink 到 `plugins/marketplaces/<mkt>/`;symlink 失败 fallback copy。\n //\n // 幂等:若目标已是指向相同 rootDir 的 symlink → no-op;若是不同\n // rootDir 的 symlink / 目录 → 冲突不覆盖(用户自装的 marketplace 不碰)。\n const marketplaceDirResult = await this.ensureMarketplaceDir(\n ref.marketplace\n );\n if (marketplaceDirResult.written) {\n writtenFiles.push(marketplaceDirResult.path);\n }\n\n // 2. 更新 known_marketplaces.json(首次见该 marketplace 时写入)\n const marketplaceEntryWritten = await this.upsertKnownMarketplace(ref);\n if (marketplaceEntryWritten) {\n writtenFiles.push(this.paths.knownMarketplacesJson);\n }\n\n // 3. 更新 installed_plugins.json(添加或覆盖 user scope 条目)\n const installedPluginsChanged = await this.upsertInstalledPlugin(\n ref,\n version,\n pluginVersionDir\n );\n if (installedPluginsChanged) {\n writtenFiles.push(this.paths.installedPluginsJson);\n }\n\n // 4. 更新 settings.json.enabledPlugins[key] = true(只在真实写盘时 push)\n const settingsChanged = await this.enablePlugin(key);\n if (settingsChanged) {\n writtenFiles.push(this.paths.settingsJson);\n }\n\n // 成功路径:清理 backup(如有)\n await this.finalizeRollback(rollback);\n\n return {\n target: this.target,\n status: 'ok',\n included: this.computeIncluded(plugin),\n skipped: this.computeSkipped(plugin),\n artifacts: { files: writtenFiles, mcpKeys },\n };\n } catch (err) {\n // Rollback:恢复所有 JSON + 删本次新建的 cache dir + 复位 backup 目录\n await this.applyRollback(rollback).catch(() => {\n // rollback 失败——吞掉错误,原始 error 更重要\n });\n return this.buildFailedResult(err);\n }\n }\n\n /**\n * 构造 failed 结果(统一 install 内任何可预期错误的 error-return 模式)。\n */\n private buildFailedResult(err: unknown): InstallResult {\n const message = err instanceof Error ? err.message : String(err);\n return {\n target: this.target,\n status: 'failed',\n included: [],\n skipped: [],\n artifacts: { files: [], mcpKeys: [] },\n errors: [message],\n };\n }\n\n // -------------------------------------------------------------------------\n // uninstall\n // -------------------------------------------------------------------------\n\n async uninstall(\n ref: PluginRef,\n opts: UninstallOptions = {}\n ): Promise<UninstallResult> {\n // detect=false 时仍可能有历史装过的残留(比如 Claude Code 被删但 settings.json 还在)——\n // 但 `detect()=false` 意味着 `<home>/.claude/` 目录都没了,uninstall 实质 no-op\n if (!(await this.detect())) {\n return {\n target: this.target,\n status: 'ok',\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n\n const key = pluginKey(ref);\n const touchedFiles: string[] = [];\n\n // 1. 预读 installed_plugins 中本插件的条目(找 mcpKeys 用)\n const installedMcpKeys = await this.collectMcpKeysFromInstalled(ref);\n\n if (opts.dryRun) {\n // 仅返回\"本次是否会改动\"预期 artifacts——和真实 uninstall 的 touchedFiles\n // 语义一致(避免插件未安装但全局 JSON 存在时的虚报)\n const files: string[] = [];\n if (await this.installedPluginsHasEntry(ref)) {\n files.push(this.paths.installedPluginsJson);\n }\n if (await this.settingsHasEnabledKey(key)) {\n files.push(this.paths.settingsJson);\n }\n return {\n target: this.target,\n status: 'ok',\n artifacts: { files, mcpKeys: installedMcpKeys },\n };\n }\n\n // 2. 从 installed_plugins.json 删除本插件 user scope 条目(可能整个 key 没了)\n const changedInstalled = await this.removeInstalledPlugin(ref);\n if (changedInstalled) {\n touchedFiles.push(this.paths.installedPluginsJson);\n }\n\n // 3. 从 settings.json.enabledPlugins 删除 key\n const changedSettings = await this.disablePlugin(key);\n if (changedSettings) {\n touchedFiles.push(this.paths.settingsJson);\n }\n\n // 4. cache 目录**保留**(和 Claude Code 原生行为一致)\n\n // 5. 若本 plugin 是其 marketplace 下最后一个 → 清理\n // `plugins/marketplaces/<mkt>/` + `known_marketplaces.json[<mkt>]`\n // (bug #4 修复:避免 marketplace 目录越装越多)。\n //\n // 注意两点:\n // - 顺序关键:必须在 `removeInstalledPlugin` **之后**判断,因为靠\n // installed_plugins.json 做\"同 marketplace 下还有别的 plugin 吗\"的引用计数\n // - **只有真正移除了 installed_plugins 条目才触发清理**(`changedInstalled=true`)——\n // 否则\"卸载未装 plugin\"会意外清掉他人还在用的 marketplace(Sparring SHOULD-FIX)\n if (changedInstalled) {\n const marketplaceCleanup =\n await this.cleanupMarketplaceIfUnreferenced(ref);\n touchedFiles.push(...marketplaceCleanup.files);\n }\n\n return {\n target: this.target,\n status: 'ok',\n artifacts: { files: touchedFiles, mcpKeys: installedMcpKeys },\n };\n }\n\n /**\n * 若当前 plugin 是其 marketplace 下最后一个(已从 installed_plugins.json 移除后\n * 没有其他 plugin),则:\n * 1. 清理 `~/.claude/plugins/marketplaces/<mkt>/`(unlink symlink 或 rm -rf dir)\n * 2. 删 `known_marketplaces.json[<mkt>]`\n *\n * 其他 plugin 仍挂在该 marketplace → no-op(保留共享 marketplace repo)。\n *\n * **安全策略**:\n * - 只清理 rush-ai 自己装的 marketplace 目录。判定:symlink 一律删(rush-ai\n * 装的是 symlink);非 symlink 目录保留(可能是用户/Claude Code 原生命令装的)\n * - 若 `installed_plugins.json` 根本不存在(极端场景)→ 视为\"无其他引用\"仍执行清理\n */\n private async cleanupMarketplaceIfUnreferenced(\n ref: PluginRef\n ): Promise<{ files: string[] }> {\n const files: string[] = [];\n if (await this.marketplaceStillReferenced(ref.marketplace)) {\n return { files };\n }\n const mktDir = this.paths.marketplaceInstallDir(ref.marketplace);\n const kind = await inodeKind(mktDir);\n if (kind === 'symlink') {\n await unlink(mktDir);\n files.push(mktDir);\n } else if (kind === 'dir') {\n // 仅当目录里有 `.rush-marketplace-source.json`(rush-ai cache 元数据)时认定\n // 是我们 copy 来的兜底产物,可安全 rm;否则保留\n if (await pathExists(join(mktDir, '.rush-marketplace-source.json'))) {\n await rm(mktDir, { recursive: true, force: true });\n files.push(mktDir);\n }\n }\n // known_marketplaces.json[<mkt>] 清理\n const knownChanged = await this.removeKnownMarketplaceEntry(\n ref.marketplace\n );\n if (knownChanged) {\n files.push(this.paths.knownMarketplacesJson);\n }\n return { files };\n }\n\n /**\n * installed_plugins.json 里是否还有其他 plugin 挂在给定 marketplace 上。\n *\n * 调用方必须**先**从 installed_plugins.json 移除本次要卸载的 plugin——否则\n * 会把自己算作\"还有引用\"。\n */\n private async marketplaceStillReferenced(\n marketplace: string\n ): Promise<boolean> {\n if (!(await pathExists(this.paths.installedPluginsJson))) {\n return false;\n }\n const { data } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const suffix = `@${marketplace}`;\n for (const [key, entries] of Object.entries(data.plugins ?? {})) {\n if (!Array.isArray(entries) || entries.length === 0) continue;\n if (key.endsWith(suffix)) return true;\n }\n return false;\n }\n\n private async removeKnownMarketplaceEntry(\n marketplace: string\n ): Promise<boolean> {\n if (!(await pathExists(this.paths.knownMarketplacesJson))) {\n return false;\n }\n return retryOnConflict(async () => {\n const { data, mtimeMs } = await readJsonFile<KnownMarketplacesFile>(\n this.paths.knownMarketplacesJson,\n () => ({})\n );\n if (!Object.hasOwn(data, marketplace)) {\n return false;\n }\n const next: KnownMarketplacesFile = { ...data };\n delete next[marketplace];\n await writeAtomicWithMtimeCheck(\n this.paths.knownMarketplacesJson,\n next,\n mtimeMs\n );\n return true;\n });\n }\n\n /**\n * 确保 `~/.claude/plugins/marketplaces/<mkt>/` 存在并指向当前 marketplaceSource.rootDir。\n *\n * 行为:\n * 1. 没传 `marketplaceSource`(兼容模式)→ no-op,返回 `{ written: false }`\n * 2. 目标已是 symlink 且 target === rootDir → 幂等 no-op\n * 3. 目标不存在 → `symlinkRunner(rootDir, mktDir)`;失败 fallback 到 copyRecursive\n * 4. 目标是 symlink 但指向不同 target → 抛错(潜在冲突,不覆盖)\n * 5. 目标是目录 → 若含 rush-ai cache marker 且内容等价 → 幂等;否则抛错(不覆盖用户目录)\n */\n private async ensureMarketplaceDir(\n marketplaceName: string\n ): Promise<{ written: boolean; path: string }> {\n const src = this.marketplaceSource;\n if (!src) {\n // 兼容模式:旧测试 / 旧 factory 路径,不布置 marketplace 目录\n return { written: false, path: '' };\n }\n const mktDir = this.paths.marketplaceInstallDir(marketplaceName);\n await mkdir(this.paths.marketplacesRootDir, { recursive: true });\n const kind = await inodeKind(mktDir);\n if (kind === 'symlink') {\n const target = await readlink(mktDir).catch(() => null);\n // 相对 symlink 应相对 linkPath **所在目录** 解析,而非 process cwd\n // (`readlink` 返回原始字符串;resolve(target) 会按 cwd 展开)\n const resolvedTarget =\n target !== null ? resolve(dirname(mktDir), target) : null;\n if (resolvedTarget && resolvedTarget === resolve(src.rootDir)) {\n // 幂等:已是同靶 symlink\n return { written: false, path: mktDir };\n }\n throw new Error(\n `Marketplace dir '${mktDir}' already exists as a symlink to '${target}', ` +\n `refusing to replace with symlink to '${src.rootDir}'. ` +\n `Remove it manually if you intend to switch marketplace sources.`\n );\n }\n if (kind === 'dir') {\n // 可能是上一次 fallback copy 产物(用 `.rush-marketplace-source.json` marker 判定)\n const metaPath = join(mktDir, '.rush-marketplace-source.json');\n if (await pathExists(metaPath)) {\n // 校验 source 是否和当前 marketplaceSource 匹配——避免 source 切换后\n // 静默复用旧目录(Sparring round 1 MUST-FIX)\n const metaSourceRaw = await readMarketplaceCacheSource(metaPath);\n const currentSourceRaw = descriptorToSourceRaw(src.descriptor);\n if (metaSourceRaw !== null && metaSourceRaw === currentSourceRaw) {\n return { written: false, path: mktDir };\n }\n throw new Error(\n `Marketplace dir '${mktDir}' was previously installed from '${\n metaSourceRaw ?? 'unknown source'\n }', ` +\n `but current marketplaceSource is '${currentSourceRaw}'. ` +\n `Refusing to overwrite silently—remove it manually or run 'rush-ai marketplace remove <name>'.`\n );\n }\n throw new Error(\n `Marketplace dir '${mktDir}' already exists (non-symlink). ` +\n `rush-ai will not overwrite it—remove it manually if it is stale.`\n );\n }\n if (kind === 'file' || kind === 'other') {\n throw new Error(\n `Marketplace path '${mktDir}' exists but is not a directory/symlink.`\n );\n }\n // kind === 'missing':尝试 symlink,失败 fallback copy\n const linkRes = await this.symlinkRunner(src.rootDir, mktDir);\n if (linkRes.kind === 'ok') {\n return { written: true, path: mktDir };\n }\n // fallback:递归 copy\n await copyDirTracked(src.rootDir, mktDir).catch(async (err) => {\n // copy 也失败 → 清除残留 + 抛原错\n await rm(mktDir, { recursive: true, force: true }).catch(() => {});\n throw err;\n });\n return { written: true, path: mktDir };\n }\n\n // -------------------------------------------------------------------------\n // 内部:能力分类 / dry-run / 幂等\n // -------------------------------------------------------------------------\n\n /**\n * Claude Code 支持的 capability 过滤。\n * `commands` / `skills` / `rules` / `hooks` / `mcp` 全部支持——没有 skipped。\n */\n private computeIncluded(plugin: ResolvedPlugin): PluginCapability[] {\n return [...plugin.capabilities];\n }\n\n private computeSkipped(_plugin: ResolvedPlugin): PluginCapability[] {\n // Claude Code 支持所有 capability;v1 里此值永远为空\n return [];\n }\n\n private async isAlreadyInstalledAtVersion(\n ref: PluginRef,\n version: string\n ): Promise<boolean> {\n if (!(await pathExists(this.paths.installedPluginsJson))) {\n return false;\n }\n const { data } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const entries = data.plugins?.[pluginKey(ref)];\n if (!Array.isArray(entries)) return false;\n return entries.some((e) => e.scope === 'user' && e.version === version);\n }\n\n private async computeDryRun(plugin: ResolvedPlugin): Promise<InstallResult> {\n const { ref, version } = plugin;\n const pluginVersionDir = this.paths.pluginVersionDir(ref, version);\n const dryFiles: string[] = [pluginVersionDir];\n\n // 枚举每个 capability 源目录下的所有文件(用 dry-run 专用 tracker,不写磁盘)\n for (const cap of CAPABILITY_DIRS) {\n const srcDir = resolve(plugin.sourceDir, cap);\n if (!(await dirExists(srcDir))) continue;\n const dstDir = this.paths.capabilityDir(ref, version, cap);\n dryFiles.push(dstDir);\n const plannedFiles = await listDirFilesRecursive(srcDir, dstDir);\n dryFiles.push(...plannedFiles);\n }\n\n dryFiles.push(this.paths.pluginManifestPath(ref, version));\n\n // marketplace 安装目录(bug #4)——只有在 marketplaceSource 注入 + 目标不存在时\n // 才会真实写入\n if (this.marketplaceSource) {\n const mktInstallDir = this.paths.marketplaceInstallDir(ref.marketplace);\n if ((await inodeKind(mktInstallDir)) === 'missing') {\n dryFiles.push(mktInstallDir);\n }\n }\n\n // 只把真实会变化的 JSON 列进来——避免 dry-run 虚报\n if (!(await this.isKnownMarketplaceRegistered(ref.marketplace))) {\n dryFiles.push(this.paths.knownMarketplacesJson);\n }\n if (\n await this.wouldInstalledPluginsChange(ref, version, pluginVersionDir)\n ) {\n dryFiles.push(this.paths.installedPluginsJson);\n }\n if (await this.wouldSettingsChange(pluginKey(ref))) {\n dryFiles.push(this.paths.settingsJson);\n }\n\n const normalizedMcp = normalizeClaudeMcpServers(\n ref,\n plugin.manifest,\n this.rushBinaryResolver\n );\n const mcpKeys = normalizedMcp ? Object.keys(normalizedMcp) : [];\n return {\n target: this.target,\n status: 'ok',\n included: this.computeIncluded(plugin),\n skipped: this.computeSkipped(plugin),\n artifacts: { files: dryFiles, mcpKeys },\n };\n }\n\n private async isKnownMarketplaceRegistered(\n marketplace: string\n ): Promise<boolean> {\n if (!(await pathExists(this.paths.knownMarketplacesJson))) {\n return false;\n }\n const { data } = await readJsonFile<KnownMarketplacesFile>(\n this.paths.knownMarketplacesJson,\n () => ({})\n );\n return Object.hasOwn(data, marketplace);\n }\n\n private async wouldInstalledPluginsChange(\n ref: PluginRef,\n version: string,\n installPath: string\n ): Promise<boolean> {\n if (!(await pathExists(this.paths.installedPluginsJson))) {\n return true; // 不存在时肯定需要新建\n }\n const { data } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const key = pluginKey(ref);\n const entries = data.plugins?.[key] ?? [];\n const userEntry = entries.find((e) => e.scope === 'user');\n if (!userEntry) return true;\n return (\n userEntry.version !== version || userEntry.installPath !== installPath\n );\n }\n\n private async wouldSettingsChange(key: string): Promise<boolean> {\n if (!(await pathExists(this.paths.settingsJson))) {\n return true;\n }\n const { data } = await readJsonFile<SettingsFile>(\n this.paths.settingsJson,\n () => ({})\n );\n const enabled = data.enabledPlugins;\n if (!enabled || typeof enabled !== 'object' || Array.isArray(enabled)) {\n return true;\n }\n return enabled[key] !== true;\n }\n\n // -------------------------------------------------------------------------\n // 内部:rollback snapshot / apply\n // -------------------------------------------------------------------------\n\n /**\n * 预读快照 + 若 `pluginVersionDir` 已存在(force 重装或异常场景),把整个目录\n * 整体 rename 到 backup 路径——**成功时** 我们会在 `finalizeRollback()` 里把\n * backup 删掉;**失败时** 在 `applyRollback()` 里把 backup rename 回去恢复。\n *\n * 为什么先 rename 整个目录而不是复制后覆盖:\n * - rename 在 POSIX 同文件系统内**原子**;不会出现 \"部分覆盖\" 状态\n * - 复制 + 覆盖成本 O(n),rename 是 O(1)\n * - 回滚语义更简单:单一 rename 即可恢复\n *\n * **时序保证(round-2 修订)**:**先读** 3 个 JSON 快照,**再做** version dir 的\n * rename。这样 JSON 损坏(`AtomicJsonCorruptError`)抛出时,原 pluginVersionDir\n * 不会被 rename 走——install() 的 try/catch 之外保持磁盘状态不变,上层捕获\n * 异常时磁盘没有\"已 rename 但无法复位\"的悬挂状态。\n *\n * **路径类型记录(round-3 修订)**:对 marketplace / plugin / version cache 目录\n * 记录其\"预存在 inode 类型\"(dir / file / symlink / 不存在)。回滚时只清理\"本次\n * 明确新建\"的路径——若路径预先是文件或其他非目录类型(用户/测试里特殊场景),\n * 绝**不删除** 它(避免数据破坏)。\n *\n * rename 本身是 install 副作用的一部分——它发生后,install() 里的 try/catch\n * 会通过 `applyRollback()` 复位。\n */\n private async captureRollback(\n ref: PluginRef,\n version: string\n ): Promise<RollbackSnapshot> {\n const pluginVersionDir = this.paths.pluginVersionDir(ref, version);\n const preexistedVersion = await inodeKind(pluginVersionDir);\n const preexistedMarketplace = await inodeKind(\n this.paths.marketplaceCacheDir(ref.marketplace)\n );\n const preexistedPlugin = await inodeKind(this.paths.pluginCacheDir(ref));\n const preexistedMarketplaceInstall = await inodeKind(\n this.paths.marketplaceInstallDir(ref.marketplace)\n );\n\n // 1. 先读 JSON 快照(任何损坏/读错在此阶段抛出,不会破坏磁盘状态)\n const known = await this.readRawJsonForRollback(\n this.paths.knownMarketplacesJson\n );\n const installed = await this.readRawJsonForRollback(\n this.paths.installedPluginsJson\n );\n const settings = await this.readRawJsonForRollback(this.paths.settingsJson);\n\n // 2. 若 version dir 预存在(且是目录)→ 整体 rename 到 backup 路径。\n // 如果 preexistedVersion 是非 'dir' 类型(file/symlink 等特殊情况),\n // 让后续的 mkdir(pluginVersionDir) 自己抛错——回滚时绝不触碰它\n let backupVersionDir: string | null = null;\n if (preexistedVersion === 'dir') {\n backupVersionDir = `${pluginVersionDir}.rollback-${randomUUID()}`;\n await rename(pluginVersionDir, backupVersionDir);\n }\n\n return {\n ref,\n version,\n pluginVersionDir,\n preexistedVersion,\n preexistedPlugin,\n preexistedMarketplace,\n preexistedMarketplaceInstall,\n backupVersionDir,\n known,\n installed,\n settings,\n };\n }\n\n /**\n * 成功路径:清理 backup version dir(本次 install 已完全覆盖它)。\n */\n private async finalizeRollback(snap: RollbackSnapshot): Promise<void> {\n if (snap.backupVersionDir) {\n await rm(snap.backupVersionDir, { recursive: true, force: true });\n }\n }\n\n /**\n * 只清理**本次 install 明确新建**的路径——绝不删除预先存在的任意 inode(文件、\n * symlink、非预期目录)。\n *\n * 判定规则:对 marketplace / plugin / version dir,若 `preexistedX === 'missing'`\n * 才视为\"本次新建\",可以 `rm -rf`。其他任何 inode 类型(`dir` / `file` / `symlink`\n * / `other`)都保持不动,除了 `preexistedVersion === 'dir'` 的场景(走 backup rename 复位)。\n */\n private async applyRollback(snap: RollbackSnapshot): Promise<void> {\n // 1a. version dir 预先是目录(force 重装)→ 删本次写入的内容 + backup rename 回位\n if (snap.preexistedVersion === 'dir' && snap.backupVersionDir) {\n await rm(snap.pluginVersionDir, { recursive: true, force: true });\n await rename(snap.backupVersionDir, snap.pluginVersionDir);\n }\n // 1b. version dir 预先不存在 → 本次新建的,可以删\n else if (snap.preexistedVersion === 'missing') {\n await rm(snap.pluginVersionDir, { recursive: true, force: true });\n }\n // 1c. 预先是文件/symlink/其他 → 不动(install() 写入阶段会自己因 ENOTDIR 抛错)\n\n // 2. 按目录层级回滚——只清理 preexistedX==='missing' 的路径\n if (snap.preexistedPlugin === 'missing') {\n await rm(this.paths.pluginCacheDir(snap.ref), {\n recursive: true,\n force: true,\n });\n }\n if (snap.preexistedMarketplace === 'missing') {\n await rm(this.paths.marketplaceCacheDir(snap.ref.marketplace), {\n recursive: true,\n force: true,\n });\n }\n\n // 2b. 清理本次 ensureMarketplaceDir 新建的 plugins/marketplaces/<mkt>/\n // 若预先已存在(preexisted !== 'missing')→ 用户/上次 install 的产物,不动\n if (snap.preexistedMarketplaceInstall === 'missing') {\n const mktInstallDir = this.paths.marketplaceInstallDir(\n snap.ref.marketplace\n );\n const currentKind = await inodeKind(mktInstallDir);\n if (currentKind === 'symlink') {\n await unlink(mktInstallDir).catch(() => {});\n } else if (currentKind !== 'missing') {\n await rm(mktInstallDir, { recursive: true, force: true }).catch(\n () => {}\n );\n }\n }\n\n // 3. 恢复三个 JSON\n await this.restoreRawJson(this.paths.knownMarketplacesJson, snap.known);\n await this.restoreRawJson(this.paths.installedPluginsJson, snap.installed);\n await this.restoreRawJson(this.paths.settingsJson, snap.settings);\n }\n\n private async readRawJsonForRollback(\n path: string\n ): Promise<JsonRollbackState> {\n if (!(await pathExists(path))) {\n return { existed: false };\n }\n // 直接用 readJsonFile 取 parsed obj(不关心 mtime)\n const { data } = await readJsonFile<Record<string, unknown>>(\n path,\n () => ({})\n );\n return { existed: true, data };\n }\n\n private async restoreRawJson(\n path: string,\n state: JsonRollbackState\n ): Promise<void> {\n if (!state.existed) {\n // 本来不存在 → 如果本次写过就删掉\n if (await pathExists(path)) {\n await rm(path, { force: true });\n }\n return;\n }\n // 本来存在 → 恢复原内容\n if (state.data !== undefined) {\n await writeJsonFile(path, state.data);\n }\n }\n\n // -------------------------------------------------------------------------\n // 内部:3 个 JSON 的 CRUD 封装\n // -------------------------------------------------------------------------\n\n /**\n * 返回 `true` 表示本次确实写入了一条新 marketplace 条目(或覆盖了已有条目的\n * lastUpdated),`false` 表示 marketplace 已存在且无变化(不写文件)。\n *\n * 策略:如果 marketplace 已存在于 known_marketplaces → 保持不动(避免抹掉用户\n * 的 autoUpdate / source 等字段)。只在首次见到该 marketplace 时写入。\n */\n private async upsertKnownMarketplace(ref: PluginRef): Promise<boolean> {\n return retryOnConflict(async () => {\n const { data, mtimeMs } = await readJsonFile<KnownMarketplacesFile>(\n this.paths.knownMarketplacesJson,\n () => ({})\n );\n if (Object.hasOwn(data, ref.marketplace)) {\n return false;\n }\n const next: KnownMarketplacesFile = {\n ...data,\n [ref.marketplace]: this.buildKnownMarketplaceEntry(ref.marketplace),\n };\n await writeAtomicWithMtimeCheck(\n this.paths.knownMarketplacesJson,\n next,\n mtimeMs\n );\n return true;\n });\n }\n\n /**\n * 构造 `known_marketplaces.json[<mkt>]` 条目。\n *\n * `installLocation` 必须指向 `plugins/marketplaces/<mkt>/`(不是 `cache/`——\n * bug #4 修复)。`source` 字段按 marketplaceSource.descriptor 映射:\n * - directory → `{ source: 'directory', path: <descriptor.path> }`\n * - github → `{ source: 'github', repo: '<owner>/<repo>' }`\n * - git → `{ source: 'git', url: '<url>' }`\n *\n * 兼容模式(`marketplaceSource` 未注入):直接用 `installLocation`(即\n * `plugins/marketplaces/<mkt>/`)充当 directory path——老调用方本来就没提供\n * source 来源,此 fallback 保持 known_marketplaces schema 合法性,`installLocation`\n * 字段仍指向新路径。\n */\n private buildKnownMarketplaceEntry(\n marketplace: string\n ): KnownMarketplaceEntry {\n const installLocation = this.paths.marketplaceInstallDir(marketplace);\n const src = this.marketplaceSource;\n const source: KnownMarketplaceEntry['source'] = (() => {\n if (!src) {\n // 兼容模式:旧测试路径——fallback 用 plugins/marketplaces/<mkt> 作 path\n return { source: 'directory', path: installLocation };\n }\n const d = src.descriptor;\n switch (d.kind) {\n case 'directory':\n return { source: 'directory', path: d.path };\n case 'github':\n return { source: 'github', repo: d.repo };\n case 'git':\n return { source: 'git', url: d.url };\n }\n })();\n return {\n source,\n installLocation,\n lastUpdated: this.now(),\n autoUpdate: false,\n };\n }\n\n /**\n * 返回 `true` 表示本次真实写入了 installed_plugins.json;\n * 返回 `false` 表示内容未变化(幂等 no-op,不写盘)。\n *\n * 幂等判定:同 key 下已有 user scope 条目 + version/installPath 完全一致 →\n * 不写盘(但 lastUpdated 仍可以省略,不算\"内容变化\")。这保证了 `force: true`\n * 重装仍会触发写盘(因为 lastUpdated 会重算),而普通重复装则 no-op。\n *\n * 注:幂等路径在调用方(`install()`)已经由 `isAlreadyInstalledAtVersion` 短路,\n * 所以此处多一道\"字段真实变化\"判定,主要防范未来的边界——现在大多数命中路径\n * 都会写盘,实际 `false` 返回较罕见。\n */\n private async upsertInstalledPlugin(\n ref: PluginRef,\n version: string,\n installPath: string\n ): Promise<boolean> {\n return retryOnConflict(async () => {\n const { data, mtimeMs } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const key = pluginKey(ref);\n const now = this.now();\n const existingEntries = data.plugins?.[key] ?? [];\n const userIdx = existingEntries.findIndex((e) => e.scope === 'user');\n const existingUserEntry =\n userIdx >= 0 ? existingEntries[userIdx] : undefined;\n const updatedEntry: InstalledPluginScopeEntry = {\n scope: 'user',\n installPath,\n version,\n installedAt: existingUserEntry?.installedAt ?? now,\n lastUpdated: now,\n };\n // 字段实质变化判定(忽略 lastUpdated——时间戳总在变)\n const fieldsUnchanged =\n existingUserEntry !== undefined &&\n existingUserEntry.scope === updatedEntry.scope &&\n existingUserEntry.installPath === updatedEntry.installPath &&\n existingUserEntry.version === updatedEntry.version;\n if (fieldsUnchanged) {\n return false;\n }\n const nextEntries =\n userIdx >= 0\n ? [\n ...existingEntries.slice(0, userIdx),\n updatedEntry,\n ...existingEntries.slice(userIdx + 1),\n ]\n : [...existingEntries, updatedEntry];\n const next: InstalledPluginsFile = {\n version: 2,\n plugins: {\n ...(data.plugins ?? {}),\n [key]: nextEntries,\n },\n };\n await writeAtomicWithMtimeCheck(\n this.paths.installedPluginsJson,\n next,\n mtimeMs\n );\n return true;\n });\n }\n\n /**\n * 返回 `true` 表示本次真实写入了 settings.json;`false` 表示已启用(幂等 no-op)。\n */\n private async enablePlugin(key: string): Promise<boolean> {\n return retryOnConflict(async () => {\n const { data, mtimeMs } = await readJsonFile<SettingsFile>(\n this.paths.settingsJson,\n () => ({})\n );\n const existingEnabled = data.enabledPlugins;\n if (\n existingEnabled !== undefined &&\n (typeof existingEnabled !== 'object' ||\n Array.isArray(existingEnabled) ||\n existingEnabled === null)\n ) {\n throw new Error(\n `${this.paths.settingsJson}: enabledPlugins 字段类型错误(期望 object)`\n );\n }\n if (\n existingEnabled &&\n Object.hasOwn(existingEnabled, key) &&\n existingEnabled[key] === true\n ) {\n // 幂等:已启用不动\n return false;\n }\n const next: SettingsFile = {\n ...data,\n enabledPlugins: {\n ...(existingEnabled ?? {}),\n [key]: true,\n },\n };\n await writeAtomicWithMtimeCheck(this.paths.settingsJson, next, mtimeMs);\n return true;\n });\n }\n\n private async removeInstalledPlugin(ref: PluginRef): Promise<boolean> {\n if (!(await pathExists(this.paths.installedPluginsJson))) {\n return false;\n }\n return retryOnConflict(async () => {\n const { data, mtimeMs } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const key = pluginKey(ref);\n const existingEntries = data.plugins?.[key];\n if (!Array.isArray(existingEntries) || existingEntries.length === 0) {\n return false;\n }\n const remaining = existingEntries.filter((e) => e.scope !== 'user');\n const nextPlugins = { ...(data.plugins ?? {}) };\n if (remaining.length === 0) {\n delete nextPlugins[key];\n } else {\n nextPlugins[key] = remaining;\n }\n const next: InstalledPluginsFile = { version: 2, plugins: nextPlugins };\n await writeAtomicWithMtimeCheck(\n this.paths.installedPluginsJson,\n next,\n mtimeMs\n );\n return true;\n });\n }\n\n private async disablePlugin(key: string): Promise<boolean> {\n if (!(await pathExists(this.paths.settingsJson))) {\n return false;\n }\n return retryOnConflict(async () => {\n const { data, mtimeMs } = await readJsonFile<SettingsFile>(\n this.paths.settingsJson,\n () => ({})\n );\n const enabled = data.enabledPlugins;\n if (\n !enabled ||\n typeof enabled !== 'object' ||\n Array.isArray(enabled) ||\n !Object.hasOwn(enabled, key)\n ) {\n return false;\n }\n const nextEnabled = { ...enabled };\n delete nextEnabled[key];\n const next: SettingsFile = { ...data };\n if (Object.keys(nextEnabled).length === 0) {\n delete next.enabledPlugins;\n } else {\n next.enabledPlugins = nextEnabled;\n }\n await writeAtomicWithMtimeCheck(this.paths.settingsJson, next, mtimeMs);\n return true;\n });\n }\n\n /** installed_plugins.json 是否有本 plugin 的 user scope 条目(uninstall dry-run 用) */\n private async installedPluginsHasEntry(ref: PluginRef): Promise<boolean> {\n if (!(await pathExists(this.paths.installedPluginsJson))) {\n return false;\n }\n const { data } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const entries = data.plugins?.[pluginKey(ref)];\n if (!Array.isArray(entries) || entries.length === 0) return false;\n return entries.some((e) => e.scope === 'user');\n }\n\n /** settings.json.enabledPlugins 是否有本 key(uninstall dry-run 用) */\n private async settingsHasEnabledKey(key: string): Promise<boolean> {\n if (!(await pathExists(this.paths.settingsJson))) {\n return false;\n }\n const { data } = await readJsonFile<SettingsFile>(\n this.paths.settingsJson,\n () => ({})\n );\n const enabled = data.enabledPlugins;\n if (!enabled || typeof enabled !== 'object' || Array.isArray(enabled)) {\n return false;\n }\n return Object.hasOwn(enabled, key);\n }\n\n private async collectMcpKeysFromInstalled(ref: PluginRef): Promise<string[]> {\n // 从 cache 里的 plugin.json 读回 mcpKeys。不依赖 registry——保持 Installer 自治。\n const manifestPath = await this.findLatestManifestPath(ref);\n if (!manifestPath) return [];\n const { data } = await readJsonFile<Record<string, unknown>>(\n manifestPath,\n () => ({})\n );\n const servers = data.mcpServers;\n if (!servers || typeof servers !== 'object' || Array.isArray(servers)) {\n return [];\n }\n return Object.keys(servers as Record<string, unknown>);\n }\n\n private async findLatestManifestPath(ref: PluginRef): Promise<string | null> {\n if (!(await pathExists(this.paths.installedPluginsJson))) {\n return null;\n }\n const { data } = await readJsonFile<InstalledPluginsFile>(\n this.paths.installedPluginsJson,\n () => ({ version: 2, plugins: {} })\n );\n const entries = data.plugins?.[pluginKey(ref)];\n if (!Array.isArray(entries) || entries.length === 0) return null;\n const userEntry = entries.find((e) => e.scope === 'user') ?? entries[0];\n if (!userEntry) return null;\n const manifestPath = join(\n userEntry.installPath,\n '.claude-plugin',\n 'plugin.json'\n );\n if (await pathExists(manifestPath)) {\n return manifestPath;\n }\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// 顶层 helpers(不暴露到 barrel)\n// ---------------------------------------------------------------------------\n\n/** 路径预先存在的 inode 类型,用于区分\"本次新建 vs 用户已有\"。 */\ntype InodeKind = 'dir' | 'file' | 'symlink' | 'other' | 'missing';\n\ninterface RollbackSnapshot {\n ref: PluginRef;\n version: string;\n pluginVersionDir: string;\n /** pluginVersionDir 预先存在的 inode 类型 */\n preexistedVersion: InodeKind;\n /** pluginCacheDir(`cache/<mkt>/<plugin>/`)预先存在的 inode 类型 */\n preexistedPlugin: InodeKind;\n /** marketplaceCacheDir(`cache/<mkt>/`)预先存在的 inode 类型 */\n preexistedMarketplace: InodeKind;\n /**\n * `plugins/marketplaces/<mkt>/`(bug #4)预先存在的 inode 类型。\n * 回滚时仅当 preexisted === 'missing' 才清理(symlink `unlink`,dir `rm -rf`)\n */\n preexistedMarketplaceInstall: InodeKind;\n /**\n * 若 `pluginVersionDir` 预存在(force 重装),install 前会把它 rename 到此路径;\n * 成功时 `finalizeRollback()` 删掉;失败时 `applyRollback()` rename 回原位。\n * 为 null 表示本次 install 前 version dir 不存在或不是 dir,不需备份。\n */\n backupVersionDir: string | null;\n known: JsonRollbackState;\n installed: JsonRollbackState;\n settings: JsonRollbackState;\n}\n\ninterface JsonRollbackState {\n existed: boolean;\n data?: Record<string, unknown>;\n}\n\n/**\n * `<name>@<marketplace>` → PluginRef(与 `pluginRefToKey` 反向,但容错返回 null)\n */\nfunction parsePluginKey(key: string): PluginRef | null {\n const at = key.indexOf('@');\n if (at <= 0 || at === key.length - 1) return null;\n return { name: key.slice(0, at), marketplace: key.slice(at + 1) };\n}\n\n/**\n * 构造要写入 `.claude-plugin/plugin.json` 的最终 JSON。\n *\n * 来源:\n * - `plugin.manifest` 整体透传(name / version / description / author / keywords /\n * homepage / repository / license / interface 等)\n * - `mcpServers`:用规范化过的对象覆盖(若传入 `undefined` 则 **drop** 原 manifest\n * 的 mcpServers 字段——Claude Code 不认 string 形态,也不接受 undefined/null)\n *\n * 规则:不落 `skills` 字段(Claude Code 约定自动扫描 `skills/` 目录,写入反而冗余;\n * spec §1.5:`commands/skills/rules/hooks` 目录不在 plugin.json 里声明)\n */\nfunction buildPluginJson(\n plugin: ResolvedPlugin,\n mcpServers: Record<string, unknown> | undefined\n): Record<string, unknown> {\n const { manifest } = plugin;\n const {\n mcpServers: _origMcp,\n skills: _skills, // Claude Code 不用此字段,drop\n ...rest\n } = manifest;\n const out: Record<string, unknown> = { ...rest };\n if (mcpServers && Object.keys(mcpServers).length > 0) {\n out.mcpServers = mcpServers;\n }\n return out;\n}\n\n/**\n * 读 `.rush-marketplace-source.json` 里记录的 `sourceRaw`。\n *\n * 用于 `ensureMarketplaceDir` 幂等校验——若目录是我们上次 fallback copy 来的,\n * 且 source 与当前 marketplaceSource 一致,可以安全复用;否则视为冲突。\n *\n * 损坏/字段缺失 → 返回 null(让上层按\"无法匹配\"处理,抛冲突错)。\n */\nasync function readMarketplaceCacheSource(\n metaPath: string\n): Promise<string | null> {\n try {\n const raw = await readFile(metaPath, 'utf8');\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n const sourceRaw = (parsed as Record<string, unknown>).sourceRaw;\n if (typeof sourceRaw === 'string') return sourceRaw;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * 把 `ClaudeCodeMarketplaceSource.descriptor` 转成与 `.rush-marketplace-source.json`\n * 里 `sourceRaw` 字段相同的格式——用于幂等校验。\n *\n * 和 `marketplaces/source.ts` 的 stringify 保持一致:\n * - directory → `directory:<path>`\n * - github → `github:<owner/repo>`\n * - git → `git:<url>`\n */\nfunction descriptorToSourceRaw(\n descriptor: ClaudeCodeMarketplaceSource['descriptor']\n): string {\n switch (descriptor.kind) {\n case 'directory':\n return `directory:${descriptor.path}`;\n case 'github':\n return descriptor.ref\n ? `github:${descriptor.repo}#${descriptor.ref}`\n : `github:${descriptor.repo}`;\n case 'git':\n return `git:${descriptor.url}`;\n }\n}\n\n/**\n * 默认 symlink 创建器。\n *\n * 能 symlink 就 symlink;碰到\"文件系统不支持/跨挂载点/权限\"类 errno 返回\n * `{ kind: 'fallback', reason }` 让上层走 copy;其他错误向上抛。\n *\n * 判定依据:\n * - `EXDEV`:跨文件系统挂载点\n * - `EPERM`:Windows 某些用户无权创建 symlink\n * - `ENOTSUP` / `EOPNOTSUPP`:文件系统不支持 symlink(NTFS/FAT 等)\n * - `EACCES`:无写权限(让上层 fallback 试一次 copy;若 copy 也不行会自然抛错)\n *\n * 生产:用 `fs.symlink`(默认 type);测试:可注入 stub。\n */\nasync function defaultSymlinkRunner(\n target: string,\n linkPath: string\n): Promise<{ kind: 'ok' } | { kind: 'fallback'; reason: string }> {\n try {\n await symlink(target, linkPath);\n return { kind: 'ok' };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (\n code === 'EXDEV' ||\n code === 'EPERM' ||\n code === 'ENOTSUP' ||\n code === 'EOPNOTSUPP' ||\n code === 'EACCES'\n ) {\n return { kind: 'fallback', reason: code };\n }\n throw err;\n }\n}\n\nasync function dirExists(p: string): Promise<boolean> {\n try {\n const s = await stat(p);\n return s.isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * 检查路径预先的 inode 类型——用于 rollback 判定\"本次新建 vs 用户已有\"。\n *\n * 用 `lstat` 不跟踪 symlink(要区分 symlink 和它指向的目标)。\n */\nasync function inodeKind(p: string): Promise<InodeKind> {\n try {\n const s = await lstat(p);\n if (s.isDirectory()) return 'dir';\n if (s.isSymbolicLink()) return 'symlink';\n if (s.isFile()) return 'file';\n return 'other';\n } catch {\n return 'missing';\n }\n}\n\n/**\n * 预演模式:列出**如果递归复制** srcDir 到 dstDir 会写入哪些文件+目录(相对 dstDir 路径已经转绝对)。\n *\n * 供 dry-run 用——不读写磁盘之外的文件,只读 srcDir 枚举结构。\n */\nasync function listDirFilesRecursive(\n srcDir: string,\n dstDir: string\n): Promise<string[]> {\n const planned: string[] = [];\n const entries = await readdir(srcDir, { withFileTypes: true });\n for (const entry of entries) {\n const srcPath = join(srcDir, entry.name);\n const dstPath = join(dstDir, entry.name);\n if (entry.isDirectory()) {\n planned.push(dstPath);\n const sub = await listDirFilesRecursive(srcPath, dstPath);\n planned.push(...sub);\n } else if (entry.isFile()) {\n planned.push(dstPath);\n }\n }\n return planned;\n}\n\n/**\n * 递归复制目录,返回**本次实际写入的文件绝对路径清单**。\n *\n * 返回清单用于 `InstallResult.artifacts.files`——让 CLI/registry 能精确知道\"装\n * 了哪些文件\",不止是\"哪些目录\"。\n *\n * Node 22 有 `cp` recursive 选项,但该 API 在某些版本有 behaviors 不一致问题——\n * 这里手写保守实现(只用 readdir + copyFile + mkdir)。\n *\n * 不处理 symlink(源目录里的 symlink 按目标文件复制,不保留 link 语义)。\n */\nasync function copyDirTracked(\n srcDir: string,\n dstDir: string\n): Promise<string[]> {\n const written: string[] = [];\n const entries = await readdir(srcDir, { withFileTypes: true });\n for (const entry of entries) {\n const srcPath = join(srcDir, entry.name);\n const dstPath = join(dstDir, entry.name);\n if (entry.isDirectory()) {\n await mkdir(dstPath, { recursive: true });\n written.push(dstPath);\n const subWritten = await copyDirTracked(srcPath, dstPath);\n written.push(...subWritten);\n } else if (entry.isFile()) {\n await mkdir(dirname(dstPath), { recursive: true });\n await copyFile(srcPath, dstPath);\n written.push(dstPath);\n }\n // symlink / fifo / socket 忽略\n }\n return written;\n}\n\n/**\n * 写 JSON 时做 mtime 比对;变化了抛 `AtomicJsonConflictError`。\n *\n * 注意:`writeJsonFile` 本身是原子的(write .tmp → rename);`writeAtomicWithMtimeCheck`\n * 在 `writeJsonFile` 之前做 mtime gate,两者组合等价于 \"CAS-like\" 保护。\n */\nasync function writeAtomicWithMtimeCheck(\n filePath: string,\n data: unknown,\n expectedMtimeMs: number | null\n): Promise<void> {\n if (expectedMtimeMs !== null) {\n let currentMtime: number | null = null;\n try {\n const s = await stat(filePath);\n currentMtime = s.mtimeMs;\n } catch {\n currentMtime = null;\n }\n if (currentMtime !== null && currentMtime !== expectedMtimeMs) {\n throw new AtomicJsonConflictError(filePath);\n }\n }\n await writeJsonFile(filePath, data);\n}\n\n/**\n * 遇到 `AtomicJsonConflictError` 重试一次。两次都冲突 → 抛给上层。\n *\n * 简化策略(spec §3.3 靠拢):只 retry 1 次,避免隐藏活锁。\n */\nasync function retryOnConflict<T>(fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (err) {\n if (err instanceof AtomicJsonConflictError) {\n return await fn();\n }\n throw err;\n }\n}\n","/**\n * MCP command 规范化(task-6 产物)。\n *\n * 仅对 rush-ai 自己的插件(`rush@rush-marketplace`)做 MCP command 绝对路径规范化;\n * 其他 plugin 透传(见 plan §7.1 / spec §1.5)。\n *\n * 说明:resolver 层(`plugins/resolver.ts` 的 `normalizeRushMcpCommand`)已经做过\n * 同样的规范化。本文件提供一个 Installer 侧的**兜底**:如果 resolver 没解析到绝对路径\n * (例如 rushAiBinaryResolver 返回 undefined,resolver 选择不改),Installer 写\n * plugin.json 前再尝试一次——用 `which rush-ai`(PATH 解析)。**不 fallback 到\n * `process.argv[0]`**(那通常是 `node`,会写错误 command)。\n *\n * 为什么在两个层都做:\n * - Resolver 层:给各家 Installer 一个统一的起点(避免 Claude Code / Codex / Cursor\n * Installer 各自重复实现)\n * - Installer 层:resolver 的兜底策略有意保持宽松(失败就透传),Installer 层可以\n * 更激进(Claude Code 的 plugin.json 要求绝对路径更严)\n *\n * 注意:对 rush plugin 且 resolver + 本兜底都拿不到绝对路径时,我们仍然写入 plugin.json\n * 的原始值——不抛错、不阻塞 install。Claude Code 运行时会自己 fail(下游问题),但\n * Installer 保持 \"能装就装\" 的语义。\n */\n\nimport { execFileSync } from 'node:child_process';\nimport { isAbsolute } from 'node:path';\nimport type { McpServerConfig, PluginManifest, PluginRef } from '../types.js';\n\n/** rush 自身 plugin 的 identifier(与 resolver 层保持一致) */\nexport const RUSH_AI_PLUGIN_NAME = 'rush' as const;\nexport const RUSH_AI_MARKETPLACE_NAME = 'rush-marketplace' as const;\nexport const RUSH_MCP_SERVER_KEY = 'rush' as const;\n\n/**\n * 决定是否应对该 plugin 做 MCP command 规范化。\n *\n * 约束条件(plan §7.1):\n * - `ref.name === 'rush'`\n * - `ref.marketplace === 'rush-marketplace'`\n *\n * 第三方 marketplace 的 rush plugin(同名但不同 marketplace)**不**命中——\n * 只有我们自己发布的插件才保证 `rush-ai` binary 可解析到绝对路径。\n */\nexport function isRushOwnPlugin(ref: PluginRef): boolean {\n return (\n ref.name === RUSH_AI_PLUGIN_NAME &&\n ref.marketplace === RUSH_AI_MARKETPLACE_NAME\n );\n}\n\n/**\n * 规范化后的 MCP servers 对象——返回 Claude Code plugin.json 顶层所需的 `mcpServers`\n * 结构(`Record<string, McpServerConfig>`)。\n *\n * - `manifest.mcpServers` 是 `string`(Codex 外部文件引用)→ 返回 `undefined`\n * (Claude Code 不应该写入 string 形态,Installer 调用方自行处理)\n * - 非 rush 插件 → 原样返回\n * - rush 插件:仅对 `rush` server key 做 command 规范化(其他 key 保持)\n *\n * 规范化策略(rush 插件且 command 非绝对路径时):\n * 1. 调用者注入的 `resolver`(通常是 `whichRushAiBinary`)\n * 2. 返回值是绝对路径 → 用它\n * 3. 否则保留原始 command(不抛错,让 Claude Code 运行时报)\n */\nexport function normalizeClaudeMcpServers(\n ref: PluginRef,\n manifest: PluginManifest,\n resolver: () => string | undefined = defaultRushBinaryResolver\n): Record<string, McpServerConfig> | undefined {\n const servers = manifest.mcpServers;\n if (servers === undefined || servers === null) return undefined;\n if (typeof servers === 'string') {\n // Codex 外部文件引用 — Claude Code 不认,Installer 跳过内嵌 MCP。\n // 不抛错(可能是多 IDE 通用的 plugin.json,其他字段仍然有价值)\n return undefined;\n }\n if (!isRushOwnPlugin(ref)) {\n // 第三方 plugin:透传作者写的值\n return { ...servers };\n }\n\n // rush own plugin —— 仅规范化 `rush` server key\n const rushServer = servers[RUSH_MCP_SERVER_KEY];\n if (!rushServer) {\n return { ...servers };\n }\n const currentCommand = rushServer.command;\n if (typeof currentCommand === 'string' && isAbsolute(currentCommand)) {\n // 已经是绝对路径,不动\n return { ...servers };\n }\n\n const resolved = resolver();\n if (!resolved || !isAbsolute(resolved)) {\n // 兜底失败:保留原值,不阻塞 install\n return { ...servers };\n }\n\n return {\n ...servers,\n [RUSH_MCP_SERVER_KEY]: { ...rushServer, command: resolved },\n };\n}\n\n/**\n * 默认 rush-ai binary 解析:`which rush-ai`(PATH 解析)。\n *\n * 作为 Installer 层兜底——resolver 层已用 `process.argv[1]` 试过一次;本函数\n * 用更稳的 PATH 查找逻辑。\n *\n * **不 fallback 到 `process.argv[0]`**(那通常是 `node`,会把 rush MCP command\n * 规范化成 node 路径,运行时语义错误)——宁可返回 `undefined`(保留原 command),\n * 也不冒险写入错误绝对路径。\n *\n * 失败返回 `undefined`,调用方保留原值。\n */\nexport function defaultRushBinaryResolver(): string | undefined {\n try {\n const result = execFileSync('which', ['rush-ai'], {\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n if (result.length > 0 && isAbsolute(result)) {\n return result;\n }\n } catch {\n // `which` 失败 —— rush-ai 未在 PATH 上,放弃兜底\n }\n return undefined;\n}\n","/**\n * Claude Code Installer 的路径 helper(task-6 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §1.1 目录布局。\n *\n * 所有路径都基于注入的 `home` 目录构造——生产代码传 `os.homedir()`,测试必须\n * 走 `os.tmpdir()` 下的临时目录(**禁止**写用户真实 `~/.claude/`)。\n *\n * 目录结构:\n * ```\n * <home>/.claude/\n * ├── settings.json ← enabledPlugins 合并\n * └── plugins/\n * ├── known_marketplaces.json ← marketplace 注册表\n * ├── installed_plugins.json ← V2 schema\n * ├── marketplaces/ ← marketplace 完整 repo(含 .claude-plugin/marketplace.json)\n * │ └── <mkt-name>/ (symlink/copy,指向 ~/.rush/marketplaces/<mkt>)\n * └── cache/\n * └── <mkt-name>/\n * └── <plugin-name>/\n * └── <version>/\n * ├── .claude-plugin/plugin.json\n * ├── commands/ skills/ rules/ hooks/ agents/ (各 capability 目录)\n * ```\n */\n\nimport { resolve } from 'node:path';\nimport type { PluginRef } from '../types.js';\n\n/** `.claude/` 目录名 */\nexport const CLAUDE_DIR = '.claude' as const;\n\n/** plugins 子目录名 */\nexport const PLUGINS_SUBDIR = 'plugins' as const;\n\n/** cache 子目录名 */\nexport const CACHE_SUBDIR = 'cache' as const;\n\n/** plugin manifest 相对 plugin version 目录的路径 */\nexport const PLUGIN_MANIFEST_RELATIVE = '.claude-plugin/plugin.json' as const;\n\n/**\n * Claude Code 原生识别的 capability 目录名。\n *\n * 安装时按顺序整体递归复制;卸载时 cache 目录**保留**(和原生行为一致)。\n * 注意:`mcp` 不在这里——MCP 通过 plugin.json 内嵌表达,不是独立目录。\n */\nexport const CAPABILITY_DIRS = [\n 'commands',\n 'skills',\n 'rules',\n 'hooks',\n 'agents',\n] as const;\n\nexport type CapabilityDirName = (typeof CAPABILITY_DIRS)[number];\n\n/**\n * 集中式路径工厂——所有 Installer 内部的磁盘写入都从这里取路径,不散落字符串。\n */\nexport class ClaudeCodePaths {\n constructor(public readonly home: string) {}\n\n /** `<home>/.claude/` */\n get claudeDir(): string {\n return resolve(this.home, CLAUDE_DIR);\n }\n\n /** `<home>/.claude/settings.json` */\n get settingsJson(): string {\n return resolve(this.claudeDir, 'settings.json');\n }\n\n /** `<home>/.claude/plugins/` */\n get pluginsDir(): string {\n return resolve(this.claudeDir, PLUGINS_SUBDIR);\n }\n\n /** `<home>/.claude/plugins/known_marketplaces.json` */\n get knownMarketplacesJson(): string {\n return resolve(this.pluginsDir, 'known_marketplaces.json');\n }\n\n /** `<home>/.claude/plugins/installed_plugins.json` */\n get installedPluginsJson(): string {\n return resolve(this.pluginsDir, 'installed_plugins.json');\n }\n\n /** `<home>/.claude/plugins/cache/` */\n get cacheDir(): string {\n return resolve(this.pluginsDir, CACHE_SUBDIR);\n }\n\n /**\n * `<home>/.claude/plugins/marketplaces/`。\n *\n * Claude Code 扫 marketplace repo(含 `.claude-plugin/marketplace.json`)的目录——\n * `known_marketplaces.json.installLocation` 必须指向此目录下的 `<mkt>/`,否则\n * Claude Code 会识别不到 plugin 条目(regression fix,bug #4)。\n */\n get marketplacesRootDir(): string {\n return resolve(this.pluginsDir, 'marketplaces');\n }\n\n /** `<home>/.claude/plugins/marketplaces/<mkt>/` */\n marketplaceInstallDir(marketplace: string): string {\n return resolve(this.marketplacesRootDir, marketplace);\n }\n\n /** `<home>/.claude/plugins/cache/<mkt>/` */\n marketplaceCacheDir(marketplace: string): string {\n return resolve(this.cacheDir, marketplace);\n }\n\n /** `<home>/.claude/plugins/cache/<mkt>/<plugin>/` */\n pluginCacheDir(ref: PluginRef): string {\n return resolve(this.marketplaceCacheDir(ref.marketplace), ref.name);\n }\n\n /** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/` */\n pluginVersionDir(ref: PluginRef, version: string): string {\n return resolve(this.pluginCacheDir(ref), version);\n }\n\n /** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/.claude-plugin/plugin.json` */\n pluginManifestPath(ref: PluginRef, version: string): string {\n return resolve(\n this.pluginVersionDir(ref, version),\n PLUGIN_MANIFEST_RELATIVE\n );\n }\n\n /** `<home>/.claude/plugins/cache/<mkt>/<plugin>/<version>/<capability>/` */\n capabilityDir(\n ref: PluginRef,\n version: string,\n capability: CapabilityDirName\n ): string {\n return resolve(this.pluginVersionDir(ref, version), capability);\n }\n}\n\n/**\n * `<plugin-name>@<marketplace>` 组合键——Claude Code installed_plugins / enabledPlugins\n * 以及 rush-ai registry 都用这个形态。\n *\n * 与 registry 里的 `pluginRefToKey` 语义一致但不直接复用(避免 cross-module\n * coupling;同时保留此处对键格式的文档)。\n */\nexport function pluginKey(ref: PluginRef): string {\n return `${ref.name}@${ref.marketplace}`;\n}\n","/**\n * Codex Installer —— `class CodexInstaller implements PluginInstaller`(task-8 产物)。\n *\n * Source of truth:\n * - `specs/plugin-schemas.md` §2(Codex 目录布局 / config.toml / plugin.json / .mcp.json)\n * - `specs/installer-interface.md` §1–§4(Installer 行为契约)\n *\n * 行为契约(严格对齐 spec §2.1):\n * - `install()` 第一行 `detect()=false` → `{status: 'skipped', ...}`\n * - 复制 plugin sourceDir 的 `skills/` 到 `<cache>/<mkt>/<plugin>/<ver>/skills/`\n * - 生成 `.codex-plugin/plugin.json`(必写 `\"skills\": \"./skills/\"`,即便 Codex\n * 实测可选——对齐 OpenAI bundled 规范和 spec §2.3 决策)\n * - 生成 `.mcp.json`(外部文件,plugin.json 引用 `\"mcpServers\": \"./.mcp.json\"`)\n * - 更新 `~/.codex/config.toml` 的 `[marketplaces.<mkt>]` + `[plugins.\"<name>@<mkt>\"]`\n * - 写前备份 `config.toml.bak.<YYYYMMDD-HHMMSS>`,`InstallResult.notes` 告知路径\n * - 写前读 mtime,写完后再 stat 确认(并发保护 —— `writeCodexConfig` 内置)\n * - 不保留注释(决策已定)\n * - Skipped capabilities: `['rules', 'hooks']`(Codex 不支持)\n * - 写入失败完整回滚(从 .bak 恢复 config.toml + 删除本次创建的 cache 目录)\n * - `InstallResult.artifacts.files` 完整列出:version dir + plugin.json + .mcp.json +\n * skills dir + config.toml + 备份路径\n * - `InstallResult.artifacts.mcpKeys` 列出 .mcp.json 里 server key\n *\n * - 删 `[plugins.\"<name>@<mkt>\"]` section\n * - 若 marketplace 下**没有其他 plugin** 使用 → 也删 `[marketplaces.<mkt>]`;否则保留\n * - cache 目录保留(和原生行为一致 + Claude Code Installer 也是这个策略)\n */\n\nimport { type Dirent, constants as fsConstants } from 'node:fs';\nimport {\n access,\n cp,\n mkdir,\n mkdtemp,\n readdir,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport {\n basename,\n dirname,\n extname,\n relative as pathRelative,\n resolve,\n} from 'node:path';\nimport type {\n InstalledPlugin,\n InstallOptions,\n InstallResult,\n PluginCapability,\n PluginInstaller,\n PluginManifest,\n PluginRef,\n ResolvedPlugin,\n UninstallOptions,\n UninstallResult,\n} from '../types.js';\nimport { buildCodexMcpContent, listMcpKeys, serializeMcpJson } from './mcp.js';\nimport {\n codexConfigBackupPath,\n codexConfigTomlPath,\n codexHomeDir,\n codexMarketplaceDir,\n codexMarketplaceManifestPath,\n codexMarketplacePluginDir,\n codexPluginManifestPath,\n codexPluginMcpPath,\n codexPluginSkillsDir,\n codexPluginVersionDir,\n pluginSectionKey,\n} from './paths.js';\nimport {\n backupCodexConfig,\n marketplaceHasOtherPlugins,\n readCodexConfig,\n removeMarketplaceSection,\n removePluginEntry,\n restoreCodexConfigFromBackup,\n setMarketplaceSection,\n setPluginEntry,\n writeCodexConfig,\n} from './toml.js';\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface CodexInstallerOptions {\n /**\n * HOME 目录注入(仅测试用)。生产传 `undefined`,默认 `os.homedir()`。\n *\n * **测试铁律**:所有单测必须传 `os.tmpdir()` 下的临时目录。\n */\n readonly home?: string;\n\n /**\n * rush-ai binary 绝对路径解析器(仅 rush@rush-marketplace 的 rush MCP server 使用)。\n *\n * 生产默认 `() => process.argv[1]`(rush-ai CLI 入口),失败时保持 manifest\n * 原 command 不改(让下游运行时报错,而非 install 阶段挂)。\n *\n * 测试里注入固定字符串最稳定。\n */\n readonly rushAiBinaryResolver?: () => string | undefined;\n\n /**\n * 当前时间注入器(仅测试用),默认 `() => new Date()`。\n *\n * 用于 marketplace `last_updated` + 备份路径时间戳,便于测试断言稳定。\n */\n readonly now?: () => Date;\n\n /**\n * marketplace source 配置(用于写 `[marketplaces.<mkt>]` section)。\n *\n * Codex Installer 不知道 plugin 来自哪个 source,必须由上层(CLI 或测试)\n * 显式传入。\n *\n * 字段:\n * - `sourceType`:`\"local\"` / `\"github\"` / `\"git\"`\n * - `source`:字符串(绝对路径 or github repo or git URL)\n *\n * 可选:上层不传时 `install` 会默认写 `source_type = \"local\"` +\n * `source = plugin.sourceDir` 的合理 fallback(对齐 phase0-spike 实测行为:\n * `codex plugin marketplace add /abs/path` 就是 source_type=local)。\n */\n readonly marketplaceSource?: {\n readonly sourceType: 'local' | 'github' | 'git';\n readonly source: string;\n };\n}\n\n// Codex 不支持的能力 —— Installer 层恒定跳过。\n// Claude `commands/*.md` 是 flat Markdown skills,安装到 Codex 时转为 skills。\nconst CODEX_UNSUPPORTED: PluginCapability[] = ['rules', 'hooks'];\n\n/**\n * Installer 自抛的预期错误基类。所有从 install()/uninstall() 预期返回\n * `status: 'failed'` 而非上抛的场景都应抛该类(或其子类)。\n *\n * `isExpectedInstallError` 会识别本基类 + 常见 FS 错误(有 errno code 的)+\n * 命名以 `CodexConfigToml` 开头的错误。\n */\nclass CodexInstallError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CodexInstallError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Installer 实现\n// ---------------------------------------------------------------------------\n\nexport class CodexInstaller implements PluginInstaller {\n readonly target = 'codex' as const;\n\n private readonly home: string;\n private readonly rushAiBinaryResolver: () => string | undefined;\n private readonly now: () => Date;\n private readonly marketplaceSource?: CodexInstallerOptions['marketplaceSource'];\n\n constructor(opts: CodexInstallerOptions = {}) {\n this.home = opts.home ?? homedir();\n this.rushAiBinaryResolver =\n opts.rushAiBinaryResolver ?? defaultRushAiBinaryResolver;\n this.now = opts.now ?? (() => new Date());\n this.marketplaceSource = opts.marketplaceSource;\n }\n\n // -------------------------------------------------------------------------\n // detect / isInstalled / list\n // -------------------------------------------------------------------------\n\n async detect(): Promise<boolean> {\n return isDir(codexHomeDir(this.home));\n }\n\n async isInstalled(ref: PluginRef): Promise<boolean> {\n const cfgPath = codexConfigTomlPath(this.home);\n if (!(await pathExists(cfgPath))) return false;\n const { data } = await readCodexConfig(cfgPath);\n const plugins = data.plugins as Record<string, unknown> | undefined;\n if (!plugins || typeof plugins !== 'object') return false;\n const key = pluginSectionKey(ref);\n const entry = plugins[key];\n if (!entry || typeof entry !== 'object') return false;\n // 仅当 enabled=true 视为已装(spec §2.2 约定 rush-ai 装 = 启用)\n return (entry as { enabled?: unknown }).enabled === true;\n }\n\n async list(): Promise<InstalledPlugin[]> {\n const cfgPath = codexConfigTomlPath(this.home);\n if (!(await pathExists(cfgPath))) return [];\n const { data } = await readCodexConfig(cfgPath);\n const plugins = data.plugins as Record<string, unknown> | undefined;\n if (!plugins || typeof plugins !== 'object') return [];\n\n const results: InstalledPlugin[] = [];\n for (const [key, raw] of Object.entries(plugins)) {\n if (!raw || typeof raw !== 'object') continue;\n const entry = raw as { enabled?: unknown };\n if (entry.enabled !== true) continue;\n const ref = parsePluginKey(key);\n if (!ref) continue;\n\n // version + installedAt:从磁盘 cache 目录的 .codex-plugin/plugin.json 推断\n // 若读不到 → 填 fallback 值,list 依旧可用\n const detected = await detectInstalledVersion(this.home, ref);\n results.push({\n ref,\n version: detected?.version ?? 'unknown',\n installedAt: detected?.installedAt ?? new Date(0).toISOString(),\n targets: ['codex'],\n });\n }\n // 字典序稳定输出\n results.sort((a, b) => {\n const ka = `${a.ref.name}@${a.ref.marketplace}`;\n const kb = `${b.ref.name}@${b.ref.marketplace}`;\n return ka < kb ? -1 : ka > kb ? 1 : 0;\n });\n return results;\n }\n\n // -------------------------------------------------------------------------\n // install / uninstall\n // -------------------------------------------------------------------------\n\n async install(\n plugin: ResolvedPlugin,\n opts: InstallOptions = {}\n ): Promise<InstallResult> {\n // spec §2.1 行为契约第一条:detect()=false → skipped(CLI 不预过滤 target)\n if (!(await this.detect())) {\n return {\n target: 'codex',\n status: 'skipped',\n included: [],\n skipped: [],\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n\n // SHOULD-FIX 防御性校验:即便上游 resolver 已约束 PluginRef 字符集,\n // Installer 层再做一次路径穿越防护 —— ref.name / ref.marketplace / version\n // 都不允许含路径分隔符或 `..`。\n const pathGuardError = assertSafePathComponents(plugin.ref, plugin.version);\n if (pathGuardError) {\n return {\n target: 'codex',\n status: 'failed',\n included: [],\n skipped: [],\n artifacts: { files: [], mcpKeys: [] },\n errors: [pathGuardError],\n };\n }\n\n const ref = plugin.ref;\n\n // 幂等:相同版本已装 + force=false → no-op\n if (!opts.force && (await this.isInstalled(ref))) {\n const existingVersion = await detectInstalledVersion(this.home, ref);\n if (existingVersion?.version === plugin.version) {\n return {\n target: 'codex',\n status: 'ok',\n included: computeIncludedCapabilities(plugin),\n skipped: [...CODEX_UNSUPPORTED],\n artifacts: { files: [], mcpKeys: [] },\n notes: [`插件已装(version=${plugin.version}),跳过重复安装`],\n };\n }\n }\n\n // dryRun:只计算、不写盘\n if (opts.dryRun) {\n return this.buildDryRunResult(plugin);\n }\n\n // 真实 install —— 走完整 pipeline 并收集回滚信息\n const rollback: RollbackPlan = {\n createdVersionDir: null,\n createdMarketplacePluginDir: null,\n backupPath: null,\n preExistingConfig: null,\n };\n\n try {\n return await this.doInstall(plugin, rollback);\n } catch (err) {\n // 回滚已写入文件 + 还原 config.toml\n await rollbackInstall(this.home, rollback);\n // 预期错误 → InstallResult.failed;bug 级异常继续上抛\n if (isExpectedInstallError(err)) {\n const message = (err as Error).message;\n return {\n target: 'codex',\n status: 'failed',\n included: [],\n skipped: [],\n artifacts: { files: [], mcpKeys: [] },\n errors: [message],\n notes: rollback.backupPath\n ? [`已从备份恢复 config.toml(${rollback.backupPath} 已删除)`]\n : undefined,\n };\n }\n throw err;\n }\n }\n\n async uninstall(\n ref: PluginRef,\n opts: UninstallOptions = {}\n ): Promise<UninstallResult> {\n if (!(await this.detect())) {\n return {\n target: 'codex',\n status: 'skipped',\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n\n // uninstall 不涉及 version 路径(只改 config.toml),但 ref.* 仍参与 section key\n // 里的 @ + \".\" 组合;做路径穿越守护避免恶意 ref 构造。\n const pathGuardError = assertSafePathComponents(ref, /* version */ null);\n if (pathGuardError) {\n return {\n target: 'codex',\n status: 'failed',\n artifacts: { files: [], mcpKeys: [] },\n errors: [pathGuardError],\n };\n }\n\n const cfgPath = codexConfigTomlPath(this.home);\n if (!(await pathExists(cfgPath))) {\n return {\n target: 'codex',\n status: 'ok',\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n\n const key = pluginSectionKey(ref);\n // 本次会删除哪些 files(不含 cache 目录,对齐 spec §task-8 \"cache 目录保留\")\n const plannedFiles: string[] = [cfgPath];\n\n // 整体 try/catch 包住从 read 开始的所有 IO/TOML 操作,保证任何预期错误\n // (Corrupt / Conflict / FS E*)都转成 status=failed 而非上抛(round-2 MUST-FIX #1)\n let actualBackup: string | null = null;\n try {\n const { data, mtimeMs } = await readCodexConfig(cfgPath);\n const pluginsSection = data.plugins as\n | Record<string, unknown>\n | undefined;\n const pluginExists =\n pluginsSection &&\n typeof pluginsSection === 'object' &&\n key in pluginsSection;\n\n if (!pluginExists) {\n // 未装 → no-op,status=ok(spec §2.1 \"若未装则 status=ok,no-op\")\n return {\n target: 'codex',\n status: 'ok',\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n\n // dryRun:只计算,不写\n if (opts.dryRun) {\n return {\n target: 'codex',\n status: 'ok',\n artifacts: { files: plannedFiles, mcpKeys: [] },\n };\n }\n\n // 真写:备份 + 修改 + 写回\n const backupPath = codexConfigBackupPath(this.home, this.now());\n actualBackup = await backupCodexConfig(cfgPath, backupPath);\n\n removePluginEntry(data, key);\n // 若该 marketplace 已无其他 plugin → 一并删 [marketplaces.<mkt>]\n if (!marketplaceHasOtherPlugins(data, ref.marketplace, key)) {\n removeMarketplaceSection(data, ref.marketplace);\n }\n\n await writeCodexConfig(cfgPath, data, mtimeMs);\n } catch (err) {\n // 失败 → 只有**备份确实发生了**(我们可能已经污染了 config.toml)才 restore。\n // 若 backup 尚未发生(读 config.toml 就挂了,例如 TOML 损坏),**不要**调用\n // restoreCodexConfigFromBackup —— 该函数在 backup=null 时会删除当前文件,\n // 而此时磁盘还是用户原始状态(虽然损坏但可人工修复),删掉 = 数据丢失。\n // Round-3 MUST-FIX #1 修复。\n if (actualBackup !== null) {\n await restoreCodexConfigFromBackup(cfgPath, actualBackup).catch(\n () => {}\n );\n }\n // 对齐 install() 的结构化失败处理:预期错误 → status=failed;bug 级上抛\n if (isExpectedInstallError(err)) {\n return {\n target: 'codex',\n status: 'failed',\n artifacts: { files: [], mcpKeys: [] },\n errors: [(err as Error).message],\n };\n }\n throw err;\n }\n\n // backup 本身也是本次 uninstall 写入的产物 —— 和 install 侧保持对称\n const finalFiles = actualBackup\n ? [...plannedFiles, actualBackup]\n : plannedFiles;\n\n return {\n target: 'codex',\n status: 'ok',\n artifacts: {\n files: finalFiles,\n mcpKeys: [],\n meta: actualBackup ? { backupPath: actualBackup } : undefined,\n },\n };\n }\n\n // -------------------------------------------------------------------------\n // Private —— install 主流程\n // -------------------------------------------------------------------------\n\n private async doInstall(\n plugin: ResolvedPlugin,\n rollback: RollbackPlan\n ): Promise<InstallResult> {\n const ref = plugin.ref;\n const key = pluginSectionKey(ref);\n const versionDir = codexPluginVersionDir(this.home, ref, plugin.version);\n const marketplaceDir = codexMarketplaceDir(this.home, ref.marketplace);\n const marketplacePluginDir = codexMarketplacePluginDir(\n marketplaceDir,\n ref.name\n );\n const cfgPath = codexConfigTomlPath(this.home);\n\n // 1. 准备 version dir —— 如果已存在(force 模式或残留),清理后重建\n const preExisted = await pathExists(versionDir);\n if (preExisted) {\n await rm(versionDir, { recursive: true, force: true });\n }\n await mkdir(versionDir, { recursive: true });\n rollback.createdVersionDir = versionDir;\n\n // 2. 生成 Codex 可扫描的 skills/(支持原生 skills/、Claude .skills/、\n // 以及 slash command markdown 转换)\n const dstSkills = codexPluginSkillsDir(versionDir);\n const writtenFiles: string[] = [versionDir];\n const skillsPlan = await planCodexSkills(plugin);\n if (skillsPlan.hasSkills) {\n await materializeCodexSkills(skillsPlan, dstSkills);\n writtenFiles.push(dstSkills);\n }\n\n // 3. 处理 MCP —— 对象形态生成 `.mcp.json`;字符串形态 copy 作者指定的外部文件\n //\n // 这是 MUST-FIX 3 的策略:不让 `mcpServers: string` 静默丢失 MCP 安装。\n //\n // 分派:\n // - 对象形态(作者内嵌声明)→ rush-ai normalize + 生成 `.mcp.json`\n // - 字符串形态(作者已备好 `.mcp.json`)→ 把源文件 copy 到 versionDir,\n // plugin.json 透传原引用字符串\n // - 无声明(undefined)→ plugin.json 不写 `mcpServers`,不生成 `.mcp.json`\n let mcpJsonRef: string | undefined; // 写入 plugin.json 的字符串引用\n let mcpKeys: string[] = [];\n const srcMcp = plugin.manifest.mcpServers;\n if (srcMcp !== undefined && srcMcp !== null) {\n if (typeof srcMcp === 'string') {\n // 作者自备 .mcp.json:copy 到 versionDir,plugin.json 引用原路径\n const resolvedExternal = await copyAuthorProvidedMcp(\n plugin.sourceDir,\n versionDir,\n srcMcp\n );\n if (resolvedExternal) {\n writtenFiles.push(resolvedExternal.destPath);\n mcpJsonRef = srcMcp;\n mcpKeys = resolvedExternal.mcpKeys;\n } else {\n // 声明了但源文件缺失 / 路径穿越 → 视为预期错误,触发 rollback\n throw new CodexInstallError(\n `plugin.json.mcpServers 引用的外部文件 '${srcMcp}' 不存在或路径非法(相对 ${plugin.sourceDir})`\n );\n }\n } else {\n // 对象形态:走 rush-ai normalize + 生成标准 .mcp.json\n const mcpContent = buildCodexMcpContent(\n ref,\n plugin.manifest,\n this.rushAiBinaryResolver\n );\n if (mcpContent) {\n const mcpPath = codexPluginMcpPath(versionDir);\n await writeFileAtomic(mcpPath, serializeMcpJson(mcpContent));\n writtenFiles.push(mcpPath);\n mcpJsonRef = './.mcp.json';\n mcpKeys = listMcpKeys(mcpContent);\n }\n }\n }\n\n // 写 plugin.json(mcpServers 字段按 mcpJsonRef 决定写不写 / 写什么值)\n const pluginJsonObj = buildCodexPluginJson(plugin.manifest, mcpJsonRef);\n const pluginJsonPath = codexPluginManifestPath(versionDir);\n await writeFileAtomic(\n pluginJsonPath,\n `${JSON.stringify(pluginJsonObj, null, 2)}\\n`\n );\n writtenFiles.push(pluginJsonPath);\n\n // 5. 更新 config.toml —— 备份 + read-modify-write\n const backupDest = codexConfigBackupPath(this.home, this.now());\n const actualBackup = await backupCodexConfig(cfgPath, backupDest);\n rollback.backupPath = actualBackup;\n\n const { data: config, mtimeMs } = await readCodexConfig(cfgPath);\n rollback.preExistingConfig = mtimeMs !== null;\n\n setMarketplaceSection(config, ref.marketplace, {\n last_updated: this.now().toISOString(),\n source_type: 'local',\n source: marketplaceDir,\n });\n setPluginEntry(config, key, { enabled: true });\n\n await writeCodexConfig(cfgPath, config, mtimeMs);\n writtenFiles.push(cfgPath);\n // backup 也算本次写入的副产物 —— spec §1.4 要求 artifacts.files 完整,\n // CLI 卸载 / 审计路径需要知道备份文件位置\n if (actualBackup) {\n writtenFiles.push(actualBackup);\n }\n\n // 6. 生成 Codex 原生 marketplace mirror。插件页读取 marketplace source\n // 下的 `.agents/plugins/marketplace.json`,只写 runtime cache 不会出现在 UI。\n await writeCodexMarketplaceMirror({\n marketplaceDir,\n pluginDir: marketplacePluginDir,\n plugin,\n versionDir,\n });\n rollback.createdMarketplacePluginDir = marketplacePluginDir;\n writtenFiles.push(\n marketplaceDir,\n codexMarketplaceManifestPath(marketplaceDir),\n marketplacePluginDir\n );\n\n // 7. 组装 InstallResult\n const included = computeIncludedCapabilities(plugin);\n\n const notes: string[] = [];\n if (actualBackup) {\n notes.push(`已备份 config.toml 到 ${actualBackup}`);\n }\n\n const artifacts = {\n files: dedup(writtenFiles),\n mcpKeys,\n meta: actualBackup ? { backupPath: actualBackup } : undefined,\n };\n\n return {\n target: 'codex',\n status: 'ok',\n included,\n skipped: [...CODEX_UNSUPPORTED],\n artifacts,\n notes: notes.length > 0 ? notes : undefined,\n };\n }\n\n /**\n * 生成 dryRun InstallResult —— 计算会写入哪些文件 + MCP key,不触磁盘。\n *\n * 和真实 install 行为对齐(round-2 MUST-FIX #2):\n * - mcpServers 对象形态 → 预报 `.mcp.json` 路径 + mcpKeys\n * - mcpServers 字符串形态 → 预报 copy 后的 `<versionDir>/<ref>` 路径 + 从源文件\n * 解析出的 mcpKeys(如果源文件存在且合法);若源文件缺失则仍给出预期写入路径\n * - 无 mcpServers → 不列 MCP 文件\n */\n private async buildDryRunResult(\n plugin: ResolvedPlugin\n ): Promise<InstallResult> {\n const ref = plugin.ref;\n const versionDir = codexPluginVersionDir(this.home, ref, plugin.version);\n const marketplaceDir = codexMarketplaceDir(this.home, ref.marketplace);\n const marketplacePluginDir = codexMarketplacePluginDir(\n marketplaceDir,\n ref.name\n );\n const cfgPath = codexConfigTomlPath(this.home);\n\n const files: string[] = [\n versionDir,\n codexPluginManifestPath(versionDir),\n marketplaceDir,\n codexMarketplaceManifestPath(marketplaceDir),\n marketplacePluginDir,\n cfgPath,\n ];\n\n const skillsPlan = await planCodexSkills(plugin);\n if (skillsPlan.hasSkills) {\n files.push(codexPluginSkillsDir(versionDir));\n }\n\n let mcpKeys: string[] = [];\n const notes: string[] = ['dry-run:未写入磁盘'];\n let dryRunStatus: 'ok' | 'failed' = 'ok';\n const dryRunErrors: string[] = [];\n const srcMcp = plugin.manifest.mcpServers;\n if (srcMcp !== undefined && srcMcp !== null) {\n if (typeof srcMcp === 'string') {\n // 字符串形态:预报 copy 目标路径\n const destPath = resolve(versionDir, srcMcp);\n files.push(destPath);\n const srcPath = resolve(plugin.sourceDir, srcMcp);\n // 和真实 install 对齐(round-3 SHOULD-FIX):路径穿越 / 源文件缺失 →\n // 真实 install 会 failed;dryRun 也应该给 failed,让 CLI 预演输出准确\n const rel = pathRelative(resolve(plugin.sourceDir), srcPath);\n const traversal =\n rel === '..' || rel.startsWith('..') || rel.startsWith('/');\n const exists = await pathExists(srcPath);\n if (traversal || !exists) {\n dryRunStatus = 'failed';\n dryRunErrors.push(\n `plugin.json.mcpServers 引用的外部文件 '${srcMcp}' ${traversal ? '路径非法' : '不存在'}(相对 ${plugin.sourceDir})`\n );\n } else {\n // 源存在 → 尽力解析 mcpKeys(宽容失败,解析错不降级 status)\n try {\n const raw = await readFile(srcPath, 'utf8');\n const parsed = JSON.parse(raw) as {\n mcpServers?: Record<string, unknown>;\n };\n if (parsed.mcpServers && typeof parsed.mcpServers === 'object') {\n mcpKeys = Object.keys(parsed.mcpServers).sort();\n }\n } catch {\n // dryRun 不让解析失败阻塞 status\n }\n }\n } else {\n const mcpContent = buildCodexMcpContent(\n ref,\n plugin.manifest,\n this.rushAiBinaryResolver\n );\n if (mcpContent) {\n files.push(codexPluginMcpPath(versionDir));\n mcpKeys = listMcpKeys(mcpContent);\n }\n }\n }\n\n return {\n target: 'codex',\n status: dryRunStatus,\n included: computeIncludedCapabilities(plugin),\n skipped: [...CODEX_UNSUPPORTED],\n artifacts: { files: dedup(files), mcpKeys },\n notes,\n ...(dryRunErrors.length > 0 ? { errors: dryRunErrors } : {}),\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers —— exported for tests where useful\n// ---------------------------------------------------------------------------\n\n/**\n * 计算本次 install 实际写入的 capabilities(plugin 声明 ∩ Codex 支持)。\n *\n * Codex 支持:`skills` / `mcp`。\n *\n * `rules` / `hooks` 即使 plugin 声明也不写入(跳过),通过\n * `InstallResult.skipped` 明示。\n * Claude-style `commands/*.md` 会转换成 Codex skills。\n */\nexport function computeIncludedCapabilities(\n plugin: ResolvedPlugin\n): PluginCapability[] {\n const included: PluginCapability[] = [];\n if (\n plugin.capabilities.includes('skills') ||\n plugin.capabilities.includes('commands')\n ) {\n included.push('skills');\n }\n if (plugin.capabilities.includes('mcp')) {\n included.push('mcp');\n }\n return included;\n}\n\n/**\n * 生成 Codex plugin.json 内容(spec §2.3)。\n *\n * 处理:\n * - 透传 manifest 里和 Codex 相关的字段(`name`/`version`/`description`/`author`/\n * `homepage`/`license`/`keywords`/`interface`)\n * - **必写** `\"skills\": \"./skills/\"`(决策已定,和 OpenAI bundled 保持一致,\n * 即使 Codex 实测可选)\n * - `mcpServers` 字段写字符串 `\"./.mcp.json\"`(Codex 约定,不能内嵌)。如果\n * plugin 没有 MCP 声明 → 不写 `mcpServers` 字段\n * - **不写** `commands`/`rules`/`hooks`/`repository`——Codex 不使用;\n * command markdown 已在安装阶段转换成 skills\n *\n * 返回的是\"可直接 JSON.stringify 成磁盘文件\"的对象。\n */\nexport function buildCodexPluginJson(\n manifest: PluginManifest,\n mcpServersRef: string | undefined\n): Record<string, unknown> {\n const out: Record<string, unknown> = {\n name: manifest.name,\n version: manifest.version,\n };\n if (manifest.description !== undefined)\n out.description = manifest.description;\n if (manifest.author !== undefined) out.author = manifest.author;\n if (manifest.homepage !== undefined) out.homepage = manifest.homepage;\n if (manifest.license !== undefined) out.license = manifest.license;\n if (manifest.keywords !== undefined) out.keywords = manifest.keywords;\n\n // spec §2.3 决策:skills 字段必写\n out.skills = './skills/';\n\n // mcpServers 字段 —— 字符串引用(只有在 plugin 有 MCP 声明时才写)\n if (mcpServersRef !== undefined) {\n out.mcpServers = mcpServersRef;\n }\n\n // Codex 桌面端展示用。第三方 Claude-style plugin 往往没有 interface,\n // 这里补一份最小可展示 metadata;作者显式字段优先。\n out.interface = buildCodexInterfaceMetadata(manifest);\n\n return out;\n}\n\nexport function buildCodexInterfaceMetadata(\n manifest: PluginManifest\n): NonNullable<PluginManifest['interface']> {\n const existing = manifest.interface ?? {};\n return {\n displayName: existing.displayName ?? manifest.name,\n shortDescription:\n existing.shortDescription ?? manifest.description ?? manifest.name,\n longDescription: existing.longDescription ?? manifest.description,\n developerName: existing.developerName ?? manifest.author?.name,\n category: existing.category ?? 'Engineering',\n capabilities: existing.capabilities ?? ['Read', 'Write'],\n websiteURL: existing.websiteURL ?? manifest.homepage,\n privacyPolicyURL: existing.privacyPolicyURL,\n termsOfServiceURL: existing.termsOfServiceURL,\n defaultPrompt: existing.defaultPrompt,\n brandColor: existing.brandColor,\n composerIcon: existing.composerIcon,\n logo: existing.logo,\n screenshots: existing.screenshots,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\ninterface RollbackPlan {\n createdVersionDir: string | null;\n createdMarketplacePluginDir: string | null;\n backupPath: string | null;\n /** config.toml 在 install 之前是否已存在(决定 restore 策略)。 */\n preExistingConfig: boolean | null;\n}\n\nasync function rollbackInstall(\n home: string,\n rollback: RollbackPlan\n): Promise<void> {\n // 1. 还原 config.toml:\n // - 有 backup → restore(覆盖当前文件,删 backup)\n // - 无 backup + preExistingConfig === false → install 前文件不存在且我们可能刚\n // 写了一份 → 删掉(best-effort)回到 install 前\"无 config\"状态\n // - 无 backup + preExistingConfig !== false → 我们在 read 或更早阶段就失败了,\n // 磁盘仍是用户原始状态,**不动**(MUST-FIX round-3 #1 同构保护)\n const cfgPath = codexConfigTomlPath(home);\n if (rollback.backupPath) {\n await restoreCodexConfigFromBackup(cfgPath, rollback.backupPath).catch(\n () => {}\n );\n } else if (rollback.preExistingConfig === false) {\n // install 之前 config.toml 本不存在;如果我们写了半道失败,删除之\n await rm(cfgPath, { force: true }).catch(() => {});\n }\n // 2. 删除本次创建的 version dir(best-effort)\n if (rollback.createdVersionDir) {\n await rm(rollback.createdVersionDir, {\n recursive: true,\n force: true,\n }).catch(() => {});\n }\n if (rollback.createdMarketplacePluginDir) {\n await rm(rollback.createdMarketplacePluginDir, {\n recursive: true,\n force: true,\n }).catch(() => {});\n }\n}\n\nfunction isExpectedInstallError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n // 允许接受:FS 错误(ENOENT/EACCES/EEXIST 等)、CodexConfigToml*Error、\n // CodexInstallError(Installer 自抛的预期失败信号)\n if (err instanceof CodexInstallError) return true;\n const name = err.name;\n if (name?.startsWith('CodexConfigToml')) return true;\n const code = (err as NodeJS.ErrnoException).code;\n if (typeof code === 'string' && /^E[A-Z]+$/.test(code)) return true;\n return false;\n}\n\n/**\n * 路径分量安全校验(SHOULD-FIX 防御性防护)。\n *\n * `ref.name` / `ref.marketplace` / `version` 都会参与构造 cache 目录路径,\n * 如果上游允许 `../` 或 `/` 进入,`path.resolve` 会把文件写出 `~/.codex/plugins/cache/`\n * 之外。此函数做\"严格白名单\":只允许字母、数字、 `-`、`_`、 `.`、`@`、`+`(version\n * 允许 semver build metadata),且**不得**以 `.` 开头(防 `.`、`..`)。\n *\n * version 的白名单比 name/marketplace 宽 1 个字符(`+`)——对应 semver build\n * metadata `1.2.3+build.1`(round-2 SHOULD-FIX)。\n *\n * 返回 null = 校验通过;返回字符串 = 人类可读的错误消息(供 InstallResult.errors)。\n */\nfunction assertSafePathComponents(\n ref: PluginRef,\n version: string | null\n): string | null {\n const checkWithCharset = (\n value: string,\n label: string,\n charset: RegExp\n ): string | null => {\n if (typeof value !== 'string' || value.length === 0) {\n return `${label} 非法:必须是非空字符串`;\n }\n // 不允许任何路径分隔符 / parent traversal\n if (value.includes('/') || value.includes('\\\\')) {\n return `${label} 非法:不允许路径分隔符(${JSON.stringify(value)})`;\n }\n if (value === '.' || value === '..' || value.startsWith('..')) {\n return `${label} 非法:不允许以 '.' / '..' 开头(${JSON.stringify(value)})`;\n }\n if (!charset.test(value)) {\n return `${label} 非法:字符集不允许(${JSON.stringify(value)})`;\n }\n return null;\n };\n const nameCharset = /^[A-Za-z0-9_.\\-@]+$/;\n // version 额外允许 `+` 支持 semver build metadata(如 `1.2.3+build.1`)\n const versionCharset = /^[A-Za-z0-9_.\\-@+]+$/;\n return (\n checkWithCharset(ref.name, 'plugin name', nameCharset) ??\n checkWithCharset(ref.marketplace, 'marketplace name', nameCharset) ??\n (version !== null\n ? checkWithCharset(version, 'plugin version', versionCharset)\n : null)\n );\n}\n\n/**\n * 处理 `plugin.json.mcpServers: string` 形态 —— 把作者指向的外部文件 copy 到\n * versionDir 对应路径,plugin.json 里透传原字符串引用。\n *\n * 仅支持**相对 plugin sourceDir** 的路径(最常见:`\"./.mcp.json\"`);绝对路径\n * 或含 `../` 逃逸 → 返回 null,调用方会把这视为错误场景触发 rollback。\n *\n * 返回 `{ destPath, mcpKeys }`:destPath = 写入 versionDir 的绝对路径;\n * mcpKeys = 从源文件里解析出的 server key 清单(供 InstallResult.artifacts 使用)。\n * 源文件不存在 / 非法路径 → 返回 null。\n */\nasync function copyAuthorProvidedMcp(\n sourceDir: string,\n versionDir: string,\n relativeRef: string\n): Promise<{ destPath: string; mcpKeys: string[] } | null> {\n // 相对路径规范化\n if (typeof relativeRef !== 'string' || relativeRef.length === 0) return null;\n const srcPath = resolve(sourceDir, relativeRef);\n // 安全校验:必须落在 sourceDir 下\n const rel = pathRelative(resolve(sourceDir), srcPath);\n if (rel === '..' || rel.startsWith('..') || rel.startsWith('/')) {\n return null;\n }\n if (!(await pathExists(srcPath))) return null;\n\n // dst 位置 = versionDir/<relativeRef 解析后路径>\n const destPath = resolve(versionDir, relativeRef);\n await mkdir(dirname(destPath), { recursive: true });\n const raw = await readFile(srcPath, 'utf8');\n await writeFileAtomic(destPath, raw);\n\n // 提取 mcpKeys:宽容解析 JSON 的 mcpServers top-level keys\n let mcpKeys: string[] = [];\n try {\n const parsed = JSON.parse(raw) as { mcpServers?: Record<string, unknown> };\n if (parsed.mcpServers && typeof parsed.mcpServers === 'object') {\n mcpKeys = Object.keys(parsed.mcpServers).sort();\n }\n } catch {\n // 源文件不是合法 JSON —— 不阻塞 install,mcpKeys 留空\n mcpKeys = [];\n }\n return { destPath, mcpKeys };\n}\n\nasync function writeCodexMarketplaceMirror(input: {\n marketplaceDir: string;\n pluginDir: string;\n plugin: ResolvedPlugin;\n versionDir: string;\n}): Promise<void> {\n const pluginParent = dirname(input.pluginDir);\n const pluginBase = basename(input.pluginDir);\n await mkdir(pluginParent, { recursive: true });\n const tmpPluginDir = await mkdtemp(\n resolve(pluginParent, `.${pluginBase}.tmp-`)\n );\n const backupPluginDir = await mkdtemp(\n resolve(pluginParent, `.${pluginBase}.bak-`)\n );\n let hadExistingPluginDir = false;\n let swappedPluginDir = false;\n try {\n await rm(tmpPluginDir, { recursive: true, force: true });\n await rm(backupPluginDir, { recursive: true, force: true }).catch(() => {});\n await cp(input.versionDir, tmpPluginDir, {\n recursive: true,\n dereference: false,\n preserveTimestamps: true,\n verbatimSymlinks: true,\n });\n hadExistingPluginDir = await pathExists(input.pluginDir);\n if (hadExistingPluginDir) {\n await rename(input.pluginDir, backupPluginDir);\n }\n // tmp/backup live beside pluginDir, so the directory swaps stay on one device.\n await rename(tmpPluginDir, input.pluginDir);\n swappedPluginDir = true;\n await upsertCodexMarketplaceManifest(input.marketplaceDir, input.plugin);\n if (hadExistingPluginDir) {\n await rm(backupPluginDir, { recursive: true, force: true });\n }\n } catch (err) {\n await rm(tmpPluginDir, { recursive: true, force: true }).catch(() => {});\n let restoredBackup = false;\n if (hadExistingPluginDir && (await pathExists(backupPluginDir))) {\n let displacedPluginDir: string | null = null;\n try {\n if (await pathExists(input.pluginDir)) {\n displacedPluginDir = await mkdtemp(\n resolve(pluginParent, `.${pluginBase}.failed-`)\n );\n await rm(displacedPluginDir, { recursive: true, force: true });\n await rename(input.pluginDir, displacedPluginDir);\n }\n await rename(backupPluginDir, input.pluginDir);\n restoredBackup = true;\n if (displacedPluginDir) {\n await rm(displacedPluginDir, { recursive: true, force: true }).catch(\n () => {}\n );\n }\n } catch {\n if (\n displacedPluginDir &&\n !(await pathExists(input.pluginDir)) &&\n (await pathExists(displacedPluginDir))\n ) {\n await rename(displacedPluginDir, input.pluginDir).catch(() => {});\n }\n }\n } else if (swappedPluginDir) {\n await rm(input.pluginDir, { recursive: true, force: true }).catch(\n () => {}\n );\n }\n if (restoredBackup || !hadExistingPluginDir) {\n await rm(backupPluginDir, { recursive: true, force: true }).catch(\n () => {}\n );\n }\n throw err;\n }\n}\n\nasync function upsertCodexMarketplaceManifest(\n marketplaceDir: string,\n plugin: ResolvedPlugin\n): Promise<void> {\n const manifestPath = codexMarketplaceManifestPath(marketplaceDir);\n const manifest = await readCodexMarketplaceManifest(manifestPath, {\n name: plugin.ref.marketplace,\n interface: {\n displayName: plugin.ref.marketplace,\n },\n plugins: [],\n });\n const plugins = Array.isArray(manifest.plugins) ? manifest.plugins : [];\n const nextEntry = {\n name: plugin.ref.name,\n source: {\n source: 'local',\n path: `./plugins/${plugin.ref.name}`,\n },\n policy: {\n installation: 'AVAILABLE',\n authentication: 'ON_INSTALL',\n },\n category: buildCodexInterfaceMetadata(plugin.manifest).category,\n };\n const nextPlugins = [\n ...plugins.filter((entry) => entry?.name !== plugin.ref.name),\n nextEntry,\n ].sort((a, b) => a.name.localeCompare(b.name));\n await writeFileAtomic(\n manifestPath,\n `${JSON.stringify({ ...manifest, plugins: nextPlugins }, null, 2)}\\n`\n );\n}\n\ninterface CodexMarketplaceManifest {\n name: string;\n interface?: { displayName?: string };\n plugins: Array<{\n name: string;\n source?: unknown;\n policy?: unknown;\n category?: unknown;\n }>;\n}\n\nasync function readCodexMarketplaceManifest(\n manifestPath: string,\n fallback: CodexMarketplaceManifest\n): Promise<CodexMarketplaceManifest> {\n if (!(await pathExists(manifestPath))) return fallback;\n try {\n const parsed = JSON.parse(await readFile(manifestPath, 'utf8')) as {\n name?: unknown;\n interface?: unknown;\n plugins?: unknown;\n };\n return {\n name: typeof parsed.name === 'string' ? parsed.name : fallback.name,\n interface:\n parsed.interface &&\n typeof parsed.interface === 'object' &&\n !Array.isArray(parsed.interface)\n ? (parsed.interface as CodexMarketplaceManifest['interface'])\n : fallback.interface,\n plugins: Array.isArray(parsed.plugins)\n ? parsed.plugins.filter(isMarketplacePluginEntry)\n : fallback.plugins,\n };\n } catch {\n return fallback;\n }\n}\n\nfunction isMarketplacePluginEntry(\n value: unknown\n): value is CodexMarketplaceManifest['plugins'][number] {\n return (\n !!value &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n typeof (value as { name?: unknown }).name === 'string'\n );\n}\n\ntype SkillSource =\n | { kind: 'copy-skill'; sourceDir: string; skillName: string }\n | { kind: 'markdown'; sourceFile: string; skillName: string };\n\ninterface CodexSkillsPlan {\n sources: SkillSource[];\n hasSkills: boolean;\n}\n\nasync function planCodexSkills(\n plugin: ResolvedPlugin\n): Promise<CodexSkillsPlan> {\n const sources: SkillSource[] = [];\n const usedNames = new Set<string>();\n const coveredAliases = new Set<string>();\n\n const commandFiles = await listMarkdownFiles(\n resolve(plugin.sourceDir, 'commands'),\n {\n recursive: false,\n }\n );\n const commandAliases = new Set(\n await Promise.all(\n commandFiles.map((file) => markdownSkillAlias(plugin.manifest.name, file))\n )\n );\n\n const nativeSkillsDir = resolve(plugin.sourceDir, 'skills');\n if (await hasCodexSkillDir(nativeSkillsDir)) {\n for (const name of await listCodexSkillNames(nativeSkillsDir)) {\n usedNames.add(name);\n coveredAliases.add(skillAlias(plugin.manifest.name, name));\n sources.push({\n kind: 'copy-skill',\n sourceDir: resolve(nativeSkillsDir, name),\n skillName: name,\n });\n }\n } else {\n const dotSkillsDir = resolve(plugin.sourceDir, '.skills');\n const claudeSkills = await listMarkdownFiles(dotSkillsDir);\n const claudeAliases = new Map<string, number>();\n for (const file of claudeSkills) {\n const alias = await markdownSkillAlias(plugin.manifest.name, file);\n claudeAliases.set(alias, (claudeAliases.get(alias) ?? 0) + 1);\n }\n for (const file of claudeSkills) {\n const alias = await markdownSkillAlias(plugin.manifest.name, file);\n if (commandAliases.has(alias)) {\n continue;\n }\n const uniqueAlias =\n (claudeAliases.get(alias) ?? 0) > 1\n ? markdownPathAlias(plugin.manifest.name, dotSkillsDir, file)\n : alias;\n const skillName = uniqueSkillName(\n usedNames,\n `${plugin.manifest.name}-${uniqueAlias}`\n );\n coveredAliases.add(alias);\n coveredAliases.add(uniqueAlias);\n sources.push({ kind: 'markdown', sourceFile: file, skillName });\n }\n }\n\n for (const file of commandFiles) {\n const alias = await markdownSkillAlias(plugin.manifest.name, file);\n if (coveredAliases.has(alias)) {\n continue;\n }\n const skillName = uniqueSkillName(\n usedNames,\n `${plugin.manifest.name}-${alias}`\n );\n coveredAliases.add(alias);\n sources.push({ kind: 'markdown', sourceFile: file, skillName });\n }\n\n return { sources, hasSkills: sources.length > 0 };\n}\n\nasync function materializeCodexSkills(\n plan: CodexSkillsPlan,\n dstSkills: string\n): Promise<void> {\n for (const source of plan.sources) {\n if (source.kind === 'copy-skill') {\n await cp(source.sourceDir, resolve(dstSkills, source.skillName), {\n recursive: true,\n dereference: false,\n preserveTimestamps: true,\n verbatimSymlinks: true,\n });\n continue;\n }\n const raw = await readFile(source.sourceFile, 'utf8');\n const normalized = normalizeSkillMarkdown(raw, source.skillName);\n await writeFileAtomic(\n resolve(dstSkills, source.skillName, 'SKILL.md'),\n normalized\n );\n }\n}\n\nfunction normalizeSkillMarkdown(raw: string, skillName: string): string {\n const text = raw.replace(/\\r\\n/g, '\\n');\n const parsed = splitMarkdownFrontmatter(text);\n if (parsed) {\n const lines = parsed.frontmatter.split('\\n');\n const hasName = lines.some((line) => /^name:\\s*/.test(line));\n const nextLines = hasName\n ? lines.map((line) =>\n /^name:\\s*/.test(line) ? `name: ${skillName}` : line\n )\n : [`name: ${skillName}`, ...lines];\n const frontmatter = nextLines.join('\\n');\n const next = `---\\n${frontmatter}${\n frontmatter.endsWith('\\n') ? '' : '\\n'\n }${parsed.afterFrontmatter}`;\n return next.endsWith('\\n') ? next : `${next}\\n`;\n }\n return `---\\nname: ${skillName}\\ndescription: ${\n firstMarkdownLine(raw) ?? skillName\n }\\n---\\n\\n${text.endsWith('\\n') ? text : `${text}\\n`}`;\n}\n\nfunction splitMarkdownFrontmatter(\n text: string\n): { frontmatter: string; afterFrontmatter: string } | null {\n if (!text.startsWith('---\\n')) return null;\n const lines = text.split('\\n');\n if (lines[0] !== '---') return null;\n let blockScalarIndent: number | null = null;\n for (let i = 1; i < lines.length; i += 1) {\n const line = lines[i] ?? '';\n const indent = leadingSpaces(line);\n if (blockScalarIndent !== null) {\n if (line.trim() === '' || indent > blockScalarIndent) {\n continue;\n }\n blockScalarIndent = null;\n }\n if (line === '---') {\n return {\n frontmatter: lines.slice(1, i).join('\\n'),\n afterFrontmatter: lines.slice(i).join('\\n'),\n };\n }\n if (/^[A-Za-z0-9_.-]+:\\s*[|>]/.test(line)) {\n blockScalarIndent = indent;\n }\n }\n return null;\n}\n\nfunction leadingSpaces(value: string): number {\n const match = value.match(/^ */);\n return match?.[0].length ?? 0;\n}\n\nfunction frontmatterValue(frontmatter: string, key: string): string | null {\n for (const line of frontmatter.split('\\n')) {\n const match = line.match(/^([A-Za-z0-9_.-]+):\\s*(.*)$/);\n if (match?.[1] === key) {\n return match[2]?.trim() ?? '';\n }\n }\n return null;\n}\n\nfunction firstMarkdownLine(raw: string): string | null {\n const line = raw\n .split(/\\r?\\n/)\n .map((value) => value.trim())\n .find((value) => value.length > 0 && value !== '---');\n if (!line) return null;\n return line.replace(/^#+\\s*/, '').slice(0, 140);\n}\n\nasync function hasCodexSkillDir(skillsDir: string): Promise<boolean> {\n return (await listCodexSkillNames(skillsDir)).length > 0;\n}\n\nasync function listCodexSkillNames(skillsDir: string): Promise<string[]> {\n if (!(await isDir(skillsDir))) return [];\n let entries: Dirent[];\n try {\n entries = await readdir(skillsDir, { withFileTypes: true });\n } catch {\n return [];\n }\n const names: string[] = [];\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (await pathExists(resolve(skillsDir, entry.name, 'SKILL.md'))) {\n names.push(entry.name);\n }\n }\n return names.sort();\n}\n\nasync function listMarkdownFiles(\n dir: string,\n opts: { recursive?: boolean } = {}\n): Promise<string[]> {\n if (!(await isDir(dir))) return [];\n const recursive = opts.recursive !== false;\n const out: string[] = [];\n async function walk(current: string): Promise<void> {\n let entries: Dirent[];\n try {\n entries = await readdir(current, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const abs = resolve(current, entry.name);\n if (entry.isDirectory()) {\n if (recursive) await walk(abs);\n continue;\n }\n if (entry.isFile() && extname(entry.name).toLowerCase() === '.md') {\n out.push(abs);\n }\n }\n }\n await walk(dir);\n return out.sort();\n}\n\nfunction uniqueSkillName(used: Set<string>, desired: string): string {\n const base = desired.length > 0 ? desired : 'skill';\n let candidate = base;\n let i = 2;\n while (used.has(candidate)) {\n candidate = `${base}-${i}`;\n i += 1;\n }\n used.add(candidate);\n return candidate;\n}\n\nasync function markdownSkillAlias(\n pluginName: string,\n filePath: string\n): Promise<string> {\n const triggerAlias = extractTriggerAlias(await readFile(filePath, 'utf8'));\n return skillAlias(\n pluginName,\n triggerAlias ?? basename(filePath, extname(filePath))\n );\n}\n\nfunction markdownPathAlias(\n pluginName: string,\n baseDir: string,\n filePath: string\n): string {\n const rel = pathRelative(baseDir, filePath);\n return skillAlias(pluginName, rel.slice(0, -extname(rel).length));\n}\n\nfunction skillAlias(pluginName: string, value: string): string {\n let alias = slugify(value);\n const pluginPrefix = `${slugify(pluginName)}-`;\n if (alias.startsWith(pluginPrefix)) {\n alias = alias.slice(pluginPrefix.length);\n }\n return alias.length > 0 ? alias : 'skill';\n}\n\nfunction extractTriggerAlias(raw: string): string | null {\n const parsed = splitMarkdownFrontmatter(raw.replace(/\\r\\n/g, '\\n'));\n const trigger = parsed\n ? frontmatterValue(parsed.frontmatter, 'trigger')\n : null;\n if (!trigger) return null;\n const match = trigger.match(/\\/[A-Za-z0-9_.-]+:([A-Za-z0-9_.-]+)/);\n return match?.[1] ?? null;\n}\n\nfunction slugify(value: string): string {\n const slug = value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n return slug.length > 0 ? slug : 'skill';\n}\n\nasync function writeFileAtomic(\n filePath: string,\n content: string\n): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n // 写 .tmp → rename\n const tmp = `${filePath}.${Math.random().toString(36).slice(2)}.tmp`;\n try {\n await writeFile(tmp, content, { encoding: 'utf8', flag: 'w' });\n // rename\n const { rename } = await import('node:fs/promises');\n await rename(tmp, filePath);\n } catch (err) {\n await rm(tmp, { force: true }).catch(() => {});\n throw err;\n }\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p, fsConstants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function isDir(p: string): Promise<boolean> {\n try {\n const s = await stat(p);\n return s.isDirectory();\n } catch {\n return false;\n }\n}\n\nfunction dedup<T>(arr: T[]): T[] {\n return Array.from(new Set(arr));\n}\n\nfunction parsePluginKey(key: string): PluginRef | null {\n // 用**最后一个** `@` 拆分(round-2 SHOULD-FIX #2)—— plugin name 白名单允许 `@`\n // (如 npm scoped package `@scope/tool` 形式),marketplace name 不含 `@`。\n // 对 `@a/b@mkt` 这种 key,期望得到 name=`@a/b`、marketplace=`mkt`;对更保险的\n // 常规 `name@mkt` 也等价。\n const at = key.lastIndexOf('@');\n if (at <= 0 || at === key.length - 1) return null;\n return { name: key.slice(0, at), marketplace: key.slice(at + 1) };\n}\n\nasync function detectInstalledVersion(\n home: string,\n ref: PluginRef\n): Promise<{ version: string; installedAt: string } | null> {\n // 扫 `<cache>/<mkt>/<plugin>/` 下的子目录,读 `.codex-plugin/plugin.json` 的 version\n // 取 mtime 最新的目录作为\"当前 version\"\n const baseDir = resolve(\n codexHomeDir(home),\n 'plugins',\n 'cache',\n ref.marketplace,\n ref.name\n );\n if (!(await isDir(baseDir))) return null;\n\n let entries: string[];\n try {\n entries = await readdir(baseDir);\n } catch {\n return null;\n }\n\n let best: { version: string; mtimeMs: number } | null = null;\n for (const entry of entries) {\n const versionDir = resolve(baseDir, entry);\n const manifestPath = resolve(versionDir, '.codex-plugin', 'plugin.json');\n if (!(await pathExists(manifestPath))) continue;\n try {\n const raw = await readFile(manifestPath, 'utf8');\n const obj = JSON.parse(raw) as { version?: unknown };\n const version =\n typeof obj.version === 'string' && obj.version.length > 0\n ? obj.version\n : entry;\n const s = await stat(versionDir);\n if (!best || s.mtimeMs > best.mtimeMs) {\n best = { version, mtimeMs: s.mtimeMs };\n }\n } catch {\n // 单个 version 目录读失败不影响其他——继续\n }\n }\n if (!best) return null;\n return {\n version: best.version,\n installedAt: new Date(best.mtimeMs).toISOString(),\n };\n}\n\nfunction defaultRushAiBinaryResolver(): string | undefined {\n const argv1 = process.argv[1];\n if (typeof argv1 === 'string' && argv1.length > 0) {\n return argv1;\n }\n return undefined;\n}\n\n// 避免 \"unused import\" lint warning(pathRelative 仅用于 readability 的 future-proof)\n// 若真不需要则删;保留是为 rollback 时可能扩展相对路径比对\nvoid pathRelative;\n","/**\n * Codex 外部 `.mcp.json` 生成 + normalize(task-8 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §2.4 + plan §7.1 MCP command 规则。\n *\n * 关键差异(vs Claude Code):\n * - Codex 的 MCP 是**外部文件**:plugin.json 写 `\"mcpServers\": \"./.mcp.json\"`,\n * 真正的 server 声明在 `<versionDir>/.mcp.json` 里\n * - `.mcp.json` 结构:`{ \"mcpServers\": { <name>: { command, args, env, cwd } } }`\n * - `cwd: \".\"` —— phase0-spike S2 实测证明 Codex 会把 cwd 锁定为 plugin cache\n * 版本目录的绝对路径,作者写 `.` 即可(不需要也不应该写绝对路径)\n *\n * command 规范化规则(和 Claude Code 一致):\n * - 仅对 `rush@rush-marketplace` 的 `rush` server key + 非绝对路径 command 调用\n * `rushAiBinaryResolver`,把 command 换成绝对路径\n * - 其他 server key / 其他 plugin / 已经是绝对路径 → **透传不改**\n * - normalize 后**新建**对象,不 mutate 入参(便于测试断言)\n */\n\nimport { isAbsolute } from 'node:path';\nimport type { McpServerConfig, PluginManifest, PluginRef } from '../types.js';\n\n/** rush-ai 自己发布的插件 identifier(和 plugins/resolver.ts 共用约定) */\nexport const RUSH_AI_PLUGIN_NAME = 'rush' as const;\nexport const RUSH_AI_MARKETPLACE_NAME = 'rush-marketplace' as const;\n/** rush plugin 内 MCP server 的 key —— 只对这个 key 做 command normalize */\nexport const RUSH_MCP_SERVER_KEY = 'rush' as const;\n\n/**\n * 由 ResolvedPlugin.manifest 的 MCP 声明生成 `.mcp.json` 内容(JSON 对象)。\n *\n * @param ref 插件引用(用于 normalize 触发条件判断)\n * @param manifest plugin.json 解析结果\n * @param rushAiBinaryResolver 仅对 rush-ai 自身插件生效的 binary 解析器\n *\n * @returns `.mcp.json` 的顶层对象;若 plugin 无 MCP 声明则返回 null\n */\nexport function buildCodexMcpContent(\n ref: PluginRef,\n manifest: PluginManifest,\n rushAiBinaryResolver?: () => string | undefined\n): { mcpServers: Record<string, McpServerConfig> } | null {\n const mcp = manifest.mcpServers;\n if (mcp === undefined || mcp === null) return null;\n\n // Codex 这边我们只支持**对象形态**的 mcpServers——作者如果直接在 plugin.json 里\n // 写了 `\"mcpServers\": \"./.mcp.json\"` 字符串,说明他们自己已经备好 .mcp.json,\n // rush-ai Installer 不应覆盖(此时应由 Installer 层直接 copy 原 .mcp.json 文件)。\n // 这里只处理对象形态 → 生成 .mcp.json。\n if (typeof mcp === 'string') return null;\n\n if (Object.keys(mcp).length === 0) return null;\n\n const normalized = normalizeMcpServers(ref, mcp, rushAiBinaryResolver);\n return { mcpServers: normalized };\n}\n\n/**\n * 返回 `.mcp.json` 里需要注入 cwd 字段的 server config(cwd=\".\" 如果没写)。\n *\n * phase0-spike 实测结论(spec §2.4 S2):Codex 会把 cwd 解析为 plugin cache\n * 版本目录的绝对路径,所以作者写 `\"cwd\": \".\"` 即可。如果原 manifest 没写 cwd\n * 我们显式补 `\".\"`——这样下游调试 `.mcp.json` 时明显看出\"cwd 是有意相对 plugin\n * 版本目录\"而非遗漏。\n */\nfunction normalizeMcpServers(\n ref: PluginRef,\n servers: Record<string, McpServerConfig>,\n rushAiBinaryResolver?: () => string | undefined\n): Record<string, McpServerConfig> {\n const isRushAiPlugin =\n ref.name === RUSH_AI_PLUGIN_NAME &&\n ref.marketplace === RUSH_AI_MARKETPLACE_NAME;\n\n const result: Record<string, McpServerConfig> = {};\n for (const [serverKey, config] of Object.entries(servers)) {\n let finalCommand = config.command;\n\n // rush-ai 自身插件 + rush server key + 非绝对 command → 解析绝对路径\n if (\n isRushAiPlugin &&\n serverKey === RUSH_MCP_SERVER_KEY &&\n typeof finalCommand === 'string' &&\n finalCommand.length > 0 &&\n !isAbsolute(finalCommand)\n ) {\n const resolved = rushAiBinaryResolver?.();\n if (resolved && isAbsolute(resolved)) {\n finalCommand = resolved;\n }\n // 如果 resolver 返回 undefined 或相对路径 → 保持原值不改(对齐 resolver 层\n // \"不挂 installer\" 的保守策略;若确实 command 跑不通,让运行时报错)\n }\n\n // 组装:显式加 cwd: \".\"(如果没写)\n const next: McpServerConfig = {\n ...config,\n command: finalCommand,\n cwd: config.cwd ?? '.',\n };\n result[serverKey] = next;\n }\n return result;\n}\n\n/**\n * 从 `.mcp.json` 内容提取 server key 清单(供 `InstallResult.artifacts.mcpKeys` 使用)。\n */\nexport function listMcpKeys(\n content: { mcpServers: Record<string, unknown> } | null\n): string[] {\n if (!content) return [];\n return Object.keys(content.mcpServers).sort();\n}\n\n/**\n * 序列化 `.mcp.json` 对象为写盘用字符串(稳定格式:2-space 缩进 + 尾换行)。\n *\n * 和 Installer 磁盘约定一致:后续 `diff` / `cat` 得到稳定输出。\n */\nexport function serializeMcpJson(content: {\n mcpServers: Record<string, McpServerConfig>;\n}): string {\n return `${JSON.stringify(content, null, 2)}\\n`;\n}\n","/**\n * Codex Installer 路径 helper。\n *\n * Source of truth: `specs/plugin-schemas.md` §2.1 目录布局。\n *\n * 磁盘布局(全部以 `home` 注入目录为根,生产默认 `os.homedir()`):\n * ```\n * <home>/.codex/\n * ├── config.toml ← TOML 合并文件\n * └── plugins/\n * ├── marketplaces/\n * │ └── <marketplace>/\n * │ ├── .agents/plugins/marketplace.json ← 插件页 marketplace manifest\n * │ └── plugins/<plugin>/... ← 最新 plugin 镜像\n * └── cache/\n * └── <marketplace>/<plugin>/<version>/... ← 运行时 cache\n * ```\n *\n * **测试铁律**:所有单测必须通过 `home` 注入 `os.tmpdir()` 下的临时目录,\n * **禁止**写用户真实 `~/.codex/`。\n */\n\nimport { resolve as pathResolve } from 'node:path';\nimport type { PluginRef } from '../types.js';\n\n/**\n * `<home>/.codex/` 目录。`detect()` 用此判断 Codex 是否安装。\n */\nexport function codexHomeDir(home: string): string {\n return pathResolve(home, '.codex');\n}\n\n/**\n * `<home>/.codex/config.toml` —— 合并 TOML 文件。\n */\nexport function codexConfigTomlPath(home: string): string {\n return pathResolve(codexHomeDir(home), 'config.toml');\n}\n\n/**\n * `<home>/.codex/plugins/cache/` —— plugin cache 根目录。\n */\nexport function codexPluginsCacheDir(home: string): string {\n return pathResolve(codexHomeDir(home), 'plugins', 'cache');\n}\n\n/**\n * `<home>/.codex/plugins/marketplaces/` —— Codex 插件页读取的 marketplace 根。\n */\nexport function codexPluginsMarketplacesDir(home: string): string {\n return pathResolve(codexHomeDir(home), 'plugins', 'marketplaces');\n}\n\n/**\n * 单个 Codex marketplace mirror 目录:\n * `<home>/.codex/plugins/marketplaces/<marketplace>/`。\n */\nexport function codexMarketplaceDir(home: string, marketplace: string): string {\n return pathResolve(codexPluginsMarketplacesDir(home), marketplace);\n}\n\n/**\n * Codex marketplace manifest:\n * `<marketplaceDir>/.agents/plugins/marketplace.json`。\n */\nexport function codexMarketplaceManifestPath(marketplaceDir: string): string {\n return pathResolve(marketplaceDir, '.agents', 'plugins', 'marketplace.json');\n}\n\n/**\n * Marketplace mirror 中单个 plugin 的目录:\n * `<marketplaceDir>/plugins/<plugin>/`。\n */\nexport function codexMarketplacePluginDir(\n marketplaceDir: string,\n pluginName: string\n): string {\n return pathResolve(marketplaceDir, 'plugins', pluginName);\n}\n\n/**\n * 单个 plugin 版本的 cache 目录:\n * `<home>/.codex/plugins/cache/<marketplace>/<plugin>/<version>/`。\n *\n * 对齐 spec §2.1:三段 marketplace/plugin/version 目录,和 Claude Code\n * cache layout 完全同构。\n */\nexport function codexPluginVersionDir(\n home: string,\n ref: PluginRef,\n version: string\n): string {\n return pathResolve(\n codexPluginsCacheDir(home),\n ref.marketplace,\n ref.name,\n version\n );\n}\n\n/**\n * plugin cache 版本目录下的 `.codex-plugin/plugin.json`(Codex 原生约定)。\n */\nexport function codexPluginManifestPath(versionDir: string): string {\n return pathResolve(versionDir, '.codex-plugin', 'plugin.json');\n}\n\n/**\n * plugin cache 版本目录下的外部 `.mcp.json`(Codex 特有——spec §2.4)。\n */\nexport function codexPluginMcpPath(versionDir: string): string {\n return pathResolve(versionDir, '.mcp.json');\n}\n\n/**\n * plugin cache 版本目录下的 `skills/` 子目录。\n */\nexport function codexPluginSkillsDir(versionDir: string): string {\n return pathResolve(versionDir, 'skills');\n}\n\n/**\n * 生成 config.toml 备份路径——`config.toml.bak.<YYYYMMDD-HHMMSS>`。\n *\n * 时间戳使用 UTC 以便多机协作时备份路径可比较(spec §2.2 不要求 local time)。\n */\nexport function codexConfigBackupPath(home: string, now: Date): string {\n const ts = formatBackupTimestamp(now);\n return `${codexConfigTomlPath(home)}.bak.${ts}`;\n}\n\n/**\n * 格式化备份时间戳 `YYYYMMDD-HHMMSS`(UTC)。\n *\n * 导出便于测试。\n */\nexport function formatBackupTimestamp(date: Date): string {\n const pad = (n: number) => String(n).padStart(2, '0');\n const yyyy = date.getUTCFullYear();\n const mm = pad(date.getUTCMonth() + 1);\n const dd = pad(date.getUTCDate());\n const hh = pad(date.getUTCHours());\n const mi = pad(date.getUTCMinutes());\n const ss = pad(date.getUTCSeconds());\n return `${yyyy}${mm}${dd}-${hh}${mi}${ss}`;\n}\n\n/**\n * config.toml 里 marketplace section 的 header key(spec §2.2)。\n * 例如 `\"marketplaces.rush-marketplace\"`。\n */\nexport function marketplaceSectionKey(marketplaceName: string): string {\n return `marketplaces.${marketplaceName}`;\n}\n\n/**\n * config.toml 里 plugin section 的 header key(spec §2.2)。\n * 例如 `plugins.\"rush@rush-marketplace\"`(带引号——@iarna/toml 自动处理)。\n *\n * 返回\"逻辑 key\"——插入 TOML 对象时直接作为嵌套 key 使用,stringify 时\n * @iarna/toml 会自动按 TOML 规范加引号。\n */\nexport function pluginSectionKey(ref: PluginRef): string {\n return `${ref.name}@${ref.marketplace}`;\n}\n","/**\n * `~/.codex/config.toml` 读-改-写(task-8 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §2.2。\n *\n * 关键规则(spec §2.2):\n * - 用 `@iarna/toml` parse / stringify(注释不保留——决策已定)\n * - 写前**必须**备份 `config.toml.bak.<YYYYMMDD-HHMMSS>`\n * - 写前读 mtime,写完后再 stat 确认(并发保护)\n * - 保留用户 sections:`[model_providers.*]` / `[projects.*]` / 顶层字段\n * `model` / `approval_policy` / `sandbox_mode` 等——全部通过 \"read-merge-write\" 保留\n * - 所有写入走 write `.tmp` → `rename` 原子替换\n * - `[plugins.\"<name>@<mkt>\"]` key 含 `@`,@iarna/toml 自动处理引号(已实测)\n *\n * 错误处理:\n * - 文件不存在 → 视为空 config(返回 `{}`)\n * - TOML 解析失败 → 抛 `CodexConfigTomlCorruptError`(CLI 层提示备份路径)\n * - mtime 冲突 → 抛 `CodexConfigTomlConflictError`(让用户重试)\n *\n * 命名约定:\n * - `CodexConfigToml` = 数据 shape(`JsonMap` 别名)\n * - `readCodexConfig` / `writeCodexConfig` = 顶层读写 API\n * - `backupCodexConfig` = 备份当前文件到 `<path>.bak.<ts>`\n * - `setMarketplaceSection` / `removeMarketplaceSection` / `setPluginEntry` / `removePluginEntry`\n * = 结构化的 section 操作函数(纯函数,接受 config → 返回新 config)\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { constants as fsConstants } from 'node:fs';\nimport {\n access,\n copyFile,\n mkdir,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport tomlModule from '@iarna/toml';\n\nconst { parse: tomlParse, stringify: tomlStringify } = tomlModule;\n\n// ---------------------------------------------------------------------------\n// 类型 & 错误\n// ---------------------------------------------------------------------------\n\n/**\n * config.toml 解析后的顶层对象。\n *\n * 不强约束 shape——@iarna/toml 返回的 `JsonMap` 即为本类型,保留用户任意自定义\n * sections / 顶层字段(spec §2.2 明确要求保留 `[model_providers.*]` 等)。\n */\n// @iarna/toml 的 JsonMap 不直接导出,这里用与 stringify 入参相同的 shape:\nexport type CodexConfigToml = Record<string, unknown>;\n\n/** 基类 —— CLI 层统一 `instanceof CodexConfigTomlError` catch。 */\nexport class CodexConfigTomlError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CodexConfigTomlError';\n }\n}\n\n/** TOML 解析失败 —— 指向备份路径供用户处理。 */\nexport class CodexConfigTomlCorruptError extends CodexConfigTomlError {\n constructor(\n public readonly filePath: string,\n public readonly cause: unknown\n ) {\n super(\n `Codex config.toml 解析失败(${filePath})。请备份后手工修复。原因:${String(\n (cause as Error | undefined)?.message ?? cause\n )}`\n );\n this.name = 'CodexConfigTomlCorruptError';\n }\n}\n\n/** 并发写冲突(mtime 在读-改-写期间被其他进程修改)。 */\nexport class CodexConfigTomlConflictError extends CodexConfigTomlError {\n constructor(public readonly filePath: string) {\n super(\n `Codex config.toml 并发写冲突:读取和写入之间 ${filePath} 被其他进程修改。请重试。`\n );\n this.name = 'CodexConfigTomlConflictError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// 读 / 写 / 备份\n// ---------------------------------------------------------------------------\n\n/**\n * 从磁盘读 config.toml + 记录 mtime(用于并发保护)。\n *\n * - 文件不存在 → 返回空配置 `{}`,`mtimeMs = null`\n * - 解析失败 → `CodexConfigTomlCorruptError`\n *\n * 返回的 `data` 是可 mutate 的副本(@iarna/toml 每次 parse 都新建对象)。\n */\nexport async function readCodexConfig(\n filePath: string\n): Promise<{ data: CodexConfigToml; mtimeMs: number | null }> {\n if (!(await pathExists(filePath))) {\n return { data: {}, mtimeMs: null };\n }\n\n const stats = await stat(filePath);\n const raw = await readFile(filePath, 'utf8');\n\n try {\n const parsed = tomlParse(raw);\n return { data: parsed as CodexConfigToml, mtimeMs: stats.mtimeMs };\n } catch (err) {\n throw new CodexConfigTomlCorruptError(filePath, err);\n }\n}\n\n/**\n * 备份当前 config.toml 到 `<path>.bak.<ts>`。\n *\n * - 源文件不存在 → no-op,返回 null(无需备份)\n * - 使用 `copyFile`(非 rename,保留原文件位置给后续 `writeCodexConfig` 覆盖)\n * - 若目标 `.bak.<ts>` 已存在(同秒多次 backup),追加 `.<uuid>` 避免覆盖\n *\n * @returns 备份文件绝对路径;源文件不存在时返回 null\n */\nexport async function backupCodexConfig(\n filePath: string,\n backupPath: string\n): Promise<string | null> {\n if (!(await pathExists(filePath))) {\n return null;\n }\n // 目标已存在 → 附加 uuid,不覆盖已有备份\n let finalBackup = backupPath;\n if (await pathExists(finalBackup)) {\n finalBackup = `${backupPath}.${randomUUID().slice(0, 8)}`;\n }\n await copyFile(filePath, finalBackup);\n return finalBackup;\n}\n\n/**\n * 原子写 config.toml(+ mtime 冲突检测)。\n *\n * 流程:\n * 1. 写前 `stat` 当前磁盘 mtime,与 `expectedMtimeMs` 比较\n * - `null` 意味着我们 load 时文件不存在;此时允许仅当现在依然不存在\n * - 值不等 → `CodexConfigTomlConflictError`(不 retry——spec §2.2 要求\n * \"写前读 mtime,写完后再 stat 确认\";retry 策略留给 Installer 层感知)\n * 2. stringify → write `.tmp` → rename 原子替换\n * 3. 写完后 stat 获取新 mtime 返回(方便 Installer 后续串联操作)\n *\n * **注意**:本函数不处理备份——备份由调用方显式调用 `backupCodexConfig`。\n * 解耦原因:installer.ts 需要在失败回滚时把备份 restore 回来,备份路径必须\n * 由上层管控、透传到 rollback。\n */\nexport async function writeCodexConfig(\n filePath: string,\n data: CodexConfigToml,\n expectedMtimeMs: number | null\n): Promise<{ mtimeMs: number }> {\n await assertNoMtimeDrift(filePath, expectedMtimeMs);\n\n const serialized = tomlStringify(data as Parameters<typeof tomlStringify>[0]);\n await atomicWrite(filePath, serialized);\n\n const afterStats = await stat(filePath);\n return { mtimeMs: afterStats.mtimeMs };\n}\n\n/**\n * 从备份文件恢复 config.toml(用于失败回滚)。\n *\n * - 备份路径为空字符串 / null / 不存在 → 说明 install 前没有原文件,直接删除当前\n * config.toml(如果存在)即可,让磁盘彻底回到\"install 前状态\"\n * - 备份存在 → copy 回去(原子替换)并删掉备份\n */\nexport async function restoreCodexConfigFromBackup(\n filePath: string,\n backupPath: string | null\n): Promise<void> {\n if (!backupPath || !(await pathExists(backupPath))) {\n // 没有备份 → install 前本无 config.toml,回滚 = 清掉当前文件(如果我们写过)\n if (await pathExists(filePath)) {\n await rm(filePath, { force: true });\n }\n return;\n }\n // 有备份 → 原子覆盖当前文件 + 删备份\n const raw = await readFile(backupPath, 'utf8');\n await atomicWrite(filePath, raw);\n await rm(backupPath, { force: true }).catch(() => {});\n}\n\n// ---------------------------------------------------------------------------\n// Section 操作(纯函数,不触磁盘)\n// ---------------------------------------------------------------------------\n\n/**\n * 在 config 上写入 / 更新 `[marketplaces.<name>]` section。\n *\n * - 若 `config.marketplaces` 缺失,自动创建 object\n * - 保留同 `marketplaces` 下其他 marketplace(只更新 `name` 这一个 key)\n *\n * **mutates** 传入对象(调用方已是内存副本,无外部副作用风险)。\n */\nexport function setMarketplaceSection(\n config: CodexConfigToml,\n name: string,\n entry: { last_updated: string; source_type: string; source: string }\n): void {\n const mkts = ensureObjectSection(config, 'marketplaces');\n mkts[name] = { ...entry };\n}\n\n/**\n * 移除 `[marketplaces.<name>]` section。若本就没有 → no-op。\n *\n * 若移除后 `marketplaces` 变空对象,**同步删除父级 `marketplaces` key**——\n * 否则 @iarna/toml stringify 出 `marketplaces = { }` 空 inline table,视觉\n * 和语义上都是污染(regression fix,回归测试 bug #5)。\n */\nexport function removeMarketplaceSection(\n config: CodexConfigToml,\n name: string\n): void {\n const mkts = config.marketplaces as Record<string, unknown> | undefined;\n if (!mkts || typeof mkts !== 'object') return;\n delete mkts[name];\n if (Object.keys(mkts).length === 0) {\n delete config.marketplaces;\n }\n}\n\n/**\n * 在 config 上写入 / 更新 `[plugins.\"<name>@<mkt>\"]` section。\n *\n * - key 带 `@`,stringify 时 @iarna/toml 自动加引号(已实测)\n * - 保留同 `plugins` 下其他 plugin 条目\n * - rush-ai 始终写 `enabled = true`(spec §2.2 约定)\n */\nexport function setPluginEntry(\n config: CodexConfigToml,\n key: string,\n entry: { enabled: boolean }\n): void {\n const plugins = ensureObjectSection(config, 'plugins');\n plugins[key] = { ...entry };\n}\n\n/**\n * 移除 `[plugins.\"<name>@<mkt>\"]` section。若本就没有 → no-op。\n *\n * 若移除后 `plugins` 变空对象,同步删除父级 `plugins` key——避免留下\n * `plugins = { }` 空 inline table 污染 config.toml(regression fix,回归测试 bug #5)。\n */\nexport function removePluginEntry(config: CodexConfigToml, key: string): void {\n const plugins = config.plugins as Record<string, unknown> | undefined;\n if (!plugins || typeof plugins !== 'object') return;\n delete plugins[key];\n if (Object.keys(plugins).length === 0) {\n delete config.plugins;\n }\n}\n\n/**\n * 判断 `[marketplaces.<name>]` 下是否至少还有一个 plugin 使用。\n *\n * Codex Installer uninstall 时用:若某 marketplace 已经没有任何 plugin 安装\n * (说明 rush-ai 不再需要这个 marketplace 注册表项)→ 才允许删该 marketplace section。\n *\n * 实现:扫所有 `[plugins.\"<name>@<mkt>\"]` key,找出后缀 `@<marketplace>` 匹配的条目。\n */\nexport function marketplaceHasOtherPlugins(\n config: CodexConfigToml,\n marketplaceName: string,\n excludedPluginKey: string\n): boolean {\n const plugins = config.plugins as Record<string, unknown> | undefined;\n if (!plugins || typeof plugins !== 'object') return false;\n const suffix = `@${marketplaceName}`;\n for (const key of Object.keys(plugins)) {\n if (key === excludedPluginKey) continue;\n if (key.endsWith(suffix)) return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p, fsConstants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function atomicWrite(filePath: string, content: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n const tmp = `${filePath}.${randomUUID()}.tmp`;\n try {\n await writeFile(tmp, content, { encoding: 'utf8', flag: 'w' });\n await rename(tmp, filePath);\n } catch (err) {\n await rm(tmp, { force: true }).catch(() => {});\n throw err;\n }\n}\n\nasync function assertNoMtimeDrift(\n filePath: string,\n expectedMtimeMs: number | null\n): Promise<void> {\n if (expectedMtimeMs === null) {\n // 我们读取时文件不存在;写入前再看一次,仍不存在才允许\"首次写\"路径\n if (await pathExists(filePath)) {\n throw new CodexConfigTomlConflictError(filePath);\n }\n return;\n }\n const current = await stat(filePath).catch(() => null);\n if (current === null) {\n // 读取时存在,现在消失 → 冲突\n throw new CodexConfigTomlConflictError(filePath);\n }\n if (current.mtimeMs !== expectedMtimeMs) {\n throw new CodexConfigTomlConflictError(filePath);\n }\n}\n\n/**\n * 确保 `config[key]` 是 object(不存在时初始化空 object),返回可 mutate 的引用。\n *\n * 若已存在但不是 object(例如用户手写成了数组或字符串)→ 抛 `CodexConfigTomlError`,\n * 提示不会破坏用户内容(对齐 spec §5.3 \"TOML 解析失败 → 报错\")。\n */\nfunction ensureObjectSection(\n config: CodexConfigToml,\n key: string\n): Record<string, unknown> {\n const existing = config[key];\n if (existing === undefined) {\n const fresh: Record<string, unknown> = {};\n config[key] = fresh;\n return fresh;\n }\n if (\n typeof existing !== 'object' ||\n existing === null ||\n Array.isArray(existing)\n ) {\n throw new CodexConfigTomlError(\n `config.toml 顶层 '${key}' 已存在但不是 object(type=${Array.isArray(existing) ? 'array' : typeof existing})。请手工备份并整理该字段后重试。`\n );\n }\n return existing as Record<string, unknown>;\n}\n","/**\n * Cursor Installer 主类(task-7 产物)。\n *\n * Source of truth:\n * - `specs/plugin-schemas.md` §3(Cursor 摊开适配格式)\n * - `specs/installer-interface.md` §1-§4(Installer 接口契约 / 原子性)\n *\n * 核心设计:\n * - Cursor 没有原生 plugin 容器:skills 走**双写**(skill dir + bridge .mdc rule)\n * - `commands` / `hooks` 能力跳过(Cursor 不支持),`skipped` 字段标明\n * - 冲突检测靠 **marker + registry 双重保险**(见 marker.ts / skills.ts)\n * - 写入失败**完全回滚**(恢复所有已变更文件到写入前状态,返回 `failed`)\n * - 所有写入路径如实回报到 `InstallResult.artifacts.files`(包括被修改的 mcp.json)\n *\n * 测试铁律:constructor 接受 `{ home }` 注入,默认 `os.homedir()`。\n * 所有单测必须注入 tmp HOME,**禁止**写用户真实 `~/.cursor/`。\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { constants as fsConstants } from 'node:fs';\nimport {\n access,\n cp,\n mkdir,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, relative, resolve } from 'node:path';\nimport { RushRegistryStore } from '../registry.js';\nimport type {\n InstalledPlugin,\n InstallOptions,\n InstallResult,\n McpServerConfig,\n PluginCapability,\n PluginInstaller,\n PluginRef,\n ResolvedPlugin,\n UninstallOptions,\n UninstallResult,\n} from '../types.js';\nimport { hasRushAiMarker } from './marker.js';\nimport {\n CursorMcpConflictError,\n mergeMcpServers,\n readCursorMcpJson,\n readCursorMcpJsonWithMtime,\n removeMcpServers,\n writeCursorMcpJson,\n} from './mcp.js';\nimport {\n bridgeSkillFileReference,\n cursorDir,\n cursorMcpJsonPath,\n cursorRuleMdcPath,\n cursorSkillDir,\n} from './paths.js';\nimport { parseMarkdown, renderBridgeRule, renderMdc } from './rules.js';\nimport { resolveSkillDescription } from './skills.js';\n\n// ---------------------------------------------------------------------------\n// 本 Installer 明确跳过的能力(Cursor 不支持 commands / hooks)\n// ---------------------------------------------------------------------------\n\n/**\n * Cursor 不支持的能力(spec §3.5)。\n *\n * 注意:`rules` 能力 Cursor 是支持的(走 .md→.mdc 转换);`skills` 也支持(双写)。\n * 只有 commands / hooks 是硬性不支持。\n */\nconst CURSOR_UNSUPPORTED_CAPABILITIES: readonly PluginCapability[] = [\n 'commands',\n 'hooks',\n] as const;\n\n// ---------------------------------------------------------------------------\n// Installer options\n// ---------------------------------------------------------------------------\n\nexport interface CursorInstallerOptions {\n /**\n * 注入的 HOME 目录。测试里必须传临时路径;生产代码传 `undefined`,默认 `os.homedir()`。\n */\n home?: string;\n}\n\n// ---------------------------------------------------------------------------\n// 回滚 ledger(install 中途失败时用它精确还原)\n// ---------------------------------------------------------------------------\n\n/**\n * 一次 install 期间每次文件变更的记录。失败时按**逆序**重放 undo。\n *\n * 四类:\n * - `restore-file`:目标文件原本存在(pre-existing),需还原到原内容\n * - `remove-file`:目标文件原本不存在,失败后应该删除\n * - `remove-dir`:目标目录原本不存在,失败后应该递归删除\n * - `restore-dir-from-backup`:目标目录原本存在,已被 rename 到 backup 位置,\n * 失败时把 backup 挪回原位(先删新写的空位,再 rename 回来)\n */\ntype RollbackEntry =\n | { kind: 'restore-file'; path: string; originalContent: string }\n | { kind: 'remove-file'; path: string }\n | { kind: 'remove-dir'; path: string }\n | { kind: 'restore-dir-from-backup'; path: string; backupPath: string };\n\n/**\n * 执行回滚:按逆序处理每条 entry,best-effort 清理。\n *\n * 不抛错——回滚过程中的任何错误会被吞掉(日志在 installer 顶层以 `notes` 返回)。\n * 理由:已经进入错误路径,二次抛错只会掩盖原始失败原因。\n */\nasync function executeRollback(ledger: RollbackEntry[]): Promise<string[]> {\n const errors: string[] = [];\n for (let i = ledger.length - 1; i >= 0; i--) {\n const entry = ledger[i];\n try {\n if (entry.kind === 'restore-file') {\n // 走 atomicWriteFile:spec §2.1 \"所有文件写入走写 .tmp → rename\"——\n // 回滚路径也要遵循,避免崩溃留下半写产物\n await atomicWriteFile(entry.path, entry.originalContent);\n } else if (entry.kind === 'remove-file') {\n await rm(entry.path, { force: true });\n } else if (entry.kind === 'remove-dir') {\n await rm(entry.path, { recursive: true, force: true });\n } else {\n // restore-dir-from-backup:先清掉当前占位(copy 写进去的新目录),再把备份搬回\n await rm(entry.path, { recursive: true, force: true });\n await safeRename(entry.backupPath, entry.path);\n }\n } catch (err) {\n errors.push(\n `rollback ${entry.kind} ${entry.path} failed: ${(err as Error).message}`\n );\n }\n }\n return errors;\n}\n\n/**\n * write-temp-rename 原子写入(spec §2.1 \"所有文件写入走写 .tmp → rename\")。\n *\n * 实现细节:\n * - tmp 文件名用 uuid 避免并发冲突(即便同进程多次写同一路径也安全)\n * - mkdir 父目录(递归),对没创建过的路径容错\n * - 失败 best-effort 清 tmp,不覆盖原始错误\n */\nasync function atomicWriteFile(\n filePath: string,\n content: string\n): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n const tmp = `${filePath}.${randomUUID()}.tmp`;\n try {\n await writeFile(tmp, content, { encoding: 'utf8', flag: 'w' });\n await rename(tmp, filePath);\n } catch (err) {\n await rm(tmp, { force: true }).catch(() => {});\n throw err;\n }\n}\n\n/**\n * 读 mcp.json 当前 mtime(ms)。文件不存在返回 `null`。\n *\n * 用于 pre-write CAS —— 在 read-then-write 窗口里再 stat 一次,对比\"read 时\" vs\n * \"write 前\"的 mtime,检测 read-to-write race。\n */\nasync function getMcpMtimeMs(filePath: string): Promise<number | null> {\n try {\n const s = await stat(filePath);\n return s.mtimeMs;\n } catch {\n return null; // 不存在 / 权限错误都当作 \"null\"\n }\n}\n\n/**\n * 两个 mtime 是否\"实质相等\"。null 和 null 相等;null 和数字不等。\n *\n * 用 `Object.is` 而不是 `===` 是为了处理理论上的 `NaN`(虽然 fs.stat 不会返回 NaN)。\n */\nfunction mtimeEqual(a: number | null, b: number | null): boolean {\n if (a === null && b === null) return true;\n if (a === null || b === null) return false;\n return Object.is(a, b);\n}\n\n/**\n * 产出 mcp.json 的**规范化**序列化结果,用于 post-write CAS 内容比对。\n *\n * 必须与 `writeCursorMcpJson` 的实际落盘格式(`JSON.stringify(data, null, 2) + '\\n'`)\n * 完全一致——否则 round-trip 会因为格式差异(空白/尾换行)假冲突。\n *\n * 注:JSON.stringify 对 object 按 key 插入顺序输出,Node 保留插入顺序。\n * 合并策略(`mergeMcpServers`)先拷贝 current、再写 additions,顺序稳定 →\n * 同一 install 调用里 expected == actual 的前提成立。\n */\nfunction serializeMcpJsonForComparison(\n data: Parameters<typeof writeCursorMcpJson>[1]\n): string {\n return `${JSON.stringify(data, null, 2)}\\n`;\n}\n\n/**\n * 判断 `childPath` 是否严格位于 `parentDir` 下(spec §3 Cursor 摊开适配:所有产物路径\n * 必须在注入的 HOME 下的 `.cursor/`)。\n *\n * 防御 registry 被污染时 uninstall 删到 `~/.cursor/` 外。\n * - 允许 `childPath === parentDir`:false(只接受严格子路径)\n * - 符号链接:不做 realpath(避免 I/O + 自锁逻辑)。spec §2.1 \"只 catch 预期错误\",\n * 若用户主动把 `~/.cursor/skills/<x>` symlink 到外部,他们已默认承担该风险。\n * 我们只防 registry.files 含有 `/etc/passwd` 这种明显越界的绝对路径\n */\nfunction isPathWithin(parentDir: string, childPath: string): boolean {\n const relPath = relative(parentDir, childPath);\n if (relPath.length === 0) return false; // 相同路径不算\"在内部\"\n if (relPath.startsWith('..')) return false;\n // Windows: path.relative 在跨盘符时返回绝对路径\n if (/^([a-zA-Z]:)?[\\\\/]/.test(relPath)) return false;\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// 冲突错误 —— 让主流程能用 instanceof 区分\"应该 failed 的冲突\" vs \"意外异常\"\n// ---------------------------------------------------------------------------\n\nclass CursorInstallConflictError extends Error {\n constructor(\n public readonly conflictPath: string,\n message: string\n ) {\n super(message);\n this.name = 'CursorInstallConflictError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Installer 主类\n// ---------------------------------------------------------------------------\n\nexport class CursorInstaller implements PluginInstaller {\n readonly target = 'cursor' as const;\n\n private readonly home: string;\n\n constructor(options: CursorInstallerOptions = {}) {\n this.home = options.home ?? homedir();\n }\n\n // -------------------------------------------------------------------------\n // PluginInstaller 接口\n // -------------------------------------------------------------------------\n\n /**\n * IDE 是否装在这台机器上:`~/.cursor/` 目录存在即可(spec §3 detect)。\n *\n * 必须幂等 + 无副作用。\n */\n async detect(): Promise<boolean> {\n return dirExists(cursorDir(this.home));\n }\n\n /**\n * 通过 rush-ai registry 查询该插件是否已装(Cursor 没有原生清单,完全靠 registry)。\n */\n async isInstalled(ref: PluginRef): Promise<boolean> {\n const store = await RushRegistryStore.load({ home: this.home });\n const entry = store.get(ref);\n return Boolean(entry?.targets?.cursor);\n }\n\n /**\n * 列出当前 target 已装的插件。完全靠 rush-ai registry(Cursor 没自己的清单)。\n */\n async list(): Promise<InstalledPlugin[]> {\n const store = await RushRegistryStore.load({ home: this.home });\n const out: InstalledPlugin[] = [];\n for (const [ref, entry] of store.list()) {\n const cursorEntry = entry.targets?.cursor;\n if (!cursorEntry) continue;\n out.push({\n ref,\n version: cursorEntry.version,\n installedAt: cursorEntry.installedAt,\n targets: ['cursor'],\n });\n }\n return out;\n }\n\n /**\n * 装一个插件。\n *\n * 流程(spec §2.1 行为契约):\n * 1. `detect()` = false → 立即返回 `skipped`\n * 2. dryRun → 计算 \"会写哪些文件 / mcpKeys\",不落盘\n * 3. 真装:按 skills → rules → mcp 顺序写入,记 ledger\n * 4. 冲突 / 写失败 → 按 ledger 逆序回滚,返回 `failed`\n * 5. 成功 → 返回 `ok`,artifacts.files 列出所有被写/修改的绝对路径\n *\n * **原子性**:任何单步失败都会触发 ledger 回滚,磁盘回到 install 前状态。\n */\n async install(\n plugin: ResolvedPlugin,\n opts: InstallOptions = {}\n ): Promise<InstallResult> {\n // 1. detect 短路\n if (!(await this.detect())) {\n return skippedResult();\n }\n\n const { included, skipped } = partitionCapabilities(plugin.capabilities);\n const force = opts.force === true;\n const dryRun = opts.dryRun === true;\n\n // 2. 同版本重复 install 默认 no-op(spec §2.1 \"同版本重复 install:默认 no-op\n // status=ok,artifacts 为空数组,`opts.force=true` 才覆盖\")\n //\n // 判定依据:rush-ai registry.cursor 条目里的 version 等于 plugin.version。\n // Cursor 没有原生清单,靠 registry 独撑。\n //\n // dryRun 仍按 plan 走——调用者可能想看\"如果没装会写什么\"。\n if (!force && !dryRun) {\n const store = await RushRegistryStore.load({ home: this.home });\n const existing = store.get(plugin.ref)?.targets?.cursor;\n if (existing && existing.version === plugin.version) {\n return {\n target: 'cursor',\n status: 'ok',\n included,\n skipped,\n artifacts: { files: [], mcpKeys: [] },\n notes: [\n `rush-ai registry 已记录同版本(${plugin.version})的 cursor 安装,默认 no-op(使用 --force 覆盖)`,\n ],\n };\n }\n }\n\n // 3. 真正写入——记 rollback ledger\n //\n // `planInstall` 本身可能抛 `CursorInstallConflictError`(marker 冲突 / skill 目录\n // 占用),必须在 try/catch 范围内以便统一包装成 status=failed;plan 阶段不会\n // 写磁盘,所以无需 rollback。\n const ledger: RollbackEntry[] = [];\n const writtenFiles: string[] = [];\n const writtenMcpKeys: string[] = [];\n\n try {\n // 预计算:skills / rules / mcp 各自要写哪些 artifact\n const plan = await this.planInstall(plugin, { force });\n\n // dry-run:直接返回计划(不写磁盘)\n if (dryRun) {\n return {\n target: 'cursor',\n status: 'ok',\n included,\n skipped,\n artifacts: {\n files: plan.plannedFiles,\n mcpKeys: plan.plannedMcpKeys,\n },\n notes: plan.conflictNotes,\n };\n }\n // skills\n for (const s of plan.skillsToWrite) {\n await this.writeSkillDir({\n plugin,\n srcDir: s.srcDir,\n destDir: s.destDir,\n ledger,\n force,\n });\n writtenFiles.push(s.destDir);\n\n await this.writeMdcFile({\n path: s.bridgeRulePath,\n content: s.bridgeRuleContent,\n ledger,\n force,\n });\n writtenFiles.push(s.bridgeRulePath);\n }\n\n // rules\n for (const r of plan.rulesToWrite) {\n await this.writeMdcFile({\n path: r.destPath,\n content: r.content,\n ledger,\n force,\n });\n writtenFiles.push(r.destPath);\n }\n\n // mcp.json\n if (plan.mcpAdditions && Object.keys(plan.mcpAdditions).length > 0) {\n const mcpPath = cursorMcpJsonPath(this.home);\n await this.writeMcpMerge({\n mcpPath,\n additions: plan.mcpAdditions,\n ledger,\n });\n writtenFiles.push(mcpPath);\n writtenMcpKeys.push(...Object.keys(plan.mcpAdditions));\n }\n\n return {\n target: 'cursor',\n status: 'ok',\n included,\n skipped,\n artifacts: {\n files: writtenFiles,\n mcpKeys: writtenMcpKeys,\n },\n notes: plan.conflictNotes,\n };\n } catch (err) {\n const rollbackErrors = await executeRollback(ledger);\n const errorMsg =\n err instanceof CursorInstallConflictError\n ? `冲突:${err.message}`\n : `写入失败:${(err as Error).message}`;\n return {\n target: 'cursor',\n status: 'failed',\n included,\n skipped,\n artifacts: { files: [], mcpKeys: [] },\n errors: [errorMsg, ...rollbackErrors],\n };\n }\n }\n\n /**\n * 卸载一个插件。\n *\n * 流程(spec §3.6):\n * 1. detect()=false → skipped\n * 2. 未装(registry 无记录)→ ok no-op\n * 3. 按 registry.cursor.files 逐个处理:\n * - skill dir → 递归删\n * - `.mdc` → 读 marker 确认是 rush-ai 产物再删;用户重写过则跳过 + warn\n * 4. 从 mcp.json 移除 registry.cursor.mcpKeys 里的 keys(写回 mcp.json)\n * 5. dryRun → 只返回 artifacts,不真删\n */\n async uninstall(\n ref: PluginRef,\n opts: UninstallOptions = {}\n ): Promise<UninstallResult> {\n if (!(await this.detect())) {\n return {\n target: 'cursor',\n status: 'skipped',\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n\n const store = await RushRegistryStore.load({ home: this.home });\n const entry = store.get(ref);\n const cursorEntry = entry?.targets?.cursor;\n if (!cursorEntry) {\n return {\n target: 'cursor',\n status: 'ok',\n artifacts: { files: [], mcpKeys: [] },\n };\n }\n\n const dryRun = opts.dryRun === true;\n const removedFiles: string[] = [];\n const removedMcpKeys: string[] = [];\n const warnings: string[] = [];\n\n // `mcp.json` 需要用\"删 key 而非删文件\"语义,单独走下面的 mcpKeys 分支处理,\n // 这里从 files 清单里剔除,避免误把整个 mcp.json 当作 artifact 删掉\n // (artifacts.files 里包含 mcp.json 路径是 spec §1.4 的要求:回报所有被\n // 修改的路径以便 registry 记账,但这不意味着 uninstall 就直接 rm 它)。\n const mcpPath = cursorMcpJsonPath(this.home);\n const cursorRootDir = cursorDir(this.home);\n\n // 1. 文件:skill dir / `.mdc`\n for (const filePath of cursorEntry.files ?? []) {\n if (filePath === mcpPath) continue; // mcp.json 走 mcpKeys 分支\n\n // 防御 registry 被污染:registry.files 里的绝对路径必须严格位于\n // 注入 HOME 下的 `~/.cursor/` 目录内。超出范围 → 跳过 + warn,\n // 绝不删。即便 attacker 在 registry.json 里塞 /etc/passwd 也安全。\n if (!isPathWithin(cursorRootDir, filePath)) {\n warnings.push(\n `跳过 ${filePath}:registry 记录的路径越出 ~/.cursor/ 作用域,疑似脏数据`\n );\n continue;\n }\n\n const info = await classifyArtifactPath(filePath);\n if (info === 'missing') continue;\n\n if (info === 'skill-dir') {\n // skill 目录:直接删(registry 记录即授权——spec §3.3 \"是 rush-ai 装的 → 覆盖\"\n // 的反向操作等价于\"删除\")。\n if (!dryRun) {\n await rm(filePath, { recursive: true, force: true });\n }\n removedFiles.push(filePath);\n continue;\n }\n\n if (info === 'mdc-file') {\n // .mdc:marker 二次确认\n const content = await readFile(filePath, 'utf8').catch(() => '');\n if (!hasRushAiMarker(content)) {\n warnings.push(\n `${filePath} 已被修改(marker 丢失),跳过不动——请用户手动处理`\n );\n continue;\n }\n if (!dryRun) {\n await rm(filePath, { force: true });\n }\n removedFiles.push(filePath);\n continue;\n }\n\n // 其他文件(理论上不应有,防御性处理)\n if (!dryRun) {\n await rm(filePath, { force: true });\n }\n removedFiles.push(filePath);\n }\n\n // 2. mcp.json:移除 registry.cursor.mcpKeys\n const mcpKeys = cursorEntry.mcpKeys ?? [];\n if (mcpKeys.length > 0) {\n const current = await readCursorMcpJson(mcpPath);\n const keysActuallyPresent = mcpKeys.filter(\n (k) => current.mcpServers?.[k] !== undefined\n );\n if (keysActuallyPresent.length > 0) {\n const next = removeMcpServers(current, keysActuallyPresent);\n if (!dryRun) {\n await writeCursorMcpJson(mcpPath, next);\n }\n removedMcpKeys.push(...keysActuallyPresent);\n }\n }\n\n return {\n target: 'cursor',\n status: 'ok',\n artifacts: {\n files: removedFiles,\n mcpKeys: removedMcpKeys,\n },\n ...(warnings.length > 0 ? { errors: warnings } : {}),\n };\n }\n\n // -------------------------------------------------------------------------\n // 内部:install 计划计算\n // -------------------------------------------------------------------------\n\n /**\n * 扫描源 plugin,计算要写的 skill / rule / mcp 列表。\n *\n * 冲突检测在此阶段完成:\n * - `.mdc` 目标已存在 + 无 marker → 抛 `CursorInstallConflictError`\n * - skill 目录已存在 + 未在 registry 里记录 + 未被 force → 抛 `CursorInstallConflictError`\n */\n private async planInstall(\n plugin: ResolvedPlugin,\n args: { force: boolean }\n ): Promise<InstallPlan> {\n const plannedFiles: string[] = [];\n const plannedMcpKeys: string[] = [];\n const skillsToWrite: SkillWritePlan[] = [];\n const rulesToWrite: RuleWritePlan[] = [];\n const conflictNotes: string[] = [];\n\n // 从 registry 查本插件历史,用于\"自己装的 skill 覆盖自己\"判定\n const registryStore = await RushRegistryStore.load({ home: this.home });\n const prevEntry = registryStore.get(plugin.ref);\n const prevFiles = new Set(prevEntry?.targets?.cursor?.files ?? []);\n\n // skills\n if (plugin.capabilities.includes('skills')) {\n const skillsSourceDir = resolve(plugin.sourceDir, 'skills');\n const skillDirs = await listSkillDirs(skillsSourceDir);\n for (const skillName of skillDirs) {\n const srcDir = resolve(skillsSourceDir, skillName);\n const destDir = cursorSkillDir(this.home, skillName);\n const bridgeRulePath = cursorRuleMdcPath(this.home, skillName);\n\n // 冲突:skill 目录存在\n if (await dirExists(destDir)) {\n const isOurs = prevFiles.has(destDir) || args.force;\n if (!isOurs) {\n throw new CursorInstallConflictError(\n destDir,\n `skill 目录已存在且非 rush-ai 装(${destDir})——请先备份或删除`\n );\n }\n }\n\n // 冲突:bridge rule 已存在且无 marker\n await assertMdcWritable(bridgeRulePath, args.force);\n\n const description = await resolveSkillDescription({\n skillSourceDir: srcDir,\n skillName,\n manifest: plugin.manifest,\n });\n const bridgeRuleContent = renderBridgeRule({\n description,\n skillFileReference: bridgeSkillFileReference(skillName),\n });\n\n skillsToWrite.push({\n srcDir,\n destDir,\n bridgeRulePath,\n bridgeRuleContent,\n });\n plannedFiles.push(destDir, bridgeRulePath);\n }\n }\n\n // rules\n if (plugin.capabilities.includes('rules')) {\n const rulesSourceDir = resolve(plugin.sourceDir, 'rules');\n const rulesFiles = await listRuleMdFiles(rulesSourceDir);\n for (const ruleFile of rulesFiles) {\n const srcPath = resolve(rulesSourceDir, ruleFile);\n const ruleName = ruleFile.replace(/\\.md$/i, '');\n const destPath = cursorRuleMdcPath(this.home, ruleName);\n await assertMdcWritable(destPath, args.force);\n\n // skill 双写也会占用 `<name>.mdc`——如果 plugin 同时有 skill 和 rule\n // 撞名,我们保守处理:让 plan 顺序里 skill 先写,rule 会检测到 marker\n // 后允许覆盖(但会产生 notes 让用户知情)\n if (\n skillsToWrite.some((s) => s.bridgeRulePath === destPath) &&\n !args.force\n ) {\n conflictNotes.push(\n `rule \"${ruleName}\" 与 skill \"${ruleName}\" 的 bridge rule 同名,rule 内容会覆盖 bridge rule`\n );\n }\n\n const src = await readFile(srcPath, 'utf8');\n const parsed = parseMarkdown(src);\n const content = renderMdc(parsed, {\n descriptionFallback: plugin.manifest.description ?? ruleName,\n });\n rulesToWrite.push({ srcPath, destPath, content });\n plannedFiles.push(destPath);\n }\n }\n\n // mcp\n const mcpAdditions = extractMcpAdditions(plugin);\n if (mcpAdditions && Object.keys(mcpAdditions).length > 0) {\n plannedFiles.push(cursorMcpJsonPath(this.home));\n plannedMcpKeys.push(...Object.keys(mcpAdditions));\n }\n\n return {\n plannedFiles,\n plannedMcpKeys,\n skillsToWrite,\n rulesToWrite,\n mcpAdditions: mcpAdditions ?? {},\n conflictNotes,\n };\n }\n\n // -------------------------------------------------------------------------\n // 内部:各写入 primitive(都带 rollback ledger 记账)\n // -------------------------------------------------------------------------\n\n private async writeSkillDir(args: {\n plugin: ResolvedPlugin;\n srcDir: string;\n destDir: string;\n ledger: RollbackEntry[];\n force: boolean;\n }): Promise<void> {\n const preExisted = await dirExists(args.destDir);\n await mkdir(dirname(args.destDir), { recursive: true });\n if (preExisted) {\n // 覆盖场景(registry 授权 / force):先把旧目录整体 rename 到 backup,\n // 再把新内容 copy 到原位置。失败时把 backup 挪回原位。\n // 这样回滚无需深度比较 / 重新写入旧文件内容。\n const backup = `${args.destDir}.${process.pid}.${Date.now()}.bak`;\n await safeRename(args.destDir, backup);\n args.ledger.push({\n kind: 'restore-dir-from-backup',\n path: args.destDir,\n backupPath: backup,\n });\n } else {\n // 新建场景:失败时递归删除即可(目录原本不存在)\n args.ledger.push({ kind: 'remove-dir', path: args.destDir });\n }\n await cp(args.srcDir, args.destDir, { recursive: true });\n }\n\n private async writeMdcFile(args: {\n path: string;\n content: string;\n ledger: RollbackEntry[];\n force: boolean;\n }): Promise<void> {\n const preExisted = await fileExists(args.path);\n if (preExisted) {\n const original = await readFile(args.path, 'utf8');\n args.ledger.push({\n kind: 'restore-file',\n path: args.path,\n originalContent: original,\n });\n } else {\n args.ledger.push({ kind: 'remove-file', path: args.path });\n }\n // spec §2.1 \"所有文件写入走写 .tmp → rename\"\n await atomicWriteFile(args.path, args.content);\n }\n\n private async writeMcpMerge(args: {\n mcpPath: string;\n additions: Record<string, McpServerConfig>;\n ledger: RollbackEntry[];\n }): Promise<void> {\n // 并发保护(spec §3.2 没明说并发,但对齐 registry.ts §3.3 的 mtime retry 思路):\n // read-modify-write 中途若文件 mtime 变更(另一 rush-ai 进程或用户手改) →\n // 重试一次:reload 最新磁盘状态,在此基础上重新 merge 本次 additions。\n // 再次冲突则抛 `CursorMcpConflictError`,由上层统一包成 status=failed + rollback。\n\n const preExisted = await fileExists(args.mcpPath);\n if (preExisted) {\n const original = await readFile(args.mcpPath, 'utf8');\n args.ledger.push({\n kind: 'restore-file',\n path: args.mcpPath,\n originalContent: original,\n });\n } else {\n args.ledger.push({ kind: 'remove-file', path: args.mcpPath });\n }\n\n await this.writeMcpMergeWithRetry(args, 0);\n }\n\n /**\n * mcp.json merge + mtime-based CAS(对齐 spec §3.2 + RushRegistryStore §3.3\n * \"read-modify-write 中途 mtime 变化 → retry 1 次\" 的思路)。\n *\n * CAS 语义(两步检测,同时覆盖 pre-read / post-write 两个时间点):\n *\n * 1. **pre-write 检测**(防 read-to-write race):\n * 在 `readCursorMcpJsonWithMtime` 之后、调用 `writeCursorMcpJson` 之前,再 stat 一次\n * mcp.json。若 mtime 已变 → 说明另一个 writer 在我们 \"读完 but 还没 write\" 的窗口\n * 里改了文件,我们的 merge base 已过期。触发 retry。\n *\n * (理论上 read-to-stat 之间仍有微小窗口,但比 read-to-write 小一个数量级——\n * 这是单文件无 fcntl 锁场景的最佳能力。)\n *\n * 2. **post-write 检测**(防 write-to-observe race 及 lost-update):\n * 写完后读回一次,比对磁盘序列化后的 content 是否与本次 `merged` 内容一致。\n * 若不一致 → 说明我们 `rename` 之后又有人 `rename` 上去覆盖了我们。触发 retry。\n * 这个断言等价于\"本次 additions 的 key **以及** 原来 current 里的 key 都还在\n * 且值正确\"——比\"只检查 additions.key 是否存在\"严格得多,可检测到\"他人覆盖导致\n * `existing` 丢失\"的 lost-update 场景。\n *\n * 3. 第 1 轮 retry 仍冲突 → 抛 `CursorMcpConflictError`,外层 try/catch 统一\n * 包成 `status: 'failed'` + rollback。\n *\n * `preMtimeMs === null`(文件原本不存在):\n * - pre-write 检测退化为\"仍然不存在\"(新文件被别人抢先 create 也算冲突)\n * - post-write 检测仍然生效(防他人紧接我们 create 之后再 rename 覆盖)\n */\n private async writeMcpMergeWithRetry(\n args: {\n mcpPath: string;\n additions: Record<string, McpServerConfig>;\n },\n attempt: number\n ): Promise<void> {\n const { data: current, mtimeMs: preMtimeMs } =\n await readCursorMcpJsonWithMtime(args.mcpPath);\n\n // 1. pre-write CAS:再次 stat 拿最新 mtime,对比 preMtimeMs\n const midMtimeMs = await getMcpMtimeMs(args.mcpPath);\n if (!mtimeEqual(preMtimeMs, midMtimeMs)) {\n if (attempt === 0) return this.writeMcpMergeWithRetry(args, 1);\n throw new CursorMcpConflictError(args.mcpPath);\n }\n\n const merged = mergeMcpServers(current, args.additions);\n const expectedSerialized = serializeMcpJsonForComparison(merged);\n await writeCursorMcpJson(args.mcpPath, merged);\n\n // 2. post-write CAS:读回磁盘,对比序列化后的内容\n const { data: post, mtimeMs: postMtimeMs } =\n await readCursorMcpJsonWithMtime(args.mcpPath);\n if (postMtimeMs === null) {\n // 被删了\n if (attempt === 0) return this.writeMcpMergeWithRetry(args, 1);\n throw new CursorMcpConflictError(args.mcpPath);\n }\n const actualSerialized = serializeMcpJsonForComparison(post);\n if (actualSerialized !== expectedSerialized) {\n // 我们写的内容被后续的 writer 覆盖了 → lost-update\n if (attempt === 0) return this.writeMcpMergeWithRetry(args, 1);\n throw new CursorMcpConflictError(args.mcpPath);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// 辅助函数\n// ---------------------------------------------------------------------------\n\n/** Cursor 不支持的能力从 included 里剥离到 skipped,其他保持 included */\nfunction partitionCapabilities(all: PluginCapability[]): {\n included: PluginCapability[];\n skipped: PluginCapability[];\n} {\n const included: PluginCapability[] = [];\n const skipped: PluginCapability[] = [];\n for (const cap of all) {\n if (CURSOR_UNSUPPORTED_CAPABILITIES.includes(cap)) {\n skipped.push(cap);\n } else {\n included.push(cap);\n }\n }\n return { included, skipped };\n}\n\nfunction skippedResult(): InstallResult {\n return {\n target: 'cursor',\n status: 'skipped',\n included: [],\n skipped: [],\n artifacts: { files: [], mcpKeys: [] },\n };\n}\n\nasync function assertMdcWritable(path: string, force: boolean): Promise<void> {\n if (!(await fileExists(path))) return;\n const raw = await readFile(path, 'utf8').catch(() => '');\n if (hasRushAiMarker(raw)) return; // 有 marker = rush-ai 自己的,允许覆盖\n if (force) return; // --force 显式覆盖用户产物(CLI 层 opt-in)\n throw new CursorInstallConflictError(\n path,\n `目标 .mdc 已存在且无 rush-ai marker(${path})——不覆盖用户手写 rule`\n );\n}\n\ninterface InstallPlan {\n plannedFiles: string[];\n plannedMcpKeys: string[];\n skillsToWrite: SkillWritePlan[];\n rulesToWrite: RuleWritePlan[];\n mcpAdditions: Record<string, McpServerConfig>;\n conflictNotes: string[];\n}\n\ninterface SkillWritePlan {\n srcDir: string;\n destDir: string;\n bridgeRulePath: string;\n bridgeRuleContent: string;\n}\n\ninterface RuleWritePlan {\n srcPath: string;\n destPath: string;\n content: string;\n}\n\n/**\n * 从 plugin.manifest.mcpServers 提取要合并进 mcp.json 的 stdio server 记录。\n *\n * Cursor 只接受对象形态(`Record<string, McpServerConfig>`)。若 manifest\n * 写的是字符串(Codex 外部文件引用),Cursor 无法消费——按 spec §3.2 处理:\n * 不写入(视为该 plugin 没声明 cursor 可用的 MCP),不报错。\n */\nfunction extractMcpAdditions(\n plugin: ResolvedPlugin\n): Record<string, McpServerConfig> | null {\n const m = plugin.manifest.mcpServers;\n if (m === undefined || m === null) return null;\n if (typeof m === 'string') return null; // Codex 外部引用,Cursor 不消费\n const out: Record<string, McpServerConfig> = {};\n for (const [key, cfg] of Object.entries(m)) {\n if (cfg && typeof cfg === 'object' && typeof cfg.command === 'string') {\n out[key] = cfg;\n }\n }\n return out;\n}\n\nasync function listSkillDirs(skillsDir: string): Promise<string[]> {\n if (!(await dirExists(skillsDir))) return [];\n const { readdir } = await import('node:fs/promises');\n const entries = await readdir(skillsDir, { withFileTypes: true });\n return entries\n .filter((e) => e.isDirectory())\n .map((e) => e.name)\n .filter((name) => !name.startsWith('.'))\n .sort();\n}\n\nasync function listRuleMdFiles(rulesDir: string): Promise<string[]> {\n if (!(await dirExists(rulesDir))) return [];\n const { readdir } = await import('node:fs/promises');\n const entries = await readdir(rulesDir, { withFileTypes: true });\n return entries\n .filter((e) => e.isFile() && /\\.md$/i.test(e.name))\n .map((e) => e.name)\n .sort();\n}\n\nasync function dirExists(p: string): Promise<boolean> {\n try {\n const s = await stat(p);\n return s.isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function fileExists(p: string): Promise<boolean> {\n try {\n await access(p, fsConstants.F_OK);\n const s = await stat(p);\n return s.isFile();\n } catch {\n return false;\n }\n}\n\nasync function safeRename(from: string, to: string): Promise<void> {\n const { rename } = await import('node:fs/promises');\n await rename(from, to);\n}\n\n/**\n * uninstall 时判定 registry 记录的文件路径是什么——用于决定删除策略。\n *\n * - `.mdc` 文件 → `mdc-file`(走 marker 二次校验)\n * - `mcp.json` 路径(特殊)→ 不会进来(mcp.json 在 mcpKeys 分支处理)\n * - 目录 → `skill-dir`(递归删)\n * - 其他普通文件 → `other-file`(直接 rm)\n * - 不存在 → `missing`(跳过)\n */\nasync function classifyArtifactPath(\n p: string\n): Promise<'mdc-file' | 'skill-dir' | 'other-file' | 'missing'> {\n let s: Awaited<ReturnType<typeof stat>>;\n try {\n s = await stat(p);\n } catch {\n return 'missing';\n }\n if (s.isDirectory()) return 'skill-dir';\n if (s.isFile()) {\n if (/\\.mdc$/i.test(p)) return 'mdc-file';\n return 'other-file';\n }\n return 'missing';\n}\n","/**\n * Cursor Installer 的 marker 机制(task-7 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §3.3 / §3.4。\n *\n * 问题背景:Cursor 没有原生 plugin 清单。rush-ai 装进 `~/.cursor/rules/*.mdc`\n * 的文件需要和用户手写的 rule 区分——否则 uninstall 可能覆盖用户产物。\n *\n * 机制:所有 rush-ai 生成的 `.mdc` 文件在 body 顶部(frontmatter 之后)加一行\n * `<!-- rush-ai:auto-generated -->`。冲突检测:\n *\n * - 目标 `.mdc` 已存在 + 有 marker → 覆盖(认为是 rush-ai 自己的产物)\n * - 目标 `.mdc` 已存在 + 无 marker → 报冲突 failed(不覆盖用户手写 rule)\n *\n * 卸载时双重校验(marker + registry 双重保险,spec §3.6):\n * - registry 记录了该文件路径 + 实际文件有 marker → 删\n * - registry 记录但文件无 marker(用户重写过)→ 跳过 + warn\n */\n\n/** rush-ai 生成产物的 marker 字符串(HTML 注释形式,在 `.mdc` 的 body 里不显示) */\nexport const RUSH_AI_MARKER = '<!-- rush-ai:auto-generated -->' as const;\n\n/**\n * 检测 `.mdc` 文件内容是否包含 rush-ai 生成 marker。\n *\n * 松匹配:只要文本出现过 `<!-- rush-ai:auto-generated -->` 子串就算有(不要求\n * 在特定位置)。这样即使用户在 marker 上方/下方加了自己的内容,我们仍认定\n * 该文件源自 rush-ai(只要不是**删掉** marker)。\n *\n * 如果用户主动删掉 marker(视为\"接管\"该 rule),我们在卸载时会尊重这个\n * 信号:即便 registry 有记录,也跳过 + warn(见 spec §3.6)。\n */\nexport function hasRushAiMarker(fileContent: string): boolean {\n return fileContent.includes(RUSH_AI_MARKER);\n}\n","/**\n * Cursor Installer 的 `~/.cursor/mcp.json` 合并(task-7 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §3.2。\n *\n * 行为:\n * - 读-改-写 `mcp.json`,顶层合并到 `mcpServers`\n * - stdio server 写入完整字段(`type`/`command`/`args`/`env`/`envFile`)。\n * rush-ai 只写 stdio,用户已有的 stdio/remote server 原样保留\n * - 同 key 覆盖(插件提供的优先)\n * - 卸载时只删本插件**明确声明**的 key(来自 InstallArtifacts.mcpKeys)\n *\n * 并发安全:使用 write-temp-rename 原子替换。\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { constants as fsConstants } from 'node:fs';\nimport {\n access,\n mkdir,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport type { McpServerConfig } from '../types.js';\n\n/**\n * `mcp.json` 顶层 shape。其他字段我们不关心,原样透传保留。\n */\nexport interface CursorMcpJson {\n mcpServers?: Record<string, CursorMcpServerEntry>;\n [extra: string]: unknown;\n}\n\n/**\n * Cursor 的 mcp.json 单 server 条目——stdio 或 remote 变体的并集。\n *\n * rush-ai 生成的只用 stdio 字段(`type: 'stdio'` 显式写,或省略——默认 stdio)。\n * remote 变体字段(type: 'http'|'sse' + url/headers/auth)读取时原样保留。\n */\nexport type CursorMcpServerEntry =\n | CursorMcpStdioEntry\n | CursorMcpRemoteEntry\n | CursorMcpUnknownEntry;\n\nexport interface CursorMcpStdioEntry {\n type?: 'stdio';\n command: string;\n args?: string[];\n env?: Record<string, string>;\n envFile?: string;\n cwd?: string;\n [extra: string]: unknown;\n}\n\nexport interface CursorMcpRemoteEntry {\n type: 'http' | 'sse';\n url: string;\n headers?: Record<string, string>;\n auth?: {\n CLIENT_ID?: string;\n CLIENT_SECRET?: string;\n scopes?: string[];\n [extra: string]: unknown;\n };\n [extra: string]: unknown;\n}\n\n/** 宽容形态:字段不全 / 用户手写格式,我们不强制校验,原样保留 */\nexport type CursorMcpUnknownEntry = Record<string, unknown>;\n\n/**\n * 读取 `mcp.json`(文件不存在返回空对象)。\n *\n * JSON 损坏不尝试修复,直接抛错(对齐 spec §5.4 \"无效 JSON → 报错\")。\n */\nexport async function readCursorMcpJson(\n filePath: string\n): Promise<CursorMcpJson> {\n return (await readCursorMcpJsonWithMtime(filePath)).data;\n}\n\n/**\n * 读取 `mcp.json` 并一并返回文件 mtime(ms),用于 read-modify-write 的并发检测。\n *\n * 文件不存在:data = {},mtimeMs = null\n */\nexport async function readCursorMcpJsonWithMtime(\n filePath: string\n): Promise<{ data: CursorMcpJson; mtimeMs: number | null }> {\n if (!(await pathExists(filePath))) {\n return { data: {}, mtimeMs: null };\n }\n const stats = await stat(filePath);\n const raw = await readFile(filePath, 'utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `~/.cursor/mcp.json 解析失败(${filePath}):${(err as Error).message}`\n );\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(`~/.cursor/mcp.json 顶层必须是 object(${filePath})`);\n }\n return { data: parsed as CursorMcpJson, mtimeMs: stats.mtimeMs };\n}\n\n/**\n * mcp.json 并发写冲突。另一个进程在我们 read-modify-write 之间改了文件。\n *\n * 并发保护策略(对齐 RushRegistryStore 的 mtime retry 方案):\n * 调用方可在 catch 到本错误后 reload + re-apply 本次 addition,最多重试一次。\n */\nexport class CursorMcpConflictError extends Error {\n constructor(public readonly filePath: string) {\n super(\n `~/.cursor/mcp.json 并发写冲突:另一个进程修改了 ${filePath}。请重试。`\n );\n this.name = 'CursorMcpConflictError';\n }\n}\n\n/**\n * 把 plugin 声明的 stdio MCP server 合并进 mcp.json 的 `mcpServers` 字段。\n *\n * @param current 当前 mcp.json 内容(readCursorMcpJson 的返回)\n * @param additions plugin 声明的 MCP server,key 覆盖同名已有 server\n * @returns 合并后的 mcp.json(deep-clone,不 mutate 入参)\n */\nexport function mergeMcpServers(\n current: CursorMcpJson,\n additions: Record<string, McpServerConfig>\n): CursorMcpJson {\n // 先 deep-clone 保留未知字段(用户 / 其他工具写入的 remote server、自定义扩展)\n const cloned: CursorMcpJson = structuredClone(current);\n if (!cloned.mcpServers) {\n cloned.mcpServers = {};\n }\n for (const [key, config] of Object.entries(additions)) {\n cloned.mcpServers[key] = toCursorStdioEntry(config);\n }\n return cloned;\n}\n\n/**\n * 把通用 `McpServerConfig`(rush-ai Installer 共用类型)转为 Cursor stdio entry。\n *\n * 显式写入 `type: 'stdio'`(spec §3.2 推荐显式写,规范更清晰),其他字段原样映射。\n *\n * 透传策略(对齐 spec §3.2 stdio server 完整字段 `type`/`command`/`args`/`env`/`envFile`):\n * - 已知字段 `command` / `args` / `env` / `cwd` 显式映射(`McpServerConfig` 接口直接支持)\n * - 其他字段(`envFile` / 上游插件作者自定义的扩展字段)按 forward-compat 策略透传:\n * 通过宽泛索引访问,保留任何 string/object 类型的附加字段。这样当 shared\n * `McpServerConfig` 类型未来扩展(或第三方 plugin 作者写了 Cursor 原生字段但\n * rush-ai 侧还没更新类型)时不会丢信息\n */\nfunction toCursorStdioEntry(config: McpServerConfig): CursorMcpStdioEntry {\n const entry: CursorMcpStdioEntry = {\n type: 'stdio',\n command: config.command,\n };\n if (config.args !== undefined) entry.args = [...config.args];\n if (config.env !== undefined) entry.env = { ...config.env };\n if (config.cwd !== undefined) entry.cwd = config.cwd;\n\n // Forward-compat 透传:`envFile` + 上游作者自定义字段。\n // 已映射的字段跳过避免重复;值深拷贝(对象/数组)防止外部 mutate。\n const handledKeys = new Set(['command', 'args', 'env', 'cwd']);\n const raw = config as unknown as Record<string, unknown>;\n for (const [key, value] of Object.entries(raw)) {\n if (handledKeys.has(key)) continue;\n if (value === undefined) continue;\n entry[key] = deepCopyPrimitive(value);\n }\n return entry;\n}\n\n/** structuredClone 兼容浅层深拷贝(只处理 JSON-safe 值)。 */\nfunction deepCopyPrimitive(v: unknown): unknown {\n if (v === null || typeof v !== 'object') return v;\n try {\n return structuredClone(v);\n } catch {\n return v; // 含 function / symbol 等不可 clone 的值:原样透传\n }\n}\n\n/**\n * 从 mcp.json 删除指定 keys(uninstall 时用)。\n *\n * @param current 当前 mcp.json 内容\n * @param keysToRemove 要删的 top-level MCP server keys(来自 registry 的 mcpKeys)\n * @returns 删改后的 mcp.json(deep-clone)\n */\nexport function removeMcpServers(\n current: CursorMcpJson,\n keysToRemove: string[]\n): CursorMcpJson {\n const cloned: CursorMcpJson = structuredClone(current);\n if (!cloned.mcpServers) return cloned;\n for (const key of keysToRemove) {\n delete cloned.mcpServers[key];\n }\n // 如果 mcpServers 变空了,保留空对象(不删字段——用户可能在意 schema 完整性)\n return cloned;\n}\n\n/**\n * 原子写入 mcp.json。\n *\n * 序列化风格:2-space 缩进,末尾换行(对齐大部分编辑器偏好)。\n */\nexport async function writeCursorMcpJson(\n filePath: string,\n data: CursorMcpJson\n): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n const content = `${JSON.stringify(data, null, 2)}\\n`;\n const tmp = `${filePath}.${randomUUID()}.tmp`;\n try {\n await writeFile(tmp, content, { encoding: 'utf8', flag: 'w' });\n await rename(tmp, filePath);\n } catch (err) {\n await rm(tmp, { force: true }).catch(() => {});\n throw err;\n }\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p, fsConstants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n","/**\n * Cursor Installer 的路径 helper(task-7 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §3.1 Cursor 目录布局。\n *\n * Cursor 没有 plugin 容器,摊开分发到:\n * - `~/.cursor/mcp.json` —— MCP 合并\n * - `~/.cursor/skills/<name>/` —— skill 目录(完整 copy 源)\n * - `~/.cursor/rules/<name>.mdc` —— rules + bridge rule\n *\n * 所有 helper 接受 `home` 参数以便测试注入临时 HOME——**禁止**写用户真实 `~/.cursor/`。\n */\n\nimport { resolve } from 'node:path';\n\n/**\n * `.cursor/` 相对 HOME 的路径(spec §3.1)。\n */\nexport const CURSOR_RELATIVE_DIR = '.cursor' as const;\n\n/** `~/.cursor/` 绝对路径 */\nexport function cursorDir(home: string): string {\n return resolve(home, CURSOR_RELATIVE_DIR);\n}\n\n/** `~/.cursor/mcp.json` 绝对路径 */\nexport function cursorMcpJsonPath(home: string): string {\n return resolve(cursorDir(home), 'mcp.json');\n}\n\n/** `~/.cursor/rules/` 绝对路径 */\nexport function cursorRulesDir(home: string): string {\n return resolve(cursorDir(home), 'rules');\n}\n\n/** `~/.cursor/rules/<name>.mdc` 绝对路径 */\nexport function cursorRuleMdcPath(home: string, ruleName: string): string {\n return resolve(cursorRulesDir(home), `${ruleName}.mdc`);\n}\n\n/** `~/.cursor/skills/` 绝对路径 */\nexport function cursorSkillsDir(home: string): string {\n return resolve(cursorDir(home), 'skills');\n}\n\n/** `~/.cursor/skills/<name>/` 绝对路径 */\nexport function cursorSkillDir(home: string, skillName: string): string {\n return resolve(cursorSkillsDir(home), skillName);\n}\n\n/**\n * bridge rule body 里 `@file` 引用 skill 的相对路径(相对 `~/`)。\n *\n * 对齐 reskill 实践(spec §3.3):`@file .cursor/skills/<name>/SKILL.md`。\n * Cursor 官方接受此形式。\n */\nexport function bridgeSkillFileReference(skillName: string): string {\n return `.cursor/skills/${skillName}/SKILL.md`;\n}\n","/**\n * Cursor Installer 的 rules 转换(task-7 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §3.4。\n *\n * 行为:\n * - 源 `.md` 有 frontmatter → 透传所有字段,补齐 `alwaysApply: false`(缺失时)\n * - 源 `.md` 无 frontmatter → 加默认 frontmatter,`description` 从第一行/标题提取\n * - Body 原样透传,**顶部插入** `<!-- rush-ai:auto-generated -->` marker(见 marker.ts)\n * - 目标扩展名 `.mdc`(Cursor 约定)\n *\n * 设计取舍:\n * - **不引入 yaml 解析库**。rush-ai 的依赖表里没有 yaml;Cursor `.mdc` frontmatter\n * 的字段非常简单(key: value,偶有 multi-line 引号),手工解析 + 序列化即可。\n * 官方 Cursor frontmatter 只定义 3 个字段:`description` / `globs` / `alwaysApply`。\n * 我们对其他字段**原样字符串透传**(保留原行,不解析 value),最大化兼容性。\n * - `alwaysApply` 强制值为布尔,缺失时补 `false`。\n */\n\nimport { RUSH_AI_MARKER } from './marker.js';\n\n/**\n * 分离后的 frontmatter / body。\n */\nexport interface ParsedMarkdown {\n /** null 表示源无 frontmatter */\n readonly frontmatter: FrontmatterFields | null;\n /** frontmatter 之外的 body 内容(不含分隔符 `---`) */\n readonly body: string;\n}\n\n/**\n * 保守的 frontmatter 字段视图。\n *\n * 用 `raw` 保留原始行文本(用于透传未知字段时不丢失格式)。\n * `parsed` 只抽我们关心的字段:`description` / `alwaysApply`。\n * `globs` 等其他字段归入 raw 原样透传。\n */\nexport interface FrontmatterFields {\n /** 解析到的 description(顶层 key: value,不支持 multiline) */\n description?: string;\n /** 解析到的 alwaysApply(true/false 字面) */\n alwaysApply?: boolean;\n /**\n * 所有 frontmatter 行的原始文本(不含开头/结尾的 `---` 分隔符),\n * 按源文件顺序。透传时原样输出,但会被我们覆盖 alwaysApply 行(若需要补齐)。\n */\n rawLines: string[];\n}\n\nconst FRONTMATTER_FENCE = '---';\nconst NEWLINE = '\\n';\n\n/**\n * 解析 Markdown 文本为 frontmatter + body。\n *\n * Frontmatter 判定规则(对齐 Cursor `.mdc` 与常见 Jekyll 约定):\n * - 文件**首行**必须恰好是 `---`\n * - 从下一行起扫描到**下一个** `---` 行(单独一行,不含前后空格)\n * - 未找到结束分隔符 → 视为**无 frontmatter**,整个文件作为 body\n *\n * 为避免在 body 里误解析(比如 body 里用到 `---` 作水平线),我们只在第一个 `---`\n * **紧贴文件开头**时才启用 frontmatter 解析。\n */\nexport function parseMarkdown(text: string): ParsedMarkdown {\n // 使用 split + 保留行号,避免误处理 CRLF(我们统一按 LF 解析,写回时也按 LF;\n // Cursor 生态默认 LF)\n const normalized = text.replace(/\\r\\n/g, '\\n');\n const lines = normalized.split('\\n');\n\n if (lines.length === 0 || lines[0] !== FRONTMATTER_FENCE) {\n return { frontmatter: null, body: normalized };\n }\n\n // 从第 1 行开始找结束分隔符\n let endIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === FRONTMATTER_FENCE) {\n endIdx = i;\n break;\n }\n }\n\n if (endIdx === -1) {\n // 开头是 `---` 但没有结束分隔符 —— 不算 frontmatter,整段当 body\n return { frontmatter: null, body: normalized };\n }\n\n const rawLines = lines.slice(1, endIdx);\n const bodyLines = lines.slice(endIdx + 1);\n const body = bodyLines.join(NEWLINE);\n\n const fields: FrontmatterFields = { rawLines };\n for (const line of rawLines) {\n const colonIdx = line.indexOf(':');\n if (colonIdx <= 0) continue;\n const key = line.slice(0, colonIdx).trim();\n const rawValue = line.slice(colonIdx + 1).trim();\n if (key === 'description') {\n fields.description = stripQuotes(rawValue);\n } else if (key === 'alwaysApply') {\n const lowered = rawValue.toLowerCase();\n if (lowered === 'true') fields.alwaysApply = true;\n else if (lowered === 'false') fields.alwaysApply = false;\n // 其他值(比如 `alwaysApply:` 空)→ 视为未定义,后续会补齐 false\n }\n }\n\n return { frontmatter: fields, body };\n}\n\n/**\n * 剥掉 YAML 风格的 `\"...\"` 或 `'...'` 引号(frontmatter value 常用,实测 Cursor\n * 不强制要求)。不做 escape 还原,保留子字符串——纯粹为生成 bridge rule 时的\n * description 提取用。\n */\nfunction stripQuotes(v: string): string {\n if (v.length >= 2) {\n const first = v.charAt(0);\n const last = v.charAt(v.length - 1);\n if ((first === '\"' && last === '\"') || (first === \"'\" && last === \"'\")) {\n return v.slice(1, -1);\n }\n }\n return v;\n}\n\n/**\n * 从 body 文本第一行或首个 H1/H2 标题提取 description 候选值。\n *\n * 规则:\n * 1. 去掉前导空白 / 空行\n * 2. 第一行若以 `# ` / `## ` 开头,取标题文本\n * 3. 否则取该行原文(截断到 120 字符以内,避免超长)\n * 4. 全空 body → 返回 undefined\n */\nexport function extractDescriptionFromBody(body: string): string | undefined {\n const lines = body.split(NEWLINE);\n for (const rawLine of lines) {\n const line = rawLine.trim();\n if (line.length === 0) continue;\n const headingMatch = /^#{1,6}\\s+(.+)$/.exec(line);\n const candidate = headingMatch ? headingMatch[1].trim() : line;\n if (candidate.length === 0) continue;\n return candidate.length > 120 ? `${candidate.slice(0, 117)}...` : candidate;\n }\n return undefined;\n}\n\n/**\n * 构建 `.mdc` 目标文件内容。\n *\n * @param parsed 从源 `.md` 解析出的 frontmatter + body\n * @param opts.descriptionFallback 当源无 frontmatter 或无 description 时的兜底\n * (一般是 plugin.json.description 或 rule 文件名)\n *\n * 输出结构(固定顺序):\n * ```\n * ---\n * <frontmatter 行,保留源顺序;alwaysApply 缺失时补齐>\n * ---\n *\n * <!-- rush-ai:auto-generated -->\n * <body 原文>\n * ```\n *\n * 行为细则:\n * - 源有 frontmatter:保留 `rawLines` 顺序透传,**仅**当缺 `alwaysApply` 时在\n * rawLines 末尾追加 `alwaysApply: false`。不触碰 `description` / `globs`\n * - 源无 frontmatter:生成最小 frontmatter(`description` + `alwaysApply: false`)\n * 其中 description 先用 body 首行/标题;再用 opts.descriptionFallback;再\n * 用空字符串兜底(但通常 fallback 总能给出值)\n * - Body 顶部**强制**插入 marker 行(紧跟 frontmatter + 一个空行)\n */\nexport function renderMdc(\n parsed: ParsedMarkdown,\n opts: { descriptionFallback: string }\n): string {\n let rawLines: string[];\n if (parsed.frontmatter) {\n rawLines = [...parsed.frontmatter.rawLines];\n if (parsed.frontmatter.alwaysApply === undefined) {\n rawLines.push('alwaysApply: false');\n }\n } else {\n const bodyDescription = extractDescriptionFromBody(parsed.body);\n const description = bodyDescription ?? opts.descriptionFallback;\n rawLines = [\n `description: ${quoteIfNeeded(description)}`,\n 'alwaysApply: false',\n ];\n }\n\n const frontmatterBlock = [\n FRONTMATTER_FENCE,\n ...rawLines,\n FRONTMATTER_FENCE,\n ].join(NEWLINE);\n\n // body 可能本来就以换行开头(源 `.md` frontmatter 之后通常有空行),\n // 统一规范化:frontmatter → 空行 → marker → 空行 → body(去掉 body 的前导空行)\n const normalizedBody = parsed.body.replace(/^[\\n\\r]+/, '');\n\n return `${frontmatterBlock}${NEWLINE}${NEWLINE}${RUSH_AI_MARKER}${NEWLINE}${NEWLINE}${normalizedBody}`;\n}\n\n/**\n * bridge rule 的生成(spec §3.3)。和 `renderMdc` 分开——bridge rule 有固定模板,\n * 不接受源 frontmatter 透传,只用 description 三级 fallback。\n */\nexport function renderBridgeRule(args: {\n description: string;\n skillFileReference: string;\n}): string {\n const rawLines = [\n `description: ${quoteIfNeeded(args.description)}`,\n 'globs:',\n 'alwaysApply: false',\n ];\n const frontmatterBlock = [\n FRONTMATTER_FENCE,\n ...rawLines,\n FRONTMATTER_FENCE,\n ].join(NEWLINE);\n return `${frontmatterBlock}${NEWLINE}${NEWLINE}${RUSH_AI_MARKER}${NEWLINE}@file ${args.skillFileReference}${NEWLINE}`;\n}\n\n/**\n * 如果 description 含冒号、井号、引号等特殊字符,用双引号包起来以保证 YAML 合法。\n *\n * 简化策略:只要包含冒号或以特殊字符开头就加引号;内部双引号转义为 `\\\"`。\n * 这样生成的结果始终是合法 YAML(Cursor 侧实测也接受无引号形式,但我们保守)。\n */\nfunction quoteIfNeeded(value: string): string {\n const needsQuote = /[\":\\n\\r#&*!|>'%@`\\t]/.test(value) || value.length === 0;\n if (!needsQuote) return value;\n const escaped = value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n return `\"${escaped}\"`;\n}\n","/**\n * Cursor Installer 的 skills 双写逻辑(task-7 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §3.3。\n *\n * **关键设计(phase0-spike 实测结论)**:Cursor 没有原生 skills 扫描。\n * 单纯把 `SKILL.md` 放进 `~/.cursor/skills/<name>/` 不会被 Cursor Agent 自动加载。\n *\n * 解决方案 = **双写**:\n * 1. `~/.cursor/skills/<name>/SKILL.md`(+ 附属文件)完整 copy 源目录\n * 2. `~/.cursor/rules/<name>.mdc` 生成 bridge rule,frontmatter 带 description,\n * body 用 `@file` 引入 SKILL.md\n *\n * bridge rule 的 `description` 三级 fallback(spec §3.3,顺序固定):\n * 1. 源 `SKILL.md` 的 frontmatter.description\n * 2. 源 `plugin.json` 的 description\n * 3. skill 名本身(如 `\"rush-task\"`)\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport type { PluginManifest } from '../types.js';\nimport { parseMarkdown } from './rules.js';\n\n/**\n * 从源 SKILL.md 提取 frontmatter.description。\n *\n * @param skillSourceDir 源 skill 目录绝对路径(`<plugin>/skills/<name>/`)\n * @returns description 字符串 | undefined(文件不存在或无 frontmatter.description 都返回 undefined)\n */\nexport async function readSkillDescription(\n skillSourceDir: string\n): Promise<string | undefined> {\n const skillMdPath = resolve(skillSourceDir, 'SKILL.md');\n let raw: string;\n try {\n raw = await readFile(skillMdPath, 'utf8');\n } catch {\n return undefined;\n }\n const parsed = parseMarkdown(raw);\n const desc = parsed.frontmatter?.description?.trim();\n if (!desc) return undefined;\n return desc;\n}\n\n/**\n * description 三级 fallback 解析(spec §3.3 顺序固定)。\n *\n * @param skillSourceDir 源 skill 目录\n * @param skillName skill 名(fallback 3)\n * @param manifest plugin.json manifest(fallback 2 用它的 description)\n */\nexport async function resolveSkillDescription(args: {\n skillSourceDir: string;\n skillName: string;\n manifest: PluginManifest;\n}): Promise<string> {\n // 1. 源 SKILL.md frontmatter.description\n const fromSkill = await readSkillDescription(args.skillSourceDir);\n if (fromSkill) return fromSkill;\n\n // 2. plugin.json.description\n const fromManifest = args.manifest.description?.trim();\n if (fromManifest) return fromManifest;\n\n // 3. skill 名本身\n return args.skillName;\n}\n","/**\n * 清理老版本 rush-ai 磁盘痕迹(task-12)。\n *\n *\n * **调用时机铁律**(plan §4.3 / spec §10 风险矩阵):\n * 本模块**只能**在新格式已成功安装且验证通过**之后**调用——绝对不允许\n * \"先清旧再装新\"。调用方(`migrate.ts`)负责严格遵守顺序。\n *\n * 清理项:\n * 1. 删除目录 `~/.rush/plugins/claude-code/`(递归)\n * 2. `unlink` `~/.claude/skills/rush-task`(仅当它是 symlink;若是目录/文件为安全起见跳过)\n * 3. 从 `~/.claude/settings.json` 移除 `mcpServers.rush`(读-改-写,原子替换)\n *\n * 每个清理子步骤独立 try/catch,**任一子步骤失败不影响其他**——整体迁移已经\n * 成功(新格式装好+验证通过),清理残留只是 best-effort。失败信息通过返回值传递\n * 让调用方记录,不抛错。\n */\n\nimport type { Stats } from 'node:fs';\nimport { lstat, rm, unlink } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { resolve } from 'node:path';\nimport {\n readJsonFile,\n writeJsonFile,\n} from '../installers/claude-code/atomic-json.js';\nimport {\n CLAUDE_SETTINGS_JSON_RELATIVE,\n LEGACY_CLAUDE_CODE_DIR_RELATIVE,\n LEGACY_SKILL_SYMLINK_RELATIVE,\n} from './detect.js';\n\nexport interface CleanupLegacyOptions {\n /** 注入 HOME(测试必填) */\n home?: string;\n}\n\n/**\n * 清理结果。`ok=true` 代表该步骤全部完成或无需执行(幂等成功);\n * `ok=false` 代表发生错误——`error` 字段描述原因。\n */\nexport interface CleanupStepResult {\n readonly ok: boolean;\n readonly error?: string;\n}\n\n/**\n * 整体清理结果——各子步骤独立记录,任一失败不中断其他步骤。\n */\nexport interface CleanupLegacyResult {\n readonly legacyDir: CleanupStepResult;\n readonly skillSymlink: CleanupStepResult;\n readonly settingsMcp: CleanupStepResult;\n}\n\n// ---------------------------------------------------------------------------\n// 各子步骤\n// ---------------------------------------------------------------------------\n\n/**\n * 删 `~/.rush/plugins/claude-code/`(递归)。\n *\n * 不存在 → no-op 视为成功。\n */\nasync function cleanupLegacyDir(home: string): Promise<CleanupStepResult> {\n const dir = resolve(home, LEGACY_CLAUDE_CODE_DIR_RELATIVE);\n try {\n await rm(dir, { recursive: true, force: true });\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n error: `remove ${dir} failed: ${errorMessage(err)}`,\n };\n }\n}\n\n/**\n * `unlink` `~/.claude/skills/rush-task`。\n *\n * 安全:只在 path 是 symlink 时才 unlink;普通目录/文件**不动**(避免误删用户自建内容)。\n * 不存在 → no-op 成功。\n */\nasync function cleanupLegacySymlink(home: string): Promise<CleanupStepResult> {\n const path = resolve(home, LEGACY_SKILL_SYMLINK_RELATIVE);\n try {\n let lst: Stats;\n try {\n lst = await lstat(path);\n } catch (err) {\n if (isNotFound(err)) return { ok: true };\n throw err;\n }\n if (!lst.isSymbolicLink()) {\n // 保守:非 symlink 不动。返回 ok=true 让 migrate 主流程继续——\n // 从用户视角看\"symlink 已不存在\"也算完成。\n return { ok: true };\n }\n await unlink(path);\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n error: `unlink ${path} failed: ${errorMessage(err)}`,\n };\n }\n}\n\n/**\n * 从 `~/.claude/settings.json` 移除 `mcpServers.rush`(并在 `mcpServers` 变空\n * 后保留空对象——Claude Code schema 允许空 `mcpServers`,且保留能防止后续\n * 写入时 key 排序变化产生的 diff)。\n *\n * 各情形返回值:\n * - settings.json 不存在 → `ok:true`(no-op 成功)\n * - 文件存在但无 `mcpServers` / 无 `mcpServers.rush` → `ok:true`(no-op 成功)\n * - 文件存在但 JSON 损坏 → `ok:false` + error(让 summarizeCleanup 捕获;\n * 我们不尝试\"智能修复\"损坏的用户配置,对齐 plan §10 风险矩阵)\n * - 写入 IO 失败 → `ok:false` + error\n *\n * 不做 mtime 冲突保护(settings.json 没有 mtime 契约)——简单原子 rename 足够。\n */\nasync function cleanupLegacyMcp(home: string): Promise<CleanupStepResult> {\n const path = resolve(home, CLAUDE_SETTINGS_JSON_RELATIVE);\n try {\n const { data, existed } = await readJsonFile<Record<string, unknown>>(\n path,\n () => ({})\n );\n if (!existed) return { ok: true };\n\n const mcpServers = data.mcpServers;\n if (\n !mcpServers ||\n typeof mcpServers !== 'object' ||\n Array.isArray(mcpServers)\n ) {\n return { ok: true };\n }\n const mcpObj = mcpServers as Record<string, unknown>;\n if (!Object.hasOwn(mcpObj, 'rush')) {\n return { ok: true };\n }\n\n // 构造删除后的新视图(不变字段原样透传)\n const { rush: _removed, ...rest } = mcpObj;\n void _removed;\n const next: Record<string, unknown> = { ...data, mcpServers: rest };\n await writeJsonFile(path, next);\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n error: `settings.json cleanup failed: ${errorMessage(err)}`,\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// 主 API\n// ---------------------------------------------------------------------------\n\n/**\n * 清理所有老版本 rush-ai 痕迹。**不抛**——所有失败走返回值。\n *\n * 调用前提:新格式已装好且通过验证(plan §4.3 严格顺序)。\n */\nexport async function cleanupLegacyInstall(\n opts: CleanupLegacyOptions = {}\n): Promise<CleanupLegacyResult> {\n const home = opts.home ?? homedir();\n // 三步独立 await,一个失败不影响其他——仍尽量把能清的都清掉。\n // 串行而非并行——避免三个写操作相互影响(settings.json 写入可能触发别的 IO)\n const legacyDir = await cleanupLegacyDir(home);\n const skillSymlink = await cleanupLegacySymlink(home);\n const settingsMcp = await cleanupLegacyMcp(home);\n return { legacyDir, skillSymlink, settingsMcp };\n}\n\n/**\n * 把 `CleanupLegacyResult` 总结成一个 `failed: boolean, reasons: string[]` 的\n * 便于日志记录的 shape。\n */\nexport function summarizeCleanup(result: CleanupLegacyResult): {\n failed: boolean;\n reasons: string[];\n} {\n const reasons: string[] = [];\n if (!result.legacyDir.ok && result.legacyDir.error) {\n reasons.push(result.legacyDir.error);\n }\n if (!result.skillSymlink.ok && result.skillSymlink.error) {\n reasons.push(result.skillSymlink.error);\n }\n if (!result.settingsMcp.ok && result.settingsMcp.error) {\n reasons.push(result.settingsMcp.error);\n }\n return { failed: reasons.length > 0, reasons };\n}\n\n// ---------------------------------------------------------------------------\n// internals\n// ---------------------------------------------------------------------------\n\nfunction isNotFound(err: unknown): boolean {\n return (\n typeof err === 'object' &&\n err !== null &&\n (err as { code?: string }).code === 'ENOENT'\n );\n}\n\nfunction errorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n","/**\n * 老版本 rush-ai(0.7.x)磁盘状态检测(task-12)。\n *\n *\n * 三个\"旧格式标记\"(or 关系,任一命中即触发迁移):\n * 1. `~/.rush/plugins/claude-code/asset-manifest.json` 存在\n * 2. `~/.claude/skills/rush-task` 是 symlink 且指向 `~/.rush/plugins/claude-code/assets`\n * (或其子目录)\n * 3. `~/.claude/settings.json` 有 `mcpServers.rush`,但 `enabledPlugins` 中\n * **不存在任何 `rush@*` 键** —— 即旧格式的 MCP 注册尚未被新格式接管\n *\n * 所有检测函数:\n * - 都走注入的 `home` 目录,便于测试\n * - 对不存在 / 损坏 / 权限错等返回 false 或中性值,不抛错(detect 不允许 throw)\n * - 不写磁盘,纯读\n */\n\nimport { access, lstat, readFile, readlink, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { isAbsolute, resolve } from 'node:path';\n\n/** 老版本 rush-ai 的资源清单路径(相对 HOME)。 */\nexport const LEGACY_ASSET_MANIFEST_RELATIVE =\n '.rush/plugins/claude-code/asset-manifest.json' as const;\n\n/** 老版本 rush-ai 的资源目录(相对 HOME)——symlink 目标比对用。 */\nexport const LEGACY_ASSETS_DIR_RELATIVE =\n '.rush/plugins/claude-code/assets' as const;\n\n/** 老版本 rush-ai 的整个 plugins/claude-code 目录(清理时整块删)。 */\nexport const LEGACY_CLAUDE_CODE_DIR_RELATIVE =\n '.rush/plugins/claude-code' as const;\n\n/** 老版本 Claude Code skills symlink 的绝对路径(相对 HOME)。 */\nexport const LEGACY_SKILL_SYMLINK_RELATIVE =\n '.claude/skills/rush-task' as const;\n\n/** `~/.claude/settings.json`(相对 HOME)。 */\nexport const CLAUDE_SETTINGS_JSON_RELATIVE = '.claude/settings.json' as const;\n\nexport interface DetectLegacyOptions {\n /** 注入 HOME(测试必填,生产默认 `os.homedir()`) */\n home?: string;\n}\n\n/**\n * 具体检测到的老版本痕迹——用于 migrate/cleanup 决策和日志。\n */\nexport interface LegacyDetectionResult {\n /** 任一触发条件命中即 true。 */\n readonly detected: boolean;\n /** 条件 1:`~/.rush/plugins/claude-code/asset-manifest.json` 存在。 */\n readonly hasAssetManifest: boolean;\n /** 条件 2:`~/.claude/skills/rush-task` 是指向老 assets 目录的 symlink。 */\n readonly hasLegacySkillSymlink: boolean;\n /** 条件 3:settings.json 有 `mcpServers.rush` 但无 `enabledPlugins[\"rush@*\"]`。 */\n readonly hasLegacyMcpWithoutEnabled: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * 判断给定 symlink 是否指向 `expectedTarget` 或其子路径。\n *\n * symlink target 可能是相对路径,需要基于 symlink 所在目录解析为绝对路径再比对。\n * 非 symlink / symlink 损坏 / 读不到 → 返回 false(不抛)。\n */\nasync function isSymlinkPointingInto(\n linkPath: string,\n expectedTarget: string\n): Promise<boolean> {\n try {\n const lst = await lstat(linkPath);\n if (!lst.isSymbolicLink()) return false;\n const rawTarget = await readlink(linkPath);\n const resolvedTarget = isAbsolute(rawTarget)\n ? rawTarget\n : resolve(linkPath, '..', rawTarget);\n // 匹配完全相等 或 指向 expectedTarget 的子路径(path + '/' 前缀)\n return (\n resolvedTarget === expectedTarget ||\n resolvedTarget.startsWith(`${expectedTarget}/`)\n );\n } catch {\n return false;\n }\n}\n\n/**\n * 读 `~/.claude/settings.json`,判断 `mcpServers.rush` 是否存在 + `enabledPlugins`\n * 是否缺少任何 `rush@*` 键。\n *\n * 读失败 / 损坏 / 缺文件 → 返回 false(不抛)。\n * detect 不能因为用户 settings.json 损坏就挂掉——那是迁移要不要触发的判断,\n * 真正迁移时若再失败会进入失败分支。\n */\nasync function hasLegacyMcpWithoutEnabled(\n settingsJsonPath: string\n): Promise<boolean> {\n try {\n if (!(await pathExists(settingsJsonPath))) return false;\n const raw = await readFile(settingsJsonPath, 'utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return false;\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return false;\n }\n const obj = parsed as Record<string, unknown>;\n const mcpServers = obj.mcpServers;\n const hasRushMcp =\n mcpServers !== null &&\n typeof mcpServers === 'object' &&\n !Array.isArray(mcpServers) &&\n Object.hasOwn(mcpServers, 'rush');\n if (!hasRushMcp) return false;\n\n const enabledPlugins = obj.enabledPlugins;\n if (\n enabledPlugins === null ||\n enabledPlugins === undefined ||\n typeof enabledPlugins !== 'object' ||\n Array.isArray(enabledPlugins)\n ) {\n // 有 rush mcp 但 enabledPlugins 字段缺失/非 object → 视为\"没有任何 rush@* 键\"\n return true;\n }\n const hasRushEnabled = Object.keys(\n enabledPlugins as Record<string, unknown>\n ).some((k) => k.startsWith('rush@'));\n return !hasRushEnabled;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// 主 API\n// ---------------------------------------------------------------------------\n\n/**\n * 检测老版本 rush-ai 磁盘状态。**不抛**——所有 IO 错误降级为 false。\n */\nexport async function detectLegacyInstall(\n opts: DetectLegacyOptions = {}\n): Promise<LegacyDetectionResult> {\n const home = opts.home ?? homedir();\n const assetManifestPath = resolve(home, LEGACY_ASSET_MANIFEST_RELATIVE);\n const legacyAssetsDir = resolve(home, LEGACY_ASSETS_DIR_RELATIVE);\n const skillSymlinkPath = resolve(home, LEGACY_SKILL_SYMLINK_RELATIVE);\n const settingsJsonPath = resolve(home, CLAUDE_SETTINGS_JSON_RELATIVE);\n\n const [hasAssetManifest, hasLegacySkillSymlink, legacyMcpWithoutEnabled] =\n await Promise.all([\n pathExists(assetManifestPath),\n isSymlinkPointingInto(skillSymlinkPath, legacyAssetsDir),\n hasLegacyMcpWithoutEnabled(settingsJsonPath),\n ]);\n\n return {\n detected:\n hasAssetManifest || hasLegacySkillSymlink || legacyMcpWithoutEnabled,\n hasAssetManifest,\n hasLegacySkillSymlink,\n hasLegacyMcpWithoutEnabled: legacyMcpWithoutEnabled,\n };\n}\n\n/**\n * 尝试从老 asset-manifest.json 提取老版本号(用于 registry.migrations.fromVersion)。\n *\n * 失败(文件不存在 / 损坏 / 无 version 字段)→ 返回 `'0.7.x'`(plan §4.3 的泛指)。\n */\nexport async function detectLegacyVersion(\n opts: DetectLegacyOptions = {}\n): Promise<string> {\n const home = opts.home ?? homedir();\n const manifestPath = resolve(home, LEGACY_ASSET_MANIFEST_RELATIVE);\n try {\n const lst = await stat(manifestPath);\n if (!lst.isFile()) return '0.7.x';\n const raw = await readFile(manifestPath, 'utf8');\n const parsed = JSON.parse(raw) as unknown;\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n const v = (parsed as Record<string, unknown>).version;\n if (typeof v === 'string' && v.trim() !== '') return v.trim();\n }\n return '0.7.x';\n } catch {\n return '0.7.x';\n }\n}\n","/**\n * 迁移失败日志(task-12)。\n *\n *\n * 写入路径:`<home>/.rush/plugins/migration-failed.log`(**追加**模式)。\n *\n * 设计考量:\n * - 追加而非覆盖:用户可能重复触发迁移失败,每次失败都应留痕供事后排查\n * - 每条一行带 ISO 时间戳 + reason;必要时再跟缩进的 stack 或 detail\n * - 写失败(例如磁盘满、权限)**不抛错**——迁移本身已经失败,日志写不上只能吞掉\n * (不能在本已失败的分支里再抛一个错)\n * - 父目录不存在先 mkdir -p(对齐 registry / installer 的惯例)\n *\n * 测试铁律:所有测试必须走注入的临时 HOME,禁止写真实 `~/.rush/`。\n */\n\nimport { appendFile, mkdir } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, resolve } from 'node:path';\n\n/** 迁移失败日志相对 HOME 的路径。 */\nexport const MIGRATION_LOG_RELATIVE_PATH =\n '.rush/plugins/migration-failed.log' as const;\n\nexport interface AppendMigrationFailureOptions {\n /** 注入 HOME(测试必填,生产默认 `os.homedir()`) */\n home?: string;\n /** 注入时间戳生成器(测试里固定时间),默认 `() => new Date().toISOString()` */\n now?: () => string;\n}\n\n/**\n * 计算迁移失败日志的绝对路径。\n */\nexport function resolveMigrationLogPath(\n opts: AppendMigrationFailureOptions = {}\n): string {\n const home = opts.home ?? homedir();\n return resolve(home, MIGRATION_LOG_RELATIVE_PATH);\n}\n\n/**\n * 追加一条迁移失败记录到日志。\n *\n * 格式:\n * ```\n * [<ISO-timestamp>] <reason>\n * [<ISO-timestamp>] detail: <detail-line-1>\n * [<ISO-timestamp>] detail: <detail-line-2>\n * ```\n *\n * - `reason` 是一行摘要(不换行;内部换行会被替换为空格)\n * - `detail` 可选,多行时每行都带时间戳前缀,便于 grep\n *\n * **永不抛错**——日志写失败只能吞掉,不能在已失败的 migrate 路径里再加一层失败。\n */\nexport async function appendMigrationFailure(\n reason: string,\n opts: AppendMigrationFailureOptions & { detail?: string } = {}\n): Promise<void> {\n const now = opts.now ?? (() => new Date().toISOString());\n const ts = now();\n const logPath = resolveMigrationLogPath(opts);\n const safeReason = reason.replace(/\\r?\\n/g, ' ').trim() || '<no reason>';\n const lines: string[] = [`[${ts}] ${safeReason}`];\n if (opts.detail) {\n for (const detailLine of opts.detail.replace(/\\r\\n/g, '\\n').split('\\n')) {\n if (detailLine.trim() === '') continue;\n lines.push(`[${ts}] detail: ${detailLine}`);\n }\n }\n const payload = `${lines.join('\\n')}\\n`;\n try {\n await mkdir(dirname(logPath), { recursive: true });\n await appendFile(logPath, payload, { encoding: 'utf8' });\n } catch {\n // 吞掉——迁移失败分支不允许再抛错\n }\n}\n","/**\n * 老版本 rush-ai → 新格式迁移主流程(task-12)。\n *\n * Source of truth:\n * - `specs/cli-commands.md` §4(UX)\n *\n * # 核心铁律(绝对不能颠倒)\n * 1. 读 registry.migrations,若 `legacy-rush-0.7.x` 已 `status=ok` → 整个跳过(幂等)\n * 2. 按新格式装 rush@rush-marketplace 到 `~/.claude/plugins/cache/...`\n * 3. 验证新格式能读回(plugin.json 存在 + JSON 解析 + name 匹配)\n * 4. 验证通过 → 清旧(删老目录 + symlink + settings.mcpServers.rush)\n * 5. 记 registry `migrations.legacy-rush-0.7.x = { migratedAt, status: 'ok', fromVersion }`\n *\n * 验证失败分支:\n * - **不清旧**(新旧并存)\n * - 写 `~/.rush/plugins/migration-failed.log`\n * - 记 registry status='failed' + notes\n * - 函数返回,**不抛**——不打断外层 install 命令\n *\n * # 关键设计:避免递归\n * `install.ts` 的 pre-install hook 调 `maybeRunMigration`;本函数内部为了\"装新\"\n * 需要调 `ClaudeCodeInstaller.install()`——这里**直接用 Installer,不走\n * `runInstall`**(runInstall 里才有 hook 调用点)。所以递归天然不会发生。\n *\n * # RUSH_SKIP_MIGRATION=1\n * 函数开头检查,命中立即 no-op 返回(plan §10 回滚开关)。\n *\n * # 测试\n * 所有测试铁律注入临时 HOME,严禁写真实 `~/`。\n */\n\nimport { access, readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { ClaudeCodeInstaller } from '../installers/claude-code/installer.js';\nimport {\n ClaudeCodePaths,\n PLUGIN_MANIFEST_RELATIVE,\n} from '../installers/claude-code/paths.js';\nimport { RushRegistryStore } from '../installers/registry.js';\nimport type {\n MigrationRecord,\n PluginInstaller,\n PluginRef,\n ResolvedPlugin,\n} from '../installers/types.js';\nimport {\n RUSH_AI_MARKETPLACE_NAME,\n RUSH_AI_PLUGIN_NAME,\n} from '../plugins/resolver.js';\nimport { cleanupLegacyInstall, summarizeCleanup } from './cleanup.js';\nimport { detectLegacyInstall, detectLegacyVersion } from './detect.js';\nimport { appendMigrationFailure } from './log.js';\n\n// ---------------------------------------------------------------------------\n// 常量 / 契约\n// ---------------------------------------------------------------------------\n\n/**\n * 迁移来源 key——写入 registry.migrations 的索引键。\n *\n * 语义:老版本 rush-ai 0.7.x 整体迁移。后续若有 0.8.x → 1.0.0 这类迁移再加新 key。\n */\nexport const LEGACY_MIGRATION_KEY = 'legacy-rush-0.7.x' as const;\n\n/** 环境变量名——命中则跳过迁移(plan §10 回滚开关)。 */\nexport const SKIP_MIGRATION_ENV = 'RUSH_SKIP_MIGRATION' as const;\n\n// ---------------------------------------------------------------------------\n// Input / Output\n// ---------------------------------------------------------------------------\n\nexport interface MaybeRunMigrationInput {\n /** 当前 install 命令目标 ref——非 `rush@rush-marketplace` 会直接 no-op。 */\n ref: PluginRef;\n /** 已 resolve 的 ResolvedPlugin——装新时直接喂给 Installer(避免重复 resolve)。 */\n resolvedPlugin?: ResolvedPlugin;\n\n /** 注入 HOME(测试必填,生产默认 `os.homedir()`)。 */\n home?: string;\n\n /** 注入 ISO 时间戳生成器(测试里固定)。默认 `() => new Date().toISOString()`。 */\n now?: () => string;\n\n /**\n * 注入环境变量 lookup(测试里不污染真实 env)。\n * 默认 `(key) => process.env[key]`。\n */\n env?: (key: string) => string | undefined;\n\n /**\n * 注入 Installer 工厂——生产走默认 `ClaudeCodeInstaller`;测试里可替换为 mock\n * 来控制 install 成功/失败/抛错。\n *\n * **铁律**:此 Installer 必须**不触发** install.ts 的 pre-install hook,否则\n * 会递归。本模块内直接 `new ClaudeCodeInstaller()` 绕过 `runInstall`,天然无递归。\n */\n installerFactory?: (home: string) => PluginInstaller;\n\n /**\n * 注入 RushRegistryStore(测试里复用已加载的 store 做并发断言);\n * 默认 `await RushRegistryStore.load({ home })`。\n */\n registry?: RushRegistryStore;\n\n /**\n * 注入\"装新后的新格式 plugin.json 路径解析器\"——测试里可以指向 fixture。\n * 默认用 `ClaudeCodePaths.pluginManifestPath(ref, version)`。\n */\n newManifestPathResolver?: (\n home: string,\n ref: PluginRef,\n version: string\n ) => string;\n\n /**\n * 人看输出回调(stdout/stderr)。v1 默认为 no-op——上层 CLI 决定是否打印\n * `ⓘ Detected rush-ai 0.7.x installation...` 这类行。\n *\n * 分两类:`info` / `warn`——失败走 warn,进度走 info。\n */\n reporter?: MigrationReporter;\n}\n\nexport interface MigrationReporter {\n info?: (line: string) => void;\n warn?: (line: string) => void;\n}\n\nexport type MaybeRunMigrationOutcome =\n | { kind: 'skipped'; reason: SkipReason }\n | { kind: 'already-done'; fromVersion?: string }\n | { kind: 'success'; fromVersion: string; cleanupIssues?: string[] }\n | { kind: 'failure'; reason: string; stage: MigrationStage };\n\nexport type SkipReason = 'env-flag' | 'non-rush-plugin' | 'no-legacy-traces';\n\nexport type MigrationStage = 'install-new' | 'verify-new' | 'registry-write';\n\n// ---------------------------------------------------------------------------\n// 主 API\n// ---------------------------------------------------------------------------\n\n/**\n * Pre-install hook 入口。调用方:`install.ts` 在 resolvePlugin 之后、Installer 之前。\n *\n * 契约(**铁律**):\n * - **永不抛错**——任何内部异常(registry load / registry save / IO / Installer bug)\n * 都被吸收到 `MaybeRunMigrationOutcome`,install 主流程不被打断\n * - 对非 `rush@rush-marketplace` 立即 no-op\n * - `RUSH_SKIP_MIGRATION=1` 立即 no-op\n * - 已迁移过(registry.migrations 有 ok 记录)立即 no-op\n * - 上次失败(status='failed')→ 再次尝试(用户可能已修问题)\n * - 成功:装新 → 验证 → 清旧 → registry 记 ok\n * - 三阶段失败(stage):\n * - `install-new` :Installer 返回 failed/partial/skipped 或 Installer 抛异常\n * - `verify-new` :Installer 成功但新格式 plugin.json 读不回\n * - `registry-write`:registry load / save 失败(包括各失败分支\"记 failed\"本身\n * 再失败的情况;此时走 log + 返回 registry-write 失败)\n * - 任何失败分支都**保留新旧并存**(不清旧),并追加写 `migration-failed.log`\n */\nexport async function maybeRunMigration(\n input: MaybeRunMigrationInput\n): Promise<MaybeRunMigrationOutcome> {\n const home = input.home ?? homedir();\n const now = input.now ?? (() => new Date().toISOString());\n const envGet = input.env ?? ((key) => process.env[key]);\n const reporter = input.reporter ?? {};\n\n // 0. RUSH_SKIP_MIGRATION=1 → no-op\n // plan §10 明确把\"关 migration hook\"作为回滚手段。\n // 任何非空 \"0\" / \"false\" / \"no\" 之外的值都视为启用跳过——用户通常写 \"1\"。\n const skipValue = envGet(SKIP_MIGRATION_ENV);\n if (isTruthyEnvValue(skipValue)) {\n return { kind: 'skipped', reason: 'env-flag' };\n }\n\n // 1. 只在装 rush@rush-marketplace 时才迁移——装其他插件不需要动用户老状态\n if (\n input.ref.name !== RUSH_AI_PLUGIN_NAME ||\n input.ref.marketplace !== RUSH_AI_MARKETPLACE_NAME\n ) {\n return { kind: 'skipped', reason: 'non-rush-plugin' };\n }\n\n // 2. 加载 registry,检查已迁移记录(幂等)\n // registry 加载失败本身**不**中断 hook——fall through 到后续流程;\n // 真正 registry 写盘失败会走 registry-write 失败分支。\n let registry: RushRegistryStore;\n try {\n registry = input.registry ?? (await RushRegistryStore.load({ home }));\n } catch (err) {\n // Registry 损坏:我们宁可不迁移也不在损坏的 registry 上操作。\n // 写 log 记一笔方便排查,整体视为 failure。\n const reason = errorMessage(err);\n await appendMigrationFailure(\n `registry load failed before migration: ${reason}`,\n { home, now }\n );\n reporter.warn?.(\n `⚠ Migration skipped: rush-ai registry load failed (${reason})`\n );\n return { kind: 'failure', reason, stage: 'registry-write' };\n }\n\n const existing = registry.getMigration(LEGACY_MIGRATION_KEY);\n if (existing && existing.status === 'ok') {\n // 幂等:已成功迁移过,直接跳过(即使磁盘上残留一些老痕迹也不再动——\n // 用户可能自己重新创建了这些文件;status=ok 的不变式由上次迁移成功保证)\n return {\n kind: 'already-done',\n ...(existing.fromVersion !== undefined\n ? { fromVersion: existing.fromVersion }\n : {}),\n };\n }\n\n // 3. 检测老痕迹——没检测到 → no-op(全新用户或上次已经清理过)\n const detection = await detectLegacyInstall({ home });\n if (!detection.detected) {\n return { kind: 'skipped', reason: 'no-legacy-traces' };\n }\n\n const fromVersion = await detectLegacyVersion({ home });\n reporter.info?.(\n `ⓘ Detected rush-ai ${fromVersion} installation at ~/.rush/plugins/claude-code/`\n );\n reporter.info?.(' Migrating to native plugin format...');\n\n // 4. 装新(严格要求 ResolvedPlugin 已传入——否则没法装)\n if (!input.resolvedPlugin) {\n // 没有 ResolvedPlugin 无法继续——记 failure,让上层知道问题\n const reason = 'resolvedPlugin not provided to maybeRunMigration';\n await appendMigrationFailure(reason, { home, now });\n const saveErr = await safeRecordMigration(\n registry,\n 'failed',\n { now: now(), fromVersion, notes: reason },\n { home, now }\n );\n reporter.warn?.(`⚠ Migration failed: ${reason}`);\n if (saveErr) {\n return { kind: 'failure', reason: saveErr, stage: 'registry-write' };\n }\n return { kind: 'failure', reason, stage: 'install-new' };\n }\n\n const installerFactory =\n input.installerFactory ?? ((h) => new ClaudeCodeInstaller({ home: h }));\n const installer = installerFactory(home);\n\n try {\n const installResult = await installer.install(input.resolvedPlugin, {\n force: false,\n dryRun: false,\n });\n if (installResult.status === 'failed') {\n const reason =\n installResult.errors?.[0] ??\n 'Claude Code installer returned failed without error details';\n await appendMigrationFailure(`install new format failed: ${reason}`, {\n home,\n now,\n detail: installResult.errors?.slice(1).join('\\n'),\n });\n const saveErr = await safeRecordMigration(\n registry,\n 'failed',\n { now: now(), fromVersion, notes: `install-new: ${reason}` },\n { home, now }\n );\n printFailureGuidance(reporter, reason);\n if (saveErr) {\n return { kind: 'failure', reason: saveErr, stage: 'registry-write' };\n }\n return { kind: 'failure', reason, stage: 'install-new' };\n }\n // `partial` 在 v1 不允许(spec §1.4 \"Installer 实现不得构造 partial\")——\n // 但防御性:若 Installer 违反契约返回 partial,我们**不**当作成功继续 cleanup,\n // 否则可能在新格式未完整安装时清掉旧格式(违反 plan §4.3 安全铁律)。\n // 统一降级为 install-new 失败,保留双份 + 写 log + registry failed。\n if (installResult.status === 'partial') {\n const reason =\n \"Claude Code installer returned 'partial' which is not permitted in v1 (spec §1.4); treating as install-new failure to preserve legacy\";\n await appendMigrationFailure(`install new format partial: ${reason}`, {\n home,\n now,\n detail: installResult.errors?.join('\\n'),\n });\n const saveErr = await safeRecordMigration(\n registry,\n 'failed',\n { now: now(), fromVersion, notes: `install-new partial: ${reason}` },\n { home, now }\n );\n printFailureGuidance(reporter, reason);\n if (saveErr) {\n return { kind: 'failure', reason: saveErr, stage: 'registry-write' };\n }\n return { kind: 'failure', reason, stage: 'install-new' };\n }\n // 'skipped' 代表 ~/.claude 目录不存在(非 Claude Code 用户)——\n // 迁移本来就不适用;记 failed 带清晰 notes 让 registry 能看到\n if (installResult.status === 'skipped') {\n const reason =\n 'Claude Code not detected (~/.claude missing); nothing to migrate';\n const saveErr = await safeRecordMigration(\n registry,\n 'failed',\n { now: now(), fromVersion, notes: `install-new skipped: ${reason}` },\n { home, now }\n );\n // 不写 migration-failed.log——这不是\"失败\",是环境不支持\n reporter.info?.(`ⓘ Migration skipped: ${reason}`);\n if (saveErr) {\n return { kind: 'failure', reason: saveErr, stage: 'registry-write' };\n }\n return { kind: 'failure', reason, stage: 'install-new' };\n }\n // 仅 'ok' 才进入后续 verify 阶段\n reporter.info?.(\n ` ✓ Copied skills to ~/.claude/plugins/cache/${input.ref.marketplace}/${input.ref.name}/${input.resolvedPlugin.version}/`\n );\n reporter.info?.(' ✓ Registered in installed_plugins.json');\n } catch (err) {\n // Installer 抛异常(bug 级)——同样走 failed 分支,不复制到 install 命令\n const reason = errorMessage(err);\n await appendMigrationFailure(`install new format threw: ${reason}`, {\n home,\n now,\n });\n const saveErr = await safeRecordMigration(\n registry,\n 'failed',\n { now: now(), fromVersion, notes: `install-new threw: ${reason}` },\n { home, now }\n );\n printFailureGuidance(reporter, reason);\n if (saveErr) {\n return { kind: 'failure', reason: saveErr, stage: 'registry-write' };\n }\n return { kind: 'failure', reason, stage: 'install-new' };\n }\n\n // 5. 验证新格式能加载(快速 dry-run:读回 plugin.json)\n const resolver =\n input.newManifestPathResolver ??\n ((h, r, v) => {\n const paths = new ClaudeCodePaths(h);\n return paths.pluginManifestPath(r, v);\n });\n const manifestPath = resolver(home, input.ref, input.resolvedPlugin.version);\n\n const verifyResult = await verifyNewFormat(\n manifestPath,\n input.ref.name,\n input.resolvedPlugin.version\n );\n if (!verifyResult.ok) {\n const reason = verifyResult.error;\n await appendMigrationFailure(`verify new format failed: ${reason}`, {\n home,\n now,\n detail: `expected manifest at ${manifestPath}`,\n });\n const saveErr = await safeRecordMigration(\n registry,\n 'failed',\n { now: now(), fromVersion, notes: `verify-new: ${reason}` },\n { home, now }\n );\n printFailureGuidance(reporter, reason);\n if (saveErr) {\n return { kind: 'failure', reason: saveErr, stage: 'registry-write' };\n }\n return { kind: 'failure', reason, stage: 'verify-new' };\n }\n reporter.info?.(' ✓ Verified new format loads correctly');\n\n // 6. 验证通过 → 清旧\n const cleanupResult = await cleanupLegacyInstall({ home });\n const cleanupSummary = summarizeCleanup(cleanupResult);\n if (cleanupResult.legacyDir.ok) {\n reporter.info?.(' ✓ Cleaned up legacy ~/.rush/plugins/claude-code/');\n }\n if (cleanupResult.skillSymlink.ok) {\n reporter.info?.(' ✓ Removed legacy symlink ~/.claude/skills/rush-task');\n }\n if (cleanupResult.settingsMcp.ok) {\n reporter.info?.(\" ✓ Removed 'rush' from settings.json.mcpServers\");\n }\n if (cleanupSummary.failed) {\n // 清理 best-effort 失败——整体仍视为迁移成功(新格式装好了),但记 notes\n // 让 registry 能看到残留,下次再 install 时会重新走 registry.getMigration\n // → ok 分支跳过,不会重试清理。\n await appendMigrationFailure(\n `cleanup partial failure after successful install: ${cleanupSummary.reasons.join(\n '; '\n )}`,\n { home, now }\n );\n }\n\n // 7. registry 记 ok\n const okSaveErr = await safeRecordMigration(\n registry,\n 'ok',\n {\n now: now(),\n fromVersion,\n ...(cleanupSummary.failed\n ? { notes: `cleanup issues: ${cleanupSummary.reasons.join('; ')}` }\n : {}),\n },\n { home, now }\n );\n if (okSaveErr) {\n // registry 写失败——不阻断用户(新格式已装好 + 老的已清),但需要上层知道\n // 下次 install 不会走 `already-done` 短路(registry 没标成 ok),会再跑一次\n // 迁移,但 legacy 已经清掉,detection 应返回 no-legacy-traces。\n reporter.warn?.(\n `⚠ Migration completed but rush-ai registry write failed: ${okSaveErr}`\n );\n return { kind: 'failure', reason: okSaveErr, stage: 'registry-write' };\n }\n\n return {\n kind: 'success',\n fromVersion,\n ...(cleanupSummary.failed ? { cleanupIssues: cleanupSummary.reasons } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\n/** 检查环境变量值是否代表 \"truthy\"(\"1\" / \"true\" / \"yes\" / \"on\" 都算)。 */\nfunction isTruthyEnvValue(value: string | undefined): boolean {\n if (value === undefined || value === '') return false;\n const normalized = value.trim().toLowerCase();\n if (normalized === '' || normalized === '0' || normalized === 'false') {\n return false;\n }\n if (normalized === 'no' || normalized === 'off') return false;\n return true;\n}\n\ninterface VerifyResult {\n ok: boolean;\n error: string;\n}\n\n/**\n * 验证新格式 plugin.json 能正常读回 + 匹配期望的 name。\n *\n * 宽松:version 允许 mismatch(Installer 已按请求 version 写入,若 manifest 里\n * 是空字符串或别的也视为文件完整性 ok——Installer 侧已有 schema 校验)。\n *\n * 不验证目录完整性(cache dir 结构)——Installer 的写入保证了这点;dry-run\n * 目的是\"新格式磁盘可见\"即可。\n */\nasync function verifyNewFormat(\n manifestPath: string,\n expectedName: string,\n _expectedVersion: string\n): Promise<VerifyResult> {\n try {\n await access(manifestPath);\n } catch (err) {\n return {\n ok: false,\n error: `manifest not found at ${manifestPath}: ${errorMessage(err)}`,\n };\n }\n\n let raw: string;\n try {\n raw = await readFile(manifestPath, 'utf8');\n } catch (err) {\n return {\n ok: false,\n error: `manifest read failed: ${errorMessage(err)}`,\n };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n return {\n ok: false,\n error: `manifest JSON parse failed: ${errorMessage(err)}`,\n };\n }\n\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return {\n ok: false,\n error: 'manifest top-level is not an object',\n };\n }\n const obj = parsed as Record<string, unknown>;\n if (obj.name !== expectedName) {\n return {\n ok: false,\n error: `manifest name mismatch: expected \"${expectedName}\", got ${JSON.stringify(\n obj.name\n )}`,\n };\n }\n\n return { ok: true, error: '' };\n}\n\n/**\n * 读-改-写 registry.migrations 记录。\n *\n * 并发冲突由 RushRegistryStore 内置 mtime 保护 + 一次重试处理。\n *\n * 可能抛错(JSON 损坏 / mtime 冲突失败 / schema mismatch / 磁盘 IO)。\n * 失败分支路径里应改用 `safeRecordMigration` —— 不抛错,让 outcome 降级为\n * `registry-write` 失败。\n */\nasync function recordMigration(\n registry: RushRegistryStore,\n status: MigrationRecord['status'],\n fields: { now: string; fromVersion?: string; notes?: string }\n): Promise<void> {\n const record: MigrationRecord = {\n migratedAt: fields.now,\n status,\n ...(fields.fromVersion !== undefined\n ? { fromVersion: fields.fromVersion }\n : {}),\n ...(fields.notes !== undefined ? { notes: fields.notes } : {}),\n };\n registry.setMigration(LEGACY_MIGRATION_KEY, record);\n await registry.save();\n}\n\n/**\n * 不抛错版本——在失败分支里记 status=failed 时使用。\n *\n * 契约:`maybeRunMigration` 永不抛错;失败分支里若连 registry 都写不动(磁盘满、\n * 并发冲突、schema mismatch 等),只能写 log + 返回非 null error——由调用方决定\n * 是继续返回原阶段(install-new/verify-new)还是升级为 registry-write 失败。\n *\n * 返回 `undefined` 代表写入成功,否则返回失败原因字符串。\n */\nasync function safeRecordMigration(\n registry: RushRegistryStore,\n status: MigrationRecord['status'],\n fields: { now: string; fromVersion?: string; notes?: string },\n logOpts: { home: string; now: () => string }\n): Promise<string | undefined> {\n try {\n await recordMigration(registry, status, fields);\n return undefined;\n } catch (err) {\n const reason = errorMessage(err);\n await appendMigrationFailure(\n `registry save failed while recording status=${status}: ${reason}`,\n { home: logOpts.home, now: logOpts.now }\n );\n return reason;\n }\n}\n\nfunction printFailureGuidance(\n reporter: MigrationReporter,\n reason: string\n): void {\n reporter.warn?.(`⚠ Migration failed: ${reason}`);\n reporter.warn?.(' → New format installed to ~/.claude/plugins/cache/...');\n reporter.warn?.(\n ' → Legacy at ~/.rush/plugins/claude-code/ preserved (still functional)'\n );\n reporter.warn?.(' → See ~/.rush/plugins/migration-failed.log for details');\n reporter.warn?.(' → Continuing with install...');\n}\n\nfunction errorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n\n// 避免 PLUGIN_MANIFEST_RELATIVE unused-import warning——\n// 便于后续若 resolver 需要直接参考 plugin.json 相对路径时可用。\nexport const __PLUGIN_MANIFEST_RELATIVE = PLUGIN_MANIFEST_RELATIVE;\n","/**\n * Plugin resolver:从 marketplace 解析出 ResolvedPlugin(task-4 产物)。\n *\n * Source of truth:\n * - `specs/installer-interface.md` §1.3 ResolvedPlugin / PluginManifest\n * - `specs/plugin-schemas.md` §1.5 plugin.json schema\n *\n * 流程(对齐 task-4 prompt \"实现步骤\"):\n * 1. 在 marketplace 的本地 cache(`ResolvedMarketplace.rootDir`)里按\n * `marketplace.json.plugins[].source.path`(或字符串 source)定位 plugin 子目录\n * 2. 读 `<plugin-dir>/.claude-plugin/plugin.json`(缺失 → `PluginManifestNotFoundError`)\n * 3. 校验 `plugin.json.version` 非空 + 非 \"unknown\"(`InvalidPluginVersionError`)\n * 4. 扫描 capabilities(commands / skills / rules / hooks / mcp)\n * 5. MCP command 规范化:**仅**对 rush-ai 自己的插件\n * (`ref.name === 'rush' && ref.marketplace === 'rush-marketplace'`)做绝对路径解析,\n * 其他所有 plugin **透传**不改(见 plan §7.1)\n * 6. 返回 `ResolvedPlugin`\n *\n * 接口设计:接受 `ResolvedMarketplace`(task-3 `MarketplaceCache.get()` 产物)而非\n * MarketplaceCache 本身——职责单一 + 便于 CLI 层手工构造 / 测试里直接 fixture。\n */\n\nimport {\n isAbsolute,\n relative as pathRelative,\n resolve as pathResolve,\n sep as pathSep,\n} from 'node:path';\nimport type {\n PluginCapability,\n PluginManifest,\n PluginRef,\n ResolvedPlugin,\n} from '../installers/types.js';\nimport type {\n MarketplacePluginEntry,\n ResolvedMarketplace,\n} from '../marketplaces/types.js';\nimport { scanCapabilities } from './capabilities.js';\nimport {\n PluginNotFoundInMarketplaceError,\n PluginSourceUnresolvableError,\n} from './errors.js';\nimport { readPluginManifest } from './manifest.js';\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/**\n * resolver 的可注入选项。\n */\nexport interface ResolvePluginOptions {\n /**\n * 解析 `rush-ai` binary 的绝对路径。仅在 ref 为 rush-ai 自己的插件\n * (`rush@rush-marketplace`)且 plugin.json.mcpServers.rush.command 非绝对路径时调用。\n *\n * 默认实现 = 当前 node 进程的 `process.argv[1]`(rush-ai CLI 入口),失败时\n * 回退到原值(保持兼容,不让 resolver 因为 rush-ai 还没发布就挂掉)。\n *\n * 测试里注入固定字符串更稳定。\n */\n readonly rushAiBinaryResolver?: () => string | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// 常量:rush-ai 自己的插件 identifier(MCP command 绝对路径规则触发条件)\n// ---------------------------------------------------------------------------\n\n/**\n * rush-ai 自己发布的插件的 PluginRef identifier。\n *\n * plan §7.1 约定:仅对本 ref 做 MCP command 绝对路径规范化,其他所有 plugin 透传。\n */\nexport const RUSH_AI_PLUGIN_NAME = 'rush' as const;\nexport const RUSH_AI_MARKETPLACE_NAME = 'rush-marketplace' as const;\n\n/**\n * rush 插件内 MCP server 的 key(plan §7 示例 / spec §1.3)。\n *\n * 只对该 key 的 command 做绝对路径规范化;其他 key(即使 rush plugin 自己声明了多个\n * MCP server)保持透传。\n */\nexport const RUSH_MCP_SERVER_KEY = 'rush' as const;\n\n// ---------------------------------------------------------------------------\n// 主 API\n// ---------------------------------------------------------------------------\n\n/**\n * 从 marketplace 解析 plugin,产出 Installer 可直接消费的 ResolvedPlugin。\n *\n * @param ref plugin 引用(含 name + marketplace name)\n * @param marketplace 已 resolve 的 marketplace(task-3 `MarketplaceCache.get()` 产物)\n * @param options 可选注入项(见 `ResolvePluginOptions`)\n *\n * @throws {PluginNotFoundInMarketplaceError} marketplace.json 里没有该 plugin\n * @throws {PluginSourceUnresolvableError} plugin 条目的 source 字段不可解析为本地路径\n * @throws {PluginManifestNotFoundError} plugin source 目录缺 `.claude-plugin/plugin.json`\n * @throws {PluginManifestCorruptError} plugin.json JSON 损坏 / 顶层 shape 错\n * @throws {InvalidPluginVersionError} plugin.json.version 缺失 / 空串 / \"unknown\"\n */\nexport async function resolvePlugin(\n ref: PluginRef,\n marketplace: ResolvedMarketplace,\n options: ResolvePluginOptions = {}\n): Promise<ResolvedPlugin> {\n // 1. 在 marketplace.json 里找 plugin 条目\n const entry = findPluginEntry(ref, marketplace);\n\n // 2. 定位 plugin 源目录(marketplace rootDir 下的子目录)\n const sourceDir = resolvePluginSourceDir(ref, marketplace, entry);\n\n // 3. 读 plugin.json + 校验 version\n const manifest = await readPluginManifest(ref.name, sourceDir);\n\n // 4. 扫描 capabilities\n const capabilities = await scanCapabilities(sourceDir, manifest);\n\n // 5. MCP command 绝对路径规范化(仅 rush-ai 自己的插件)\n const normalizedManifest = normalizeRushMcpCommand(\n ref,\n manifest,\n options.rushAiBinaryResolver\n );\n\n // 6. 组装 ResolvedPlugin\n return {\n ref,\n version: normalizedManifest.version,\n sourceDir,\n manifest: normalizedManifest,\n capabilities,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\n/**\n * 在 marketplace.manifest.plugins 数组里找 `ref.name` 对应的条目。\n */\nfunction findPluginEntry(\n ref: PluginRef,\n marketplace: ResolvedMarketplace\n): MarketplacePluginEntry {\n const entry = marketplace.manifest.plugins.find((p) => p.name === ref.name);\n if (!entry) {\n const available = marketplace.manifest.plugins.map((p) => p.name);\n throw new PluginNotFoundInMarketplaceError(\n ref.name,\n marketplace.name,\n available\n );\n }\n return entry;\n}\n\n/**\n * 由 marketplace plugin 条目的 `source` 字段定位 plugin 子目录的绝对路径。\n *\n * 支持的 source 形态:\n * - 缺失 `source`:默认相对路径 `plugins/<name>/`(对齐 phase0-spike fixture 约定)\n * - 字符串:作为相对 marketplace.rootDir 的路径使用(允许 `./foo` / `foo/bar`)\n * - 对象形态:\n * - `{ source: 'local', path: <相对路径> }` → 用 path\n * - 含 `path` 字段但没 `source` → 用 path(宽容)\n * - 含 `url` 但无 `path`(如 git-subdir 指向外部仓库的 url 但未声明子目录)\n * → 抛 `PluginSourceUnresolvableError`(Phase 1 不支持远程拉取子目录)\n *\n * 所有返回路径都是**绝对路径**,且必须 resolve 到 marketplace.rootDir 下(防止 `../`\n * 逃逸——目录逃逸在 Phase 1 不支持,避免意外读取 marketplace 以外的目录)。\n */\nfunction resolvePluginSourceDir(\n ref: PluginRef,\n marketplace: ResolvedMarketplace,\n entry: MarketplacePluginEntry\n): string {\n const relPath = extractRelativeSourcePath(ref, marketplace, entry);\n if (isAbsolute(relPath)) {\n throw new PluginSourceUnresolvableError(\n ref.name,\n marketplace.name,\n `source path must be relative to marketplace root; got absolute '${relPath}'`\n );\n }\n const rootAbs = pathResolve(marketplace.rootDir);\n const abs = pathResolve(rootAbs, relPath);\n // 防止 `../` 逃逸到 marketplace 以外。\n //\n // 用 `path.relative(root, abs)` 判定,下列三种结果都视为逃逸:\n // 1. rel === '..' → 恰好指向 root 的上一级\n // 2. rel.startsWith(`..${path.sep}`) → `../foo` / `..\\foo`(跨平台分隔符)\n // 3. isAbsolute(rel) → Windows 跨盘符时 `path.relative` 会直接\n // 返回 `to` 的绝对路径(Node 文档行为)\n // 比老写法 `startsWith(root + '/')` 更稳,后者在 Windows 下会因字面 '/' 与\n // `path.resolve` 产出的反斜杠不匹配导致误判/漏判。\n const rel = pathRelative(rootAbs, abs);\n const isEscaping =\n rel === '..' || rel.startsWith(`..${pathSep}`) || isAbsolute(rel);\n if (isEscaping) {\n throw new PluginSourceUnresolvableError(\n ref.name,\n marketplace.name,\n `resolved plugin source dir '${abs}' escapes marketplace root '${rootAbs}'`\n );\n }\n return abs;\n}\n\n/**\n * 从 plugin 条目的 `source` 字段抽出\"相对 marketplace rootDir 的路径\"。\n *\n * 不碰磁盘——返回的路径可能指向不存在的目录,后续 `readPluginManifest` 会发现。\n */\nfunction extractRelativeSourcePath(\n ref: PluginRef,\n marketplace: ResolvedMarketplace,\n entry: MarketplacePluginEntry\n): string {\n if (entry.source === undefined || entry.source === null) {\n // 默认约定:`plugins/<name>/`(对齐 phase0-spike fixture)\n return `plugins/${entry.name}`;\n }\n\n if (typeof entry.source === 'string') {\n const s = entry.source.trim();\n if (s.length === 0) {\n throw new PluginSourceUnresolvableError(\n ref.name,\n marketplace.name,\n 'source string is empty'\n );\n }\n return s;\n }\n\n if (typeof entry.source === 'object' && !Array.isArray(entry.source)) {\n const src = entry.source as {\n source?: unknown;\n type?: unknown;\n path?: unknown;\n url?: unknown;\n };\n if (typeof src.path === 'string' && src.path.trim().length > 0) {\n return src.path.trim();\n }\n const kind =\n typeof src.source === 'string'\n ? src.source\n : typeof src.type === 'string'\n ? src.type\n : 'unknown';\n throw new PluginSourceUnresolvableError(\n ref.name,\n marketplace.name,\n `source object (kind='${kind}') has no 'path' field; Phase 1 resolver only supports local paths relative to marketplace root`\n );\n }\n\n throw new PluginSourceUnresolvableError(\n ref.name,\n marketplace.name,\n `source field has unexpected type '${typeof entry.source}'`\n );\n}\n\n/**\n * 仅对 rush-ai 自己的插件(`rush@rush-marketplace`)做 MCP command 绝对路径规范化。\n *\n * 其他 plugin 透传——即使 command 写的是相对路径,也不替换(由各家 Installer 按\n * 自己的规则处理,如 Codex `.mcp.json` 的 cwd 相对路径)。\n *\n * 触发条件:\n * - `ref.name === 'rush'` 且 `ref.marketplace === 'rush-marketplace'`\n * - `manifest.mcpServers` 是对象形态(不是 Codex 的字符串外部文件引用)\n * - 存在 `mcpServers.rush` 且其 `command` 非绝对路径\n *\n * 返回**新对象**(不 mutate 原 manifest),避免副作用。\n */\nfunction normalizeRushMcpCommand(\n ref: PluginRef,\n manifest: PluginManifest,\n rushAiBinaryResolver: (() => string | undefined) | undefined\n): PluginManifest {\n if (\n ref.name !== RUSH_AI_PLUGIN_NAME ||\n ref.marketplace !== RUSH_AI_MARKETPLACE_NAME\n ) {\n return manifest;\n }\n const servers = manifest.mcpServers;\n if (servers === undefined || typeof servers === 'string') {\n return manifest;\n }\n const rushServer = servers[RUSH_MCP_SERVER_KEY];\n if (!rushServer || typeof rushServer !== 'object') {\n return manifest;\n }\n const currentCommand = rushServer.command;\n if (typeof currentCommand !== 'string' || currentCommand.length === 0) {\n return manifest;\n }\n if (isAbsolute(currentCommand)) {\n return manifest;\n }\n\n const resolved = (rushAiBinaryResolver ?? defaultRushAiBinaryResolver)();\n if (!resolved || !isAbsolute(resolved)) {\n // 兜底:resolve 不出绝对路径就保持原 command(不让 resolver 阶段挂掉)。\n // 下游 Installer 若需要强制绝对路径,会在 install 阶段报错。\n return manifest;\n }\n\n const nextServers = {\n ...servers,\n [RUSH_MCP_SERVER_KEY]: { ...rushServer, command: resolved },\n };\n return { ...manifest, mcpServers: nextServers };\n}\n\n/**\n * 默认 rush-ai binary 解析:取当前 node 进程的 argv[1]。\n *\n * 实际场景里 rush-ai CLI 入口会填到 argv[1];测试里应注入自定义 resolver。\n */\nfunction defaultRushAiBinaryResolver(): string | undefined {\n const argv1 = process.argv[1];\n if (typeof argv1 === 'string' && isAbsolute(argv1)) {\n return argv1;\n }\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// 便于下游引用的类型再导出\n// ---------------------------------------------------------------------------\n\nexport type { PluginRef, ResolvedPlugin, PluginCapability };\n","/**\n * 扫描 plugin 源目录产出 capability 清单(task-4 产物)。\n *\n * Source of truth: `specs/installer-interface.md` §1.2 PluginCapability + plan §7.1。\n *\n * - `commands/` 目录下任一 `.md` 文件 → `commands`\n * - `skills/` 目录下任一子目录含 `SKILL.md`,或 Claude-style `.skills`\n * 目录里的 markdown → `skills`\n * - `rules/` 目录下任一 `.md` 文件 → `rules`\n * - `hooks/` 目录下任一 `.json` / `.sh` / `.js` 文件 → `hooks`\n * - plugin.json 顶层有 `mcpServers` 字段(对象或字符串) → `mcp`\n *\n * 设计原则:\n * - 宽容扫描:目录不存在视为\"没有该能力\"(不抛错)\n * - 只扫一层(commands/rules/hooks)或两层(skills/<name>/SKILL.md);\n * `.skills` 允许递归找 markdown,因为 Claude plugin 常按主题分组\n * - 结果顺序**固定**:按 `CAPABILITY_ORDER` 产出,便于测试断言 + 人眼 diff\n */\n\nimport { type Dirent, constants as fsConstants } from 'node:fs';\nimport { access, readdir, stat } from 'node:fs/promises';\nimport { extname, resolve } from 'node:path';\nimport type { PluginCapability, PluginManifest } from '../installers/types.js';\n\n/**\n * Capability 枚举的规范顺序(测试断言 + 格式化输出用)。\n */\nexport const CAPABILITY_ORDER: readonly PluginCapability[] = [\n 'commands',\n 'skills',\n 'rules',\n 'hooks',\n 'mcp',\n] as const;\n\nconst HOOK_EXTENSIONS = new Set(['.json', '.sh', '.js']);\n\n/**\n * 扫描 plugin 源目录 + 检查 manifest,返回实际发现的 capability 清单。\n *\n * @param sourceDir plugin 源目录绝对路径\n * @param manifest 已解析的 plugin.json(用于检查 `mcpServers`)\n * @returns capability 清单,按 `CAPABILITY_ORDER` 排序\n */\nexport async function scanCapabilities(\n sourceDir: string,\n manifest: PluginManifest\n): Promise<PluginCapability[]> {\n const found = new Set<PluginCapability>();\n\n if (await hasCommands(sourceDir)) {\n found.add('commands');\n }\n if (await hasSkills(sourceDir)) {\n found.add('skills');\n }\n if (await hasRules(sourceDir)) {\n found.add('rules');\n }\n if (await hasHooks(sourceDir)) {\n found.add('hooks');\n }\n if (hasMcp(manifest)) {\n found.add('mcp');\n }\n\n return CAPABILITY_ORDER.filter((cap) => found.has(cap));\n}\n\n// ---------------------------------------------------------------------------\n// 单能力探测函数(导出便于独立测试)\n// ---------------------------------------------------------------------------\n\n/** `commands/` 下任一 `.md` 文件 → 有 commands 能力。 */\nexport async function hasCommands(sourceDir: string): Promise<boolean> {\n return dirHasFileWithExt(resolve(sourceDir, 'commands'), '.md');\n}\n\n/**\n * `skills/` 下任一子目录含 `SKILL.md`,或 `.skills` 目录里的 markdown → 有\n * skills 能力。\n *\n * 注意:skill 判定必须以 `SKILL.md` 为准(不是目录下随便有 `.md`)——对齐\n * Claude Code / Codex 原生扫描行为,也避免误报。\n */\nexport async function hasSkills(sourceDir: string): Promise<boolean> {\n const skillsDir = resolve(sourceDir, 'skills');\n if (await dirExists(skillsDir)) {\n let entries: Dirent[];\n try {\n entries = await readdir(skillsDir, { withFileTypes: true });\n } catch {\n return false;\n }\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const skillMdPath = resolve(skillsDir, entry.name, 'SKILL.md');\n if (await fileExists(skillMdPath)) {\n return true;\n }\n }\n }\n return hasClaudeStyleSkills(sourceDir);\n}\n\n/** `rules/` 下任一 `.md` 文件 → 有 rules 能力。 */\nexport async function hasRules(sourceDir: string): Promise<boolean> {\n return dirHasFileWithExt(resolve(sourceDir, 'rules'), '.md');\n}\n\n/** `hooks/` 下任一 `.json` / `.sh` / `.js` 文件 → 有 hooks 能力。 */\nexport async function hasHooks(sourceDir: string): Promise<boolean> {\n const hooksDir = resolve(sourceDir, 'hooks');\n if (!(await dirExists(hooksDir))) {\n return false;\n }\n let entries: Dirent[];\n try {\n entries = await readdir(hooksDir, { withFileTypes: true });\n } catch {\n return false;\n }\n for (const entry of entries) {\n if (!entry.isFile()) continue;\n if (HOOK_EXTENSIONS.has(extname(entry.name).toLowerCase())) {\n return true;\n }\n }\n return false;\n}\n\n/** plugin.json 顶层有非空 `mcpServers` → 有 mcp 能力。 */\nexport function hasMcp(manifest: PluginManifest): boolean {\n const m = manifest.mcpServers;\n if (m === undefined || m === null) return false;\n if (typeof m === 'string') {\n return m.length > 0;\n }\n // object: 必须至少有一个 server key\n return Object.keys(m).length > 0;\n}\n\n// ---------------------------------------------------------------------------\n// 小工具\n// ---------------------------------------------------------------------------\n\nasync function dirHasFileWithExt(\n dirPath: string,\n ext: string\n): Promise<boolean> {\n if (!(await dirExists(dirPath))) {\n return false;\n }\n let entries: Dirent[];\n try {\n entries = await readdir(dirPath, { withFileTypes: true });\n } catch {\n return false;\n }\n const target = ext.toLowerCase();\n return entries.some(\n (e) => e.isFile() && extname(e.name).toLowerCase() === target\n );\n}\n\nasync function hasClaudeStyleSkills(sourceDir: string): Promise<boolean> {\n const dotSkillsDir = resolve(sourceDir, '.skills');\n if (!(await dirExists(dotSkillsDir))) {\n return false;\n }\n async function walk(dirPath: string): Promise<boolean> {\n let entries: Dirent[];\n try {\n entries = await readdir(dirPath, { withFileTypes: true });\n } catch {\n return false;\n }\n for (const entry of entries) {\n const abs = resolve(dirPath, entry.name);\n if (entry.isDirectory()) {\n if (await walk(abs)) return true;\n continue;\n }\n if (entry.isFile() && extname(entry.name).toLowerCase() === '.md') {\n return true;\n }\n }\n return false;\n }\n return walk(dotSkillsDir);\n}\n\nasync function dirExists(p: string): Promise<boolean> {\n try {\n const s = await stat(p);\n return s.isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function fileExists(p: string): Promise<boolean> {\n try {\n await access(p, fsConstants.R_OK);\n const s = await stat(p);\n return s.isFile();\n } catch {\n return false;\n }\n}\n","/**\n * Plugin resolver 错误类型(task-4 产物)。\n *\n * Source of truth: `specs/installer-interface.md` §1.3 / `specs/plugin-schemas.md` §1.5。\n *\n * 设计原则:\n * - 错误类型**细分**,便于 CLI 层按 `instanceof` 给出定向用户提示\n * - 所有错误保留关键字段(pluginName / marketplaceName / manifestPath 等),方便排障\n * - 每个子类通过 `Error.name` 区分(如 `'InvalidPluginVersionError'`)——未显式暴露\n * `code` 字段;CLI 层用 `instanceof` + `Error.name` 分派,而不是按 code 字符串\n */\n\n/** 基类 —— CLI 层统一 `instanceof PluginResolverError` catch。 */\nexport class PluginResolverError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'PluginResolverError';\n }\n}\n\n/**\n * marketplace.json 里找不到请求的 plugin 条目。\n *\n * 触发:`ref.name` 在 marketplace.manifest.plugins 数组里不存在。\n */\nexport class PluginNotFoundInMarketplaceError extends PluginResolverError {\n constructor(\n public readonly pluginName: string,\n public readonly marketplaceName: string,\n public readonly availableNames: readonly string[]\n ) {\n super(\n `Plugin '${pluginName}' not found in marketplace '${marketplaceName}'. ` +\n `Available plugins: ${availableNames.length > 0 ? availableNames.map((n) => `'${n}'`).join(', ') : '(none)'}.`\n );\n this.name = 'PluginNotFoundInMarketplaceError';\n }\n}\n\n/**\n * marketplace.json 里 plugin 条目的 `source` 字段无法落到本地路径。\n *\n * Phase 1 支持的形态:\n * - 字符串:相对 marketplace rootDir 的路径(直接使用)\n * - `{ source: 'local' | ..., path: <相对路径> }`:取 path\n *\n * 不支持:缺 path 的对象(如纯 `{ source: 'git-subdir', url: '...' }`)—— 抛本错误。\n */\nexport class PluginSourceUnresolvableError extends PluginResolverError {\n constructor(\n public readonly pluginName: string,\n public readonly marketplaceName: string,\n public readonly reason: string\n ) {\n super(\n `Cannot resolve local source dir for plugin '${pluginName}' in marketplace '${marketplaceName}': ${reason}`\n );\n this.name = 'PluginSourceUnresolvableError';\n }\n}\n\n/**\n * marketplace cache 里缺 `.claude-plugin/plugin.json` 文件。\n *\n * 触发:`source.path` 指向的目录不存在,或该目录下没有 plugin.json。\n */\nexport class PluginManifestNotFoundError extends PluginResolverError {\n constructor(\n public readonly pluginName: string,\n public readonly expectedManifestPath: string\n ) {\n super(\n `plugin.json not found for plugin '${pluginName}' at '${expectedManifestPath}'. ` +\n `Either the plugin source path is wrong, or the marketplace cache is stale (try 'rush-ai marketplace update').`\n );\n this.name = 'PluginManifestNotFoundError';\n }\n}\n\n/**\n * `.claude-plugin/plugin.json` 文件存在但无法解析(JSON 损坏 / shape 错)。\n */\nexport class PluginManifestCorruptError extends PluginResolverError {\n constructor(\n public readonly manifestPath: string,\n public readonly detail: string\n ) {\n super(\n `plugin.json at '${manifestPath}' is corrupt: ${detail}. Please fix the file or contact the plugin author.`\n );\n this.name = 'PluginManifestCorruptError';\n }\n}\n\n/**\n * `plugin.json.version` 字段缺失/为空/为 \"unknown\"。\n *\n * 修复 #906 P1:Claude Code 默认会把缺 version 的 plugin 落到\n * `cache/<mkt>/<plugin>/unknown/` 坏目录,必须在 resolver 阶段拦下。\n */\nexport class InvalidPluginVersionError extends PluginResolverError {\n constructor(\n public readonly pluginName: string,\n public readonly manifestPath: string,\n public readonly actualValue: string | undefined\n ) {\n super(\n `plugin.json at '${manifestPath}' has invalid version field (got ${\n actualValue === undefined ? 'undefined' : `'${actualValue}'`\n }). ` +\n `Plugin '${pluginName}' must declare a non-empty version (e.g. \"0.1.0\"); ` +\n `'unknown' is not allowed (see #906).`\n );\n this.name = 'InvalidPluginVersionError';\n }\n}\n","/**\n * `.claude-plugin/plugin.json` 读取 + 校验(task-4 产物)。\n *\n * Source of truth: `specs/plugin-schemas.md` §1.5 / `specs/installer-interface.md` §1.3。\n *\n * 读取策略(对齐 marketplace manifest 的\"最小必要字段\"原则):\n * - 必需字段:`name`(非空 string)+ `version`(非空 string,不得为 \"unknown\")\n * - 其他字段原样透传(`description` / `author` / `mcpServers` / `skills` / `interface` ...)\n * - JSON 损坏 / 顶层非 object → `PluginManifestCorruptError`\n * - 缺 / 非法 version → `InvalidPluginVersionError`(修复 #906 P1,与 plan §7.1 一致)\n *\n * **不做语义校验**:mcpServers 的 command 合法性、interface 子字段类型等都不在此 gate;\n * 那些校验属于各家 Installer 的职责或运行时。\n */\n\nimport { constants as fsConstants } from 'node:fs';\nimport { access, readFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport type { PluginManifest } from '../installers/types.js';\nimport {\n InvalidPluginVersionError,\n PluginManifestCorruptError,\n PluginManifestNotFoundError,\n} from './errors.js';\n\n/**\n * plugin.json 相对 plugin source 目录的路径(Claude Code / Codex 共用此位置;\n * Codex 官方样本里也可能用 `.codex-plugin/plugin.json`,但本 resolver 只认\n * `.claude-plugin/plugin.json`——rush-ai marketplace 生成时统一写入此路径)。\n */\nexport const PLUGIN_MANIFEST_RELATIVE_PATH =\n '.claude-plugin/plugin.json' as const;\n\n/**\n * 从 plugin source 目录读取 + 校验 plugin.json。\n *\n * @param pluginName 用于错误消息(来自 PluginRef.name 或 marketplace entry.name)\n * @param sourceDir plugin 源目录绝对路径(marketplace rootDir 下某子目录)\n *\n * @throws {PluginManifestNotFoundError} 文件不存在或不可读\n * @throws {PluginManifestCorruptError} JSON 损坏 / 顶层非 object / name 缺失\n * @throws {InvalidPluginVersionError} version 缺失 / 空串 / \"unknown\"\n */\nexport async function readPluginManifest(\n pluginName: string,\n sourceDir: string\n): Promise<PluginManifest> {\n const manifestPath = resolve(sourceDir, PLUGIN_MANIFEST_RELATIVE_PATH);\n\n try {\n await access(manifestPath, fsConstants.R_OK);\n } catch {\n throw new PluginManifestNotFoundError(pluginName, manifestPath);\n }\n\n const raw = await readFile(manifestPath, 'utf8');\n return parsePluginManifest(pluginName, raw, manifestPath);\n}\n\n/**\n * 从字符串解析 + 校验 plugin.json。\n *\n * 抽出此函数便于测试里直接传字符串,不依赖磁盘。\n */\nexport function parsePluginManifest(\n pluginName: string,\n raw: string,\n sourcePathForError: string\n): PluginManifest {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new PluginManifestCorruptError(\n sourcePathForError,\n (err as Error).message\n );\n }\n\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new PluginManifestCorruptError(\n sourcePathForError,\n 'root must be a JSON object'\n );\n }\n\n const obj = parsed as Record<string, unknown>;\n\n if (typeof obj.name !== 'string' || obj.name.length === 0) {\n throw new PluginManifestCorruptError(\n sourcePathForError,\n \"'name' must be a non-empty string\"\n );\n }\n\n // version 校验 —— 修复 #906 P1\n const version = obj.version;\n if (\n typeof version !== 'string' ||\n version.length === 0 ||\n version.toLowerCase() === 'unknown'\n ) {\n throw new InvalidPluginVersionError(\n pluginName,\n sourcePathForError,\n typeof version === 'string' ? version : undefined\n );\n }\n\n // `mcpServers` 允许 `Record<string, McpServerConfig>` 或 `string`(Codex 外部文件引用)。\n // 只做顶层类型 gate,不展开校验——那是 Installer 的事。\n if (obj.mcpServers !== undefined) {\n const m = obj.mcpServers;\n const isObject = typeof m === 'object' && m !== null && !Array.isArray(m);\n if (typeof m !== 'string' && !isObject) {\n throw new PluginManifestCorruptError(\n sourcePathForError,\n \"'mcpServers' must be an object (Claude Code inline) or string (Codex external file path)\"\n );\n }\n }\n\n return obj as unknown as PluginManifest;\n}\n","/**\n * `rush-ai plugin *` 公用工具(task-10)。\n *\n * 本文件只负责跨命令共享的纯函数:\n * - `parsePluginRef`:解析 `name[@marketplace]` → `PluginRef`(含自动推断 marketplace)\n * - `parseTargets`:解析 `--target claude-code,codex,cursor` → `InstallTarget[]`\n * - `defaultInstallerFactory`:CLI 默认三家 Installer 构造器(`{ home }` 透传)\n * - `buildRegistryEntry` / `applyRegistry`:CLI 层统一把 InstallResult 写入 rush-ai registry\n *\n * 设计原则(对齐 `specs/installer-interface.md` §4 + `specs/cli-commands.md` §2):\n * - Installer 层不写 registry——本文件提供的 `applyRegistry` 是 **唯一** 写入点\n * - Installer 调用次序 = 用户传入的 `--target` 顺序(CLI 不预过滤,透传)\n * - 所有 HOME 注入通过 option 对象传入,测试强制 tmp HOME\n */\n\nimport {\n ClaudeCodeInstaller,\n type ClaudeCodeInstallerOptions,\n CodexInstaller,\n type CodexInstallerOptions,\n CursorInstaller,\n type CursorInstallerOptions,\n type InstallResult,\n type InstallTarget,\n type PluginCapability,\n type PluginInstaller,\n type PluginRef,\n type ResolvedPlugin,\n type RushRegistryEntry,\n type RushRegistryStore,\n} from '../../installers/index.js';\nimport type {\n MarketplaceCache,\n MarketplaceSource,\n ResolvedMarketplace,\n} from '../../marketplaces/index.js';\nimport { RushError } from '../../util/errors.js';\n\n// ---------------------------------------------------------------------------\n// Targets\n// ---------------------------------------------------------------------------\n\n/**\n * 默认 target 集合(与 spec §2.1 \"默认 claude-code,codex,cursor\" 对齐)。\n *\n * CLI 不预过滤——即便某 target 的 IDE 没装,也调用该 Installer 让它自己返回\n * `status: 'skipped'`(spec §3.1)。\n */\nexport const DEFAULT_TARGETS: readonly InstallTarget[] = [\n 'claude-code',\n 'codex',\n 'cursor',\n] as const;\n\nconst VALID_TARGETS = new Set<InstallTarget>(DEFAULT_TARGETS);\n\n/**\n * 解析 `--target` 参数值。\n *\n * 规则:\n * - `undefined` / 空字符串 → 返回 DEFAULT_TARGETS 拷贝\n * - 逗号分隔,`trim` 每段,忽略空段\n * - 去重(保留首次出现顺序)\n * - 任意段不在合法集合 → 抛 `RushError(code='INVALID_TARGET', exitCode=2)`\n *\n * 不再做大小写规范——spec 要求精确匹配 `claude-code` / `codex` / `cursor`。\n */\nexport function parseTargets(raw?: string): InstallTarget[] {\n if (raw === undefined || raw.trim().length === 0) {\n return [...DEFAULT_TARGETS];\n }\n const segments = raw\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n const seen = new Set<InstallTarget>();\n const out: InstallTarget[] = [];\n for (const seg of segments) {\n if (!VALID_TARGETS.has(seg as InstallTarget)) {\n throw new RushError(\n `Invalid --target '${seg}'. Expected one of: ${[...DEFAULT_TARGETS].join(', ')}`,\n { raw: seg },\n 'INVALID_TARGET',\n 2\n );\n }\n const t = seg as InstallTarget;\n if (!seen.has(t)) {\n seen.add(t);\n out.push(t);\n }\n }\n if (out.length === 0) {\n return [...DEFAULT_TARGETS];\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// PluginRef parsing\n// ---------------------------------------------------------------------------\n\n/**\n * 解析 `<name>[@<marketplace>]`。\n *\n * 规则(spec §1.1 + §2.1 自动推断):\n * - `name` 必须非空,不得含 `@` / 空白\n * - `marketplace` 若省略:从 `cache.list()` 聚合后,按 plugin name 反查 marketplace\n * - 仅 1 个命中 → 自动选\n * - ≥ 2 个命中 → 抛 `RushError(code='AMBIGUOUS_PLUGIN', exitCode=2)`\n * - 0 个命中 → 抛 `RushError(code='PLUGIN_NOT_FOUND', exitCode=2)`\n *\n * `cache` 参数允许测试注入 mock。CLI 生产路径调用方自己 new `MarketplaceCache({ home })`。\n */\nexport async function parsePluginRef(\n raw: string,\n cache: MarketplaceCache\n): Promise<PluginRef> {\n const trimmed = raw.trim();\n if (trimmed.length === 0) {\n throw new RushError(\n 'Plugin name must not be empty',\n { raw },\n 'INVALID_PLUGIN_REF',\n 2\n );\n }\n\n const at = trimmed.indexOf('@');\n if (at === 0) {\n throw new RushError(\n `Invalid plugin ref '${trimmed}': name part is empty`,\n { raw },\n 'INVALID_PLUGIN_REF',\n 2\n );\n }\n\n if (at > 0) {\n const name = trimmed.slice(0, at);\n const marketplace = trimmed.slice(at + 1);\n if (marketplace.length === 0) {\n throw new RushError(\n `Invalid plugin ref '${trimmed}': marketplace part is empty after '@'`,\n { raw },\n 'INVALID_PLUGIN_REF',\n 2\n );\n }\n if (marketplace.includes('@')) {\n throw new RushError(\n `Invalid plugin ref '${trimmed}': marketplace must not contain '@'`,\n { raw },\n 'INVALID_PLUGIN_REF',\n 2\n );\n }\n return { name, marketplace };\n }\n\n // 无 @mkt —— 从 cache 反查\n const matches = await findMarketplacesByPluginName(trimmed, cache);\n if (matches.length === 0) {\n throw new RushError(\n `Plugin '${trimmed}' not found in any registered marketplace. ` +\n `Run 'rush-ai marketplace list' to see known marketplaces.`,\n { name: trimmed },\n 'PLUGIN_NOT_FOUND',\n 2\n );\n }\n if (matches.length > 1) {\n throw new RushError(\n `Plugin '${trimmed}' is ambiguous: found in ${matches.length} marketplaces ` +\n `(${matches.join(', ')}). Specify '${trimmed}@<marketplace>' explicitly.`,\n { name: trimmed, candidates: matches },\n 'AMBIGUOUS_PLUGIN',\n 2\n );\n }\n const only = matches[0];\n if (only === undefined) {\n // Node 的 TS 细粒度类型检查兜底(理论上 length===1 时 [0] 必存在)\n throw new RushError(\n `Internal: expected 1 marketplace match for '${trimmed}'`,\n { name: trimmed },\n 'INTERNAL_ERROR',\n 2\n );\n }\n return { name: trimmed, marketplace: only };\n}\n\n/**\n * 扫描所有 cache 里的 marketplace.json,找哪些 marketplace 提供 `pluginName`。\n *\n * 只读不改——并发安全。marketplace.json 损坏时跳过(宽容策略,免让一个坏 marketplace\n * 阻塞整个 install 命令)。\n */\nexport async function findMarketplacesByPluginName(\n pluginName: string,\n cache: MarketplaceCache\n): Promise<string[]> {\n const cached = await cache.list();\n const hits: string[] = [];\n for (const entry of cached) {\n try {\n const resolved = await cache.get(entry.name);\n const found = resolved.manifest.plugins.some(\n (p) => p.name === pluginName\n );\n if (found) hits.push(entry.name);\n } catch {\n // 单个 marketplace 损坏不阻塞扫描——其他 marketplace 仍可命中\n }\n }\n hits.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));\n return hits;\n}\n\n// ---------------------------------------------------------------------------\n// Installer factory\n// ---------------------------------------------------------------------------\n\n/**\n * 生产 Installer 实例的工厂方法。每个 target 一个,按需 `new`。\n *\n * `home` 注入:透传给三家 Installer 的 `{ home }` option(测试铁律)。\n *\n * Codex 需要额外 `marketplaceSource` 来写 `[marketplaces.<mkt>]` section——由\n * 调用方(install.ts)在拿到 ResolvedMarketplace 后用 `codexMarketplaceSource` 适配。\n */\nexport interface InstallerFactoryOptions {\n /** 测试用 HOME 注入。生产默认走 `os.homedir()`(由 Installer 自己解析)。 */\n home?: string;\n /** Codex:marketplace 在 config.toml 里的 source 描述(install 专用) */\n codexMarketplaceSource?: CodexInstallerOptions['marketplaceSource'];\n /**\n * Claude Code:marketplace 源描述(bug #4 修复)。\n * `install` 时 Installer 据此布置 `plugins/marketplaces/<mkt>/` 并写入\n * `known_marketplaces.json.installLocation`。\n */\n claudeMarketplaceSource?: ClaudeCodeInstallerOptions['marketplaceSource'];\n}\n\n/**\n * 默认 Installer 工厂:按 target 返回对应实例。\n *\n * 每次调用 new 一个新实例——无状态 + 便于 CLI 层显式控制注入参数。\n */\nexport function defaultInstallerFactory(\n target: InstallTarget,\n opts: InstallerFactoryOptions = {}\n): PluginInstaller {\n switch (target) {\n case 'claude-code': {\n const ccOpts: ClaudeCodeInstallerOptions = {};\n if (opts.home !== undefined) ccOpts.home = opts.home;\n if (opts.claudeMarketplaceSource !== undefined) {\n ccOpts.marketplaceSource = opts.claudeMarketplaceSource;\n }\n return new ClaudeCodeInstaller(ccOpts);\n }\n case 'cursor': {\n const cuOpts: CursorInstallerOptions = {};\n if (opts.home !== undefined) cuOpts.home = opts.home;\n return new CursorInstaller(cuOpts);\n }\n case 'codex': {\n const cxOpts: CodexInstallerOptions = {\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n ...(opts.codexMarketplaceSource !== undefined\n ? { marketplaceSource: opts.codexMarketplaceSource }\n : {}),\n };\n return new CodexInstaller(cxOpts);\n }\n default: {\n // 穷举检查——新 target 会在此抛 TS 错\n const _exhaustive: never = target;\n void _exhaustive;\n throw new RushError(\n `Unknown install target: ${String(target)}`,\n {},\n 'UNKNOWN_TARGET',\n 2\n );\n }\n }\n}\n\n/**\n * 把 `MarketplaceSource` 转成 Codex Installer 需要的 `marketplaceSource` 形态。\n *\n * 映射规则:\n * - directory → `{ sourceType: 'local', source: <abs path> }`\n * - github → `{ sourceType: 'git', source: 'https://github.com/<owner>/<repo>.git' }`\n * (通用 Codex 只接受 local/git;github shorthand 必须展开为 https URL,\n * 否则 reload 会报 `unknown variant 'github', expected 'git' or 'local'`。\n * ref 无法通过 source 字段表达,由 marketplace cache 层 shallow clone 处理)\n * - git → `{ sourceType: 'git', source: <url> }`\n * - 其他(rush / npm)→ undefined(Codex Installer 自己走 fallback)\n */\nexport function codexMarketplaceSourceFrom(\n source: MarketplaceSource\n): CodexInstallerOptions['marketplaceSource'] {\n switch (source.kind) {\n case 'directory':\n return { sourceType: 'local', source: source.path };\n case 'github': {\n // Codex CLI 只接受 source_type ∈ { \"git\", \"local\" }。\n // 早期本项目假设 Codex 接受 \"github\" shorthand(phase0-spike 观察到\n // ~/.codex 里 openai-curated 写的是 `source_type = \"github\"`),但实测\n // 那是 OpenAI internal fork 的特殊支持。通用 Codex 会拒绝,启动时\n // 报 \"unknown variant `github`, expected `git` or `local`\" 导致 reload 失败。\n // 因此 github source 必须展开为完整 https git URL。\n // ref 目前无法通过 source 字段表达;marketplace cache 层已经通过 git\n // `--branch <ref>` 处理 shallow clone,Codex 侧仅需要可 clone 的 URL。\n const url = `https://github.com/${source.owner}/${source.repo}.git`;\n return { sourceType: 'git', source: url };\n }\n case 'git':\n return { sourceType: 'git', source: source.url };\n default:\n return undefined;\n }\n}\n\n/**\n * 把 `ResolvedMarketplace` 映射成 `ClaudeCodeInstaller` 的 `marketplaceSource` option。\n *\n * - `rootDir`:ResolvedMarketplace.rootDir(对 directory source 是用户给的路径;\n * 对 github: source 是 `~/.rush/marketplaces/<name>/`)\n * - `descriptor`:按 source.kind 映射到 Claude Code `known_marketplaces.json.source`\n * 的字段结构\n * - directory → `{ kind: 'directory', path: <abs path> }`\n * - github → `{ kind: 'github', repo: '<owner>/<repo>' }`(Claude Code 认 github shorthand)\n * - git → `{ kind: 'git', url: <url> }`\n * - 其他(rush / npm)→ undefined(Installer 退化到 directory fallback)\n */\nexport function claudeMarketplaceSourceFrom(\n marketplace: ResolvedMarketplace\n): ClaudeCodeInstallerOptions['marketplaceSource'] {\n const src = marketplace.source;\n switch (src.kind) {\n case 'directory':\n return {\n rootDir: marketplace.rootDir,\n descriptor: { kind: 'directory', path: src.path },\n };\n case 'github':\n return {\n rootDir: marketplace.rootDir,\n descriptor: {\n kind: 'github',\n repo: `${src.owner}/${src.repo}`,\n ...(src.ref !== undefined ? { ref: src.ref } : {}),\n },\n };\n case 'git':\n return {\n rootDir: marketplace.rootDir,\n descriptor: { kind: 'git', url: src.url },\n };\n default:\n return undefined;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registry writer\n// ---------------------------------------------------------------------------\n\n/**\n * 把一组 `InstallResult`(同一个 plugin,跨 target)折合成 `RushRegistryEntry`。\n *\n * 语义:\n * - `installedAt` / `sourceVersion`:用 CLI 层时钟 + 当前 plugin.version\n * - `targets[t]` 仅记录 status != skipped 的 target;skipped 的不写 registry\n * (spec §3.2 没明说,但 skipped 的 target 不属于\"装了\",写进去会让 list 输出噪声)\n * - targets[t].files / mcpKeys 直接来自 `InstallResult.artifacts`\n * - 若本次所有 target 都是 skipped,返回 undefined——调用方应 no-op(不写 registry)\n *\n * 幂等:同 ref 反复调 → 后面的覆盖前面的(registry 是 read-modify-write 语义)。\n */\nexport function buildRegistryEntry(\n plugin: ResolvedPlugin,\n results: readonly InstallResult[],\n nowIso: string\n): RushRegistryEntry | undefined {\n const nonSkipped = results.filter((r) => r.status !== 'skipped');\n if (nonSkipped.length === 0) {\n return undefined;\n }\n const targets: RushRegistryEntry['targets'] = {};\n for (const r of nonSkipped) {\n targets[r.target] = {\n status: r.status,\n version: plugin.version,\n files: [...r.artifacts.files],\n mcpKeys: [...r.artifacts.mcpKeys],\n skipped: [...r.skipped] as PluginCapability[],\n installedAt: nowIso,\n };\n }\n return {\n installedAt: nowIso,\n sourceVersion: plugin.version,\n targets,\n };\n}\n\n/**\n * 把 install 结果合并进 rush-ai registry + 落盘。\n *\n * 失败时抛 `RushError`——本函数只负责 registry 写入;Installer 的 disk 已经装好,\n * 所以即便 registry 写失败,用户也能看到 CLI 输出中\"三家装好\"的 `✓`,只是 rush-ai\n * 这份\"账本\"落空。CLI 层应该把 registry 失败报给用户,让他们手动 `rush-ai plugin list`\n * 观察偏差。\n *\n * 注:本函数不重试——`RushRegistryStore.save()` 自己做一次并发 retry,超过一次抛\n * `RushRegistryConflictError`。CLI 层 catch 后统一转 RushError。\n */\nexport async function applyInstallToRegistry(\n store: RushRegistryStore,\n ref: PluginRef,\n plugin: ResolvedPlugin,\n results: readonly InstallResult[],\n nowIso: string\n): Promise<void> {\n const built = buildRegistryEntry(plugin, results, nowIso);\n if (!built) {\n // 全部 skipped,不写 registry\n return;\n }\n\n // Merge 语义:`--target <subset>` 只装部分 IDE 时,本次 results 不含其他\n // target。必须保留 existing.targets 里未被本次覆盖的条目,否则 uninstall\n // 读不到其他 IDE 的 artifacts → 磁盘泄漏。\n //\n // 顶层 `installedAt` 策略:**首次安装时间保留**。首次 install 用本次的 nowIso;\n // 后续 --target subset / --force reinstall 不覆盖,方便用户追溯\"这个 plugin\n // 最早什么时候出现在我的系统\"。每个 target 的 `installedAt` 另记在\n // `targets.<ide>.installedAt`,反映本次写入时间。\n const existing = store.get(ref);\n const mergedTargets = {\n ...(existing?.targets ?? {}),\n ...built.targets,\n };\n store.set(ref, {\n installedAt: existing?.installedAt ?? built.installedAt,\n sourceVersion: built.sourceVersion,\n targets: mergedTargets,\n });\n await store.save();\n}\n\n/**\n * 卸载对 registry 的影响——把本次卸掉的 targets 从 registry 条目里移除。\n *\n * 如果清理后该 plugin 没有任何 target 了,整条删除。\n *\n * `uninstalledTargets` 是 \"本次真正走了 uninstall 流程(status != skipped)\" 的\n * target 列表——跳过的 target 不改 registry。\n */\nexport async function applyUninstallToRegistry(\n store: RushRegistryStore,\n ref: PluginRef,\n uninstalledTargets: readonly InstallTarget[]\n): Promise<void> {\n if (uninstalledTargets.length === 0) return;\n const existing = store.get(ref);\n if (!existing) {\n await store.save();\n return;\n }\n const nextTargets = { ...existing.targets };\n for (const t of uninstalledTargets) {\n delete nextTargets[t];\n }\n if (Object.keys(nextTargets).length === 0) {\n store.delete(ref);\n } else {\n store.set(ref, { ...existing, targets: nextTargets });\n }\n await store.save();\n}\n\n// ---------------------------------------------------------------------------\n// Typed re-exports(给 commands/plugin 子模块用)\n// ---------------------------------------------------------------------------\n\nexport type {\n InstallResult,\n InstallTarget,\n PluginInstaller,\n ResolvedMarketplace,\n};\n","/**\n * `rush-ai plugin install <name>[@<mkt>] [--target ...] [--force] [--dry-run]`(task-10)。\n *\n * Source of truth:\n * - `specs/cli-commands.md` §2.1\n * - `specs/installer-interface.md` §1-§4\n *\n * 流程(对齐 spec §4 \"CLI 顶层调用\"):\n * 1. 解析 plugin ref(`<name>[@<mkt>]`,含自动推断 marketplace)\n * 2. 用 MarketplaceCache 拿 ResolvedMarketplace + resolvePlugin → ResolvedPlugin\n * 3. (task-12 才接入 migration hook,task-10 **不**做)\n * 4. 按 targets 串行调用每个 Installer(不预过滤,让 Installer 自己返回 skipped)\n * - 每个 Installer 内部已做 detect()=false 短路\n * - 失败的 target 保留其他家继续跑(spec §4 \"默认策略:一家 failed 不拖累另一家\")\n * 5. 一次性把非 skipped 的结果写入 rush-ai registry(CLI 层唯一写入点)\n * 6. 返回 `InstallCommandResult`——commander wrapper 打印汇总\n *\n * 退出码(spec §2.1 v1 单一规则):\n * - 0 所有被调用 target 均为 ok/skipped\n * - 1 任何一个 target 出现 failed\n * - 2 插件解析失败(marketplace 里没 / 多义等)\n *\n * --dry-run:所有 Installer 调用 `opts.dryRun: true`,不写磁盘,不写 registry。\n */\n\nimport type { Command } from 'commander';\nimport {\n type InstallResult,\n type InstallTarget,\n type PluginInstaller,\n type PluginRef,\n type ResolvedPlugin,\n RushRegistryConflictError,\n RushRegistryCorruptError,\n RushRegistrySchemaMismatchError,\n RushRegistryStore,\n} from '../../installers/index.js';\nimport {\n MarketplaceCache,\n MarketplaceNotFoundError,\n type ResolvedMarketplace,\n} from '../../marketplaces/index.js';\nimport {\n type MaybeRunMigrationOutcome,\n type MigrationReporter,\n maybeRunMigration,\n} from '../../migration/index.js';\nimport { output } from '../../output/logger.js';\nimport {\n PluginManifestNotFoundError,\n PluginNotFoundInMarketplaceError,\n PluginResolverError,\n type ResolvePluginOptions,\n resolvePlugin,\n} from '../../plugins/index.js';\nimport { RushError } from '../../util/errors.js';\nimport {\n applyInstallToRegistry,\n claudeMarketplaceSourceFrom,\n codexMarketplaceSourceFrom,\n DEFAULT_TARGETS,\n defaultInstallerFactory,\n parsePluginRef,\n parseTargets,\n} from './common.js';\n\n// ---------------------------------------------------------------------------\n// Input / Output 数据结构\n// ---------------------------------------------------------------------------\n\nexport interface InstallCommandInput {\n /** 原始 `<name>[@<mkt>]` 参数 */\n ref: string;\n /** `--target` 原始值;undefined 走默认三家 */\n targetRaw?: string;\n /** `--force` */\n force?: boolean;\n /** `--dry-run` */\n dryRun?: boolean;\n /**\n * `--target` 是否被用户显式指定(用于汇总输出格式差异化——显式列出的\n * target 若 skipped 要给 \"ⓘ Skipped: <target> (IDE not installed)\" 提示,\n * 默认走的 target skipped 则沉默)。\n *\n * 测试不传时走 `targetRaw !== undefined` 自动推断。\n */\n targetExplicit?: boolean;\n\n /** 测试用 HOME 注入(生产默认 `os.homedir()`) */\n home?: string;\n /** 测试/advanced:直接注入 MarketplaceCache(如 mock GitRunner) */\n cache?: MarketplaceCache;\n /** 测试/advanced:直接注入 RushRegistryStore 实例 */\n registry?: RushRegistryStore;\n /** 测试/advanced:覆盖 resolvePlugin 行为(避免真实 IO) */\n resolvePluginFn?: (\n ref: PluginRef,\n marketplace: ResolvedMarketplace,\n options?: ResolvePluginOptions\n ) => Promise<ResolvedPlugin>;\n /** 测试/advanced:覆盖 Installer 工厂 */\n installerFactory?: (\n target: InstallTarget,\n opts: { home?: string; marketplace: ResolvedMarketplace }\n ) => PluginInstaller;\n /** 测试用:固定时间戳注入 */\n now?: () => string;\n /**\n * 测试/advanced:覆盖 pre-install migration hook。生产默认 `maybeRunMigration`。\n *\n * **铁律**:覆盖的 runner 内部**绝对不能**再调本 `runInstall`——否则会和\n * hook 形成递归。生产 `maybeRunMigration` 直接调 `ClaudeCodeInstaller.install()`\n * 绕过了这一层,测试里如果自定义也必须遵守。\n */\n migrationRunner?: typeof maybeRunMigration;\n /**\n * 迁移流程的进度/告警输出。默认沉默(v1 不强制打印 migration 输出——\n * commander wrapper 会传入一个写 stdout 的 reporter)。\n */\n migrationReporter?: MigrationReporter;\n}\n\n/**\n * 一次 install 命令的完整结果——commander wrapper 根据此对象打印汇总 + 决定退出码。\n */\nexport interface InstallCommandResult {\n ref: PluginRef;\n /** 最终解析到的 ResolvedPlugin(可用于测试断言 version / capabilities) */\n plugin: ResolvedPlugin;\n /** 按输入 targets 顺序排列的结果 */\n results: InstallResult[];\n /** 用户是否显式指定了 --target(影响汇总输出) */\n targetExplicit: boolean;\n /** --dry-run 模式标识 */\n dryRun: boolean;\n /** 聚合 exit code(0/1/2)。commander wrapper 直接使用。 */\n exitCode: 0 | 1 | 2;\n /** registry 写失败时的提示(非阻断;只在人看模式下展示) */\n registryError?: string;\n /**\n * 迁移 hook 的结果(仅装 `rush@rush-marketplace` 时非 undefined)。用于测试\n * 断言;commander wrapper 不直接使用此字段——迁移的 UX 已通过 reporter 打印。\n */\n migration?: MaybeRunMigrationOutcome;\n}\n\n// ---------------------------------------------------------------------------\n// Pure runner\n// ---------------------------------------------------------------------------\n\n/**\n * `rush-ai plugin install` 的纯业务实现。无 console I/O。\n *\n * 失败策略(见 spec §2.1 退出码):\n * - 插件解析失败 → 抛 `RushError(exitCode=2)`\n * - Installer 部分失败 → 不抛,返回结果里 exitCode=1\n * - registry 写失败 → 不抛,结果里填 registryError,exitCode 仍沿用 Installer 结果\n * (spec §10 风险矩阵:\"rush-ai 进程崩在安装中途\":registry 丢失不影响本次\n * Installer 的真实磁盘状态;用户可 `rush-ai plugin list` 手动观察差异)\n */\nexport async function runInstall(\n input: InstallCommandInput\n): Promise<InstallCommandResult> {\n const targets = parseTargets(input.targetRaw);\n const targetExplicit = input.targetExplicit ?? input.targetRaw !== undefined;\n const force = input.force === true;\n const dryRun = input.dryRun === true;\n const now = input.now ?? (() => new Date().toISOString());\n\n // 1. 解析 plugin ref\n const cache =\n input.cache ??\n new MarketplaceCache(input.home !== undefined ? { home: input.home } : {});\n const ref = await parsePluginRef(input.ref, cache);\n\n // 2. 拿 ResolvedMarketplace + resolvePlugin\n let marketplace: ResolvedMarketplace;\n try {\n marketplace = await cache.get(ref.marketplace);\n } catch (err) {\n if (err instanceof MarketplaceNotFoundError) {\n throw new RushError(\n `Marketplace '${ref.marketplace}' is not in the local cache. ` +\n `Run 'rush-ai marketplace add <source>' first.`,\n { marketplaceName: ref.marketplace },\n 'MARKETPLACE_NOT_FOUND',\n 2\n );\n }\n throw err;\n }\n\n const resolveFn = input.resolvePluginFn ?? resolvePlugin;\n let plugin: ResolvedPlugin;\n try {\n plugin = await resolveFn(ref, marketplace);\n } catch (err) {\n if (err instanceof PluginNotFoundInMarketplaceError) {\n throw new RushError(\n err.message,\n { pluginName: ref.name, marketplace: ref.marketplace },\n 'PLUGIN_NOT_FOUND_IN_MARKETPLACE',\n 2\n );\n }\n if (err instanceof PluginManifestNotFoundError) {\n throw new RushError(\n err.message,\n { pluginName: ref.name, marketplace: ref.marketplace },\n 'PLUGIN_MANIFEST_NOT_FOUND',\n 2\n );\n }\n if (err instanceof PluginResolverError) {\n throw new RushError(\n err.message,\n { pluginName: ref.name, marketplace: ref.marketplace },\n 'PLUGIN_RESOLVE_FAILED',\n 2\n );\n }\n throw err;\n }\n\n // 3. Pre-install migration hook(task-12)——迁移老版本 rush-ai 用户\n // 设计要点:\n // - 函数内部对非 `rush@rush-marketplace` 自动 no-op;这里无条件调即可\n // - `maybeRunMigration` 调 `ClaudeCodeInstaller.install()` 绕过本 runInstall,\n // 所以**不会**递归触发本 hook\n // - 迁移失败**不抛**——outcome.kind === 'failure' 已走 log + registry,install 主流程继续\n // - `--dry-run` 不触发迁移(不写磁盘的原则)\n let migrationOutcome: MaybeRunMigrationOutcome | undefined;\n if (!dryRun) {\n const runMigration = input.migrationRunner ?? maybeRunMigration;\n try {\n migrationOutcome = await runMigration({\n ref,\n resolvedPlugin: plugin,\n ...(input.home !== undefined ? { home: input.home } : {}),\n ...(input.migrationReporter !== undefined\n ? { reporter: input.migrationReporter }\n : {}),\n ...(input.now !== undefined ? { now: input.now } : {}),\n });\n } catch (err) {\n // `maybeRunMigration` 契约是\"永不抛错\"——这里只是防御性兜底,实际不应命中。\n // 真实阶段信息拿不到(因为违反契约时未能走正常返回),用 `registry-write`\n // 作为保守默认——它表达\"最顶层 wrapper 层面的失败\",优于把锅甩给具体阶段。\n migrationOutcome = {\n kind: 'failure',\n reason: `migration hook contract violation (runner threw): ${\n err instanceof Error ? err.message : String(err)\n }`,\n stage: 'registry-write',\n };\n }\n }\n\n // 4. 按 target 串行调用每个 Installer(不预过滤,让 Installer 自己决定 skipped)\n const results: InstallResult[] = [];\n for (const t of targets) {\n const factory =\n input.installerFactory ??\n ((target, opts) =>\n defaultInstallerFactory(target, {\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n ...(target === 'codex'\n ? {\n codexMarketplaceSource: codexMarketplaceSourceFrom(\n opts.marketplace.source\n ),\n }\n : {}),\n ...(target === 'claude-code'\n ? {\n claudeMarketplaceSource: claudeMarketplaceSourceFrom(\n opts.marketplace\n ),\n }\n : {}),\n }));\n const installer = factory(t, {\n ...(input.home !== undefined ? { home: input.home } : {}),\n marketplace,\n });\n try {\n const result = await installer.install(plugin, { force, dryRun });\n // v1 partial 防御性收敛(spec §1.4 \"Installer 实现不得构造 partial\"):\n // 若 Installer 违反契约返回 partial,CLI 层不透传——强制收敛为 failed,\n // 带上 note 让开发者知道是契约违反。\n if (result.status === 'partial') {\n results.push({\n ...result,\n status: 'failed',\n errors: [\n ...(result.errors ?? []),\n `Installer '${t}' returned 'partial' which is not permitted in v1 (spec §1.4). Treating as failed.`,\n ],\n });\n } else {\n results.push(result);\n }\n } catch (err) {\n // Installer 契约:bug 级异常不 swallow。但 CLI 层聚合视角下,让整个命令\n // 因为单一 IDE 的 bug 挂掉不合理——包装为 failed 记录,供 --debug 模式\n // 下用户追 cause。继续跑其他 target(spec §4 \"一家 failed 不拖累另一家\")。\n const message = err instanceof Error ? err.message : String(err);\n results.push({\n target: t,\n status: 'failed',\n included: [],\n skipped: [],\n artifacts: { files: [], mcpKeys: [] },\n errors: [message],\n });\n }\n }\n\n // 5. 把非 skipped 结果写入 rush-ai registry(CLI 层唯一写入点)\n // dry-run 或全部 skipped → 跳过 registry 写入\n let registryError: string | undefined;\n if (!dryRun) {\n try {\n const store =\n input.registry ??\n (await RushRegistryStore.load(\n input.home !== undefined ? { home: input.home } : {}\n ));\n await applyInstallToRegistry(store, ref, plugin, results, now());\n } catch (err) {\n registryError = formatRegistryError(err);\n }\n }\n\n // 6. 聚合 exit code\n const hasFailed = results.some((r) => r.status === 'failed');\n const exitCode: 0 | 1 | 2 = hasFailed ? 1 : 0;\n\n return {\n ref,\n plugin,\n results,\n targetExplicit,\n dryRun,\n exitCode,\n ...(registryError !== undefined ? { registryError } : {}),\n ...(migrationOutcome !== undefined ? { migration: migrationOutcome } : {}),\n };\n}\n\nfunction formatRegistryError(err: unknown): string {\n if (err instanceof RushRegistryConflictError) {\n return `rush-ai registry write conflict: ${err.message}`;\n }\n if (err instanceof RushRegistryCorruptError) {\n return `rush-ai registry is corrupt: ${err.message}`;\n }\n if (err instanceof RushRegistrySchemaMismatchError) {\n return `rush-ai registry schema mismatch: ${err.message}`;\n }\n if (err instanceof Error) {\n return `rush-ai registry write failed: ${err.message}`;\n }\n return `rush-ai registry write failed: ${String(err)}`;\n}\n\n// ---------------------------------------------------------------------------\n// Commander wrapper\n// ---------------------------------------------------------------------------\n\nexport function registerInstallCommand(group: Command, _root: Command): void {\n group\n .command('install')\n .description(\n 'Install a plugin to one or more IDEs (Claude Code, Codex, Cursor)'\n )\n .argument('<ref>', 'plugin reference: <name> or <name>@<marketplace>')\n .option(\n '--target <list>',\n `comma-separated targets (default: ${[...DEFAULT_TARGETS].join(',')})`\n )\n .option('--force', 'reinstall even if same version is already installed')\n .option('--dry-run', \"don't touch disk; print the plan\")\n .action(\n async (\n ref: string,\n opts: { target?: string; force?: boolean; dryRun?: boolean }\n ) => {\n const result = await runInstall({\n ref,\n ...(opts.target !== undefined ? { targetRaw: opts.target } : {}),\n targetExplicit: opts.target !== undefined,\n ...(opts.force ? { force: true } : {}),\n ...(opts.dryRun ? { dryRun: true } : {}),\n migrationReporter: {\n info: (line) => output.dim(line),\n warn: (line) => output.warn(line),\n },\n });\n\n printInstallSummary(result);\n\n if (result.exitCode !== 0) {\n // 交给 index.ts 顶层 handler 转成 process.exit;保持错误语义一致\n throw new RushError(\n `Install completed with failures (see output above)`,\n {},\n 'INSTALL_PARTIAL_FAILED',\n result.exitCode\n );\n }\n }\n );\n}\n\n/**\n * 人看模式汇总输出(spec §2.1 / plan §5 示例对齐)。\n *\n * 行格式:\n * ```\n * ✓ Installed to Claude Code (commands + skills + rules + MCP)\n * ✓ Installed to Codex (skills + MCP) [commands/rules/hooks 跳过: Codex 不支持]\n * ⓘ Skipped: Cursor (IDE not installed)\n * ✗ Failed to install to Codex: <reason>\n * ```\n *\n * 规则:\n * - 能力跳过(commands/rules/hooks 在不支持的 IDE)**不降低行级 `✓`**\n * - skipped + 用户显式 --target → 显示 \"ⓘ Skipped\"\n * - skipped + 默认 target → 沉默(不显示)\n * - failed → `✗` + 首条 error\n * - dry-run → 前缀 \"(dry-run)\";artifacts 列表按 verbose 原则简化呈现\n */\nexport function printInstallSummary(result: InstallCommandResult): void {\n const { plugin, results, targetExplicit, dryRun } = result;\n const tag = dryRun ? ' (dry-run)' : '';\n output.log(\n `Installing ${plugin.ref.name}@${plugin.ref.marketplace} v${plugin.version}...${tag}`\n );\n output.newline();\n\n for (const r of results) {\n const label = targetDisplayName(r.target);\n if (r.status === 'skipped') {\n if (targetExplicit) {\n output.dim(`ⓘ Skipped: ${label} (IDE not installed)`);\n }\n // 默认 target:沉默跳过\n continue;\n }\n if (r.status === 'failed') {\n const firstError = r.errors?.[0] ?? 'unknown error';\n output.error(`✗ Failed to install to ${label}: ${firstError}`);\n continue;\n }\n // ok —— v1 partial 已在 runner 里收敛为 failed,此分支只处理 ok 语义\n const includedFrag =\n r.included.length > 0 ? `(${formatCapabilityList(r.included)})` : '';\n const skippedFrag = formatSkippedFragment(r.skipped, r.target);\n const line = [`✓ Installed to ${label}`, includedFrag, skippedFrag]\n .filter((s) => s.length > 0)\n .join(' ');\n output.success(line);\n }\n\n // Next steps(仅人看模式;dry-run 跳过 + 至少有一家 ok 才展示)\n // v1 不支持 partial——runner 已把任何 partial 收敛为 failed,此处只看 ok\n const anyOk = results.some((r) => r.status === 'ok');\n if (anyOk && !dryRun) {\n output.newline();\n output.dim('Next steps:');\n for (const r of results) {\n if (r.status !== 'ok') continue;\n output.dim(\n ` - ${targetDisplayName(r.target)}: ${nextStepFor(r.target)}`\n );\n }\n }\n\n if (result.registryError) {\n output.newline();\n output.warn(result.registryError);\n output.dim(\n ' The plugin is still installed on disk; run `rush-ai plugin list` to inspect.'\n );\n }\n}\n\n/** 能力列表 \"commands + skills + rules + MCP\" 格式化。 */\nfunction formatCapabilityList(caps: readonly string[]): string {\n return caps.map((c) => (c === 'mcp' ? 'MCP' : c)).join(' + ');\n}\n\n/**\n * 生成尾部方括号跳过提示(如 `[commands/rules/hooks 跳过: Codex 不支持]`)。\n * 空 skipped 返回空串。\n */\nfunction formatSkippedFragment(\n skipped: readonly string[],\n target: InstallTarget\n): string {\n if (skipped.length === 0) return '';\n const joined = skipped.join('/');\n return `[${joined} 跳过: ${targetDisplayName(target)} 不支持]`;\n}\n\nfunction targetDisplayName(t: InstallTarget): string {\n switch (t) {\n case 'claude-code':\n return 'Claude Code';\n case 'codex':\n return 'Codex';\n case 'cursor':\n return 'Cursor';\n default: {\n const _exhaustive: never = t;\n void _exhaustive;\n return String(t);\n }\n }\n}\n\nfunction nextStepFor(t: InstallTarget): string {\n switch (t) {\n case 'claude-code':\n return \"restart Claude Code or run '/plugin list' to see the installed plugin\";\n case 'codex':\n return 'start a new Codex session';\n case 'cursor':\n return 'restart Cursor';\n default:\n return 'restart the IDE';\n }\n}\n","/**\n * `rush-ai plugin list [--available] [--target ...]`(task-10)。\n *\n * Source of truth: `specs/cli-commands.md` §2.3\n *\n * 两种模式:\n * - 默认:从 rush-ai registry 读已装插件,跨 target 聚合输出\n * ```\n * NAME@MARKETPLACE VERSION TARGETS\n * rush@rush-marketplace 0.2.0 claude-code ✓ codex ✓ cursor ✓\n * yuanli-infra@rush-marketplace 1.0.0 claude-code ✓ codex - cursor ✓\n * ```\n * - `✓` = status=ok\n * - `-` = status=skipped(IDE 没装)\n * - `✗` = status=failed(上次 install 出错,registry 仍保留)\n *\n * - `--available`:从所有 cache 里的 marketplace 读可装插件,标注已装/未装\n * ```\n * AVAILABLE PLUGINS\n * rush-marketplace:\n * - rush (v0.2.0) [installed]\n * - yuanli-infra (v1.0.0) [installed]\n * - octo (v0.5.0) [not installed]\n * ```\n *\n * `--target` 过滤显示列(默认显示三家;显式指定只展示对应列)。\n *\n * JSON 模式:`--json`(继承 root --output 选项)返回结构化数据。\n */\n\nimport type { Command } from 'commander';\nimport {\n type InstallStatus,\n type InstallTarget,\n type PluginRef,\n RushRegistryStore,\n} from '../../installers/index.js';\nimport { MarketplaceCache } from '../../marketplaces/index.js';\nimport { resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport { DEFAULT_TARGETS, parseTargets } from './common.js';\n\nexport interface ListCommandInput {\n available?: boolean;\n targetRaw?: string;\n home?: string;\n cache?: MarketplaceCache;\n registry?: RushRegistryStore;\n}\n\nexport interface InstalledPluginRow {\n ref: PluginRef;\n version: string;\n /** 每个 target 的 status;undefined = registry 里没条目(视为 \"-\") */\n targets: {\n [T in InstallTarget]?: InstallStatus;\n };\n}\n\nexport interface AvailablePluginRow {\n marketplace: string;\n name: string;\n version: string | undefined;\n description: string | undefined;\n installed: boolean;\n}\n\nexport interface ListCommandResult {\n mode: 'installed' | 'available';\n targets: InstallTarget[];\n installed?: InstalledPluginRow[];\n available?: AvailablePluginRow[];\n}\n\n/**\n * 纯函数实现。\n *\n * - 默认模式:列当前 registry 里的所有 plugin,过滤展示列\n * - `--available`:扫所有 marketplace cache,列 marketplace.json 里声明的 plugin\n */\nexport async function runList(\n input: ListCommandInput = {}\n): Promise<ListCommandResult> {\n const targets = parseTargets(input.targetRaw);\n\n const cache =\n input.cache ??\n new MarketplaceCache(input.home !== undefined ? { home: input.home } : {});\n const registry =\n input.registry ??\n (await RushRegistryStore.load(\n input.home !== undefined ? { home: input.home } : {}\n ));\n\n if (input.available) {\n return runAvailable(cache, registry, targets);\n }\n return runInstalled(registry, targets);\n}\n\nfunction runInstalled(\n registry: RushRegistryStore,\n targets: InstallTarget[]\n): ListCommandResult {\n const installed: InstalledPluginRow[] = [];\n for (const [ref, entry] of registry.list()) {\n const row: InstalledPluginRow = {\n ref,\n version: entry.sourceVersion,\n targets: {},\n };\n for (const t of targets) {\n const te = entry.targets[t];\n if (te) row.targets[t] = te.status;\n }\n installed.push(row);\n }\n return { mode: 'installed', targets, installed };\n}\n\nasync function runAvailable(\n cache: MarketplaceCache,\n registry: RushRegistryStore,\n targets: InstallTarget[]\n): Promise<ListCommandResult> {\n const installedKeys = new Set<string>();\n for (const [ref] of registry.list()) {\n installedKeys.add(`${ref.name}@${ref.marketplace}`);\n }\n\n const rows: AvailablePluginRow[] = [];\n const mkts = await cache.list();\n for (const mkt of mkts) {\n try {\n const resolved = await cache.get(mkt.name);\n for (const plugin of resolved.manifest.plugins) {\n const refKey = `${plugin.name}@${mkt.name}`;\n rows.push({\n marketplace: mkt.name,\n name: plugin.name,\n version: plugin.version,\n description: plugin.description,\n installed: installedKeys.has(refKey),\n });\n }\n } catch {\n // 单个 marketplace 解析失败 → 跳过(避免一家坏拖累全部)\n }\n }\n // 按 marketplace 名 + plugin 名字典序\n rows.sort((a, b) => {\n if (a.marketplace !== b.marketplace) {\n return a.marketplace < b.marketplace ? -1 : 1;\n }\n return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;\n });\n return { mode: 'available', targets, available: rows };\n}\n\n// ---------------------------------------------------------------------------\n// Commander wrapper\n// ---------------------------------------------------------------------------\n\nexport function registerListCommand(group: Command, root: Command): void {\n group\n .command('list')\n .alias('ls')\n .description(\n 'List installed plugins (default) or available plugins (--available)'\n )\n .option('--available', 'list plugins available in cached marketplaces')\n .option(\n '--target <list>',\n `comma-separated targets (default: ${[...DEFAULT_TARGETS].join(',')})`\n )\n .action(async (opts: { available?: boolean; target?: string }) => {\n const format = resolveFormat(root.opts());\n const result = await runList({\n ...(opts.available ? { available: true } : {}),\n ...(opts.target !== undefined ? { targetRaw: opts.target } : {}),\n });\n\n if (format === 'json') {\n output.log(JSON.stringify(result, null, 2));\n return;\n }\n printListSummary(result);\n });\n}\n\nexport function printListSummary(result: ListCommandResult): void {\n if (result.mode === 'available') {\n printAvailable(result);\n return;\n }\n printInstalled(result);\n}\n\nfunction printInstalled(result: ListCommandResult): void {\n const rows = result.installed ?? [];\n if (rows.length === 0) {\n output.log('(no plugins installed)');\n output.dim(\n \" Install one with 'rush-ai plugin install <name>[@<marketplace>]'\"\n );\n return;\n }\n\n // 对齐表头:NAME@MARKETPLACE VERSION TARGETS\n const refStrs = rows.map((r) => `${r.ref.name}@${r.ref.marketplace}`);\n const nameWidth = Math.max(\n 'NAME@MARKETPLACE'.length,\n ...refStrs.map((s) => s.length)\n );\n const versionWidth = Math.max(\n 'VERSION'.length,\n ...rows.map((r) => r.version.length)\n );\n\n const header =\n `${'NAME@MARKETPLACE'.padEnd(nameWidth)} ` +\n `${'VERSION'.padEnd(versionWidth)} ` +\n `TARGETS`;\n output.log(output.bold(header));\n\n for (let i = 0; i < rows.length; i++) {\n const r = rows[i];\n const refStr = refStrs[i];\n if (!r || refStr === undefined) continue;\n const targetFrags: string[] = [];\n for (const t of result.targets) {\n const status = r.targets[t];\n targetFrags.push(`${t} ${statusIcon(status)}`);\n }\n output.log(\n `${refStr.padEnd(nameWidth)} ` +\n `${r.version.padEnd(versionWidth)} ` +\n `${targetFrags.join(' ')}`\n );\n }\n}\n\nfunction printAvailable(result: ListCommandResult): void {\n const rows = result.available ?? [];\n if (rows.length === 0) {\n output.log('(no available plugins)');\n output.dim(\n \" Add a marketplace with 'rush-ai marketplace add <source>' to discover plugins.\"\n );\n return;\n }\n output.log(output.bold('AVAILABLE PLUGINS'));\n\n const byMkt = new Map<string, AvailablePluginRow[]>();\n for (const r of rows) {\n const arr = byMkt.get(r.marketplace) ?? [];\n arr.push(r);\n byMkt.set(r.marketplace, arr);\n }\n for (const [mkt, mktRows] of byMkt) {\n output.log(`${mkt}:`);\n for (const row of mktRows) {\n const versionFrag = row.version ? ` (v${row.version})` : '';\n const installedFrag = row.installed ? '[installed]' : '[not installed]';\n output.log(` - ${row.name}${versionFrag} ${installedFrag}`);\n }\n }\n}\n\nfunction statusIcon(status: InstallStatus | undefined): string {\n if (status === undefined || status === 'skipped') return '-';\n if (status === 'ok') return '✓';\n if (status === 'failed') return '✗';\n // partial v1 不会出现,兜底按 failed 处理(spec §2.3)\n return '✗';\n}\n","/**\n * `rush-ai plugin uninstall <name>[@<mkt>] [--target ...] [--dry-run]`(task-10)。\n *\n * Source of truth:\n * - `specs/cli-commands.md` §2.2\n * - `specs/installer-interface.md` §2\n *\n * 流程:\n * 1. 解析 plugin ref(`name@mkt` 必须明确——优先读 registry;不在 registry 里但 cache\n * 能唯一命中 marketplace 也接受;否则报 ref 解析错)\n * 2. 对每个 target 调 `installer.uninstall(ref, { dryRun })`\n * - Installer 未 detect 到 IDE → 自己返回 `status: 'ok' + artifacts 空`(按 spec 行为)\n * - Installer failed → 保留其他 target 继续\n * 3. `--dry-run`:不写 registry,展示 artifacts 清单\n * 4. 非 dry-run:把对应 target 从 registry 条目里清理(全清后整条删)\n *\n * 输出示例(spec §2.2):\n * ```\n * Uninstalling rush@rush-marketplace...\n *\n * ✓ Removed from Claude Code\n * ✓ Removed from Codex\n * ✓ Removed from Cursor (3 files, 1 MCP entry)\n * ```\n *\n * 退出码:\n * - 0 所有被调用 target 均为 ok / skipped\n * - 1 任何一个 target failed\n * - 2 ref 解析失败\n */\n\nimport type { Command } from 'commander';\nimport {\n type InstallTarget,\n type PluginInstaller,\n type PluginRef,\n RushRegistryConflictError,\n RushRegistryCorruptError,\n RushRegistrySchemaMismatchError,\n RushRegistryStore,\n type UninstallResult,\n} from '../../installers/index.js';\nimport { MarketplaceCache } from '../../marketplaces/index.js';\nimport { output } from '../../output/logger.js';\nimport { RushError } from '../../util/errors.js';\nimport {\n applyUninstallToRegistry,\n DEFAULT_TARGETS,\n defaultInstallerFactory,\n parsePluginRef,\n parseTargets,\n} from './common.js';\n\nexport interface UninstallCommandInput {\n ref: string;\n targetRaw?: string;\n dryRun?: boolean;\n targetExplicit?: boolean;\n\n home?: string;\n cache?: MarketplaceCache;\n registry?: RushRegistryStore;\n installerFactory?: (\n target: InstallTarget,\n opts: { home?: string }\n ) => PluginInstaller;\n}\n\nexport interface UninstallCommandResult {\n ref: PluginRef;\n results: UninstallResult[];\n targetExplicit: boolean;\n dryRun: boolean;\n exitCode: 0 | 1 | 2;\n registryError?: string;\n}\n\nexport async function runUninstall(\n input: UninstallCommandInput\n): Promise<UninstallCommandResult> {\n const targets = parseTargets(input.targetRaw);\n const targetExplicit = input.targetExplicit ?? input.targetRaw !== undefined;\n const dryRun = input.dryRun === true;\n\n // 1. 解析 plugin ref —— 先在 registry 找;找不到再走 cache 反查(与 install 对称)\n const cache =\n input.cache ??\n new MarketplaceCache(input.home !== undefined ? { home: input.home } : {});\n const ref = await parsePluginRef(input.ref, cache).catch(async (err) => {\n // uninstall 对\"marketplace 没 cache\"的场景更宽容——用户可能把 marketplace\n // 已经 remove 了但插件装在三家 IDE 里残留。只要 registry 能找到就接受。\n if (err instanceof RushError && err.code === 'PLUGIN_NOT_FOUND') {\n const reg =\n input.registry ??\n (await RushRegistryStore.load(\n input.home !== undefined ? { home: input.home } : {}\n ));\n const all = reg.list();\n const matches = all.filter(([r]) => r.name === input.ref.trim());\n if (matches.length === 1) {\n const only = matches[0];\n if (only) return only[0];\n }\n if (matches.length > 1) {\n const names = matches.map(([r]) => `${r.name}@${r.marketplace}`);\n throw new RushError(\n `Plugin '${input.ref}' is ambiguous across registry: ${names.join(', ')}. ` +\n `Specify '<name>@<marketplace>' explicitly.`,\n { candidates: names },\n 'AMBIGUOUS_PLUGIN',\n 2\n );\n }\n }\n throw err;\n });\n\n // 2. 每个 target 串行 uninstall\n const results: UninstallResult[] = [];\n for (const t of targets) {\n const factory =\n input.installerFactory ??\n ((target, opts) =>\n defaultInstallerFactory(target, {\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n }));\n const installer = factory(t, {\n ...(input.home !== undefined ? { home: input.home } : {}),\n });\n try {\n const result = await installer.uninstall(ref, { dryRun });\n // v1 partial 防御性收敛(同 install)——Installer 违反契约返回 partial\n // 时收敛为 failed,避免透传到 registry / 退出码聚合\n if (result.status === 'partial') {\n results.push({\n ...result,\n status: 'failed',\n errors: [\n ...(result.errors ?? []),\n `Installer '${t}' returned 'partial' which is not permitted in v1 (spec §1.4). Treating as failed.`,\n ],\n });\n } else {\n results.push(result);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n results.push({\n target: t,\n status: 'failed',\n artifacts: { files: [], mcpKeys: [] },\n errors: [message],\n });\n }\n }\n\n // 3. 非 dry-run → 把 ok 的 target 从 registry 条目清理\n let registryError: string | undefined;\n if (!dryRun) {\n try {\n const store =\n input.registry ??\n (await RushRegistryStore.load(\n input.home !== undefined ? { home: input.home } : {}\n ));\n const successTargets = results\n .filter((r) => r.status === 'ok')\n .map((r) => r.target);\n await applyUninstallToRegistry(store, ref, successTargets);\n } catch (err) {\n registryError = formatRegistryError(err);\n }\n }\n\n const hasFailed = results.some((r) => r.status === 'failed');\n const exitCode: 0 | 1 | 2 = hasFailed ? 1 : 0;\n\n return {\n ref,\n results,\n targetExplicit,\n dryRun,\n exitCode,\n ...(registryError !== undefined ? { registryError } : {}),\n };\n}\n\nfunction formatRegistryError(err: unknown): string {\n if (err instanceof RushRegistryConflictError) {\n return `rush-ai registry write conflict: ${err.message}`;\n }\n if (err instanceof RushRegistryCorruptError) {\n return `rush-ai registry is corrupt: ${err.message}`;\n }\n if (err instanceof RushRegistrySchemaMismatchError) {\n return `rush-ai registry schema mismatch: ${err.message}`;\n }\n if (err instanceof Error) {\n return `rush-ai registry write failed: ${err.message}`;\n }\n return `rush-ai registry write failed: ${String(err)}`;\n}\n\n// ---------------------------------------------------------------------------\n// Commander wrapper\n// ---------------------------------------------------------------------------\n\nexport function registerUninstallCommand(group: Command, _root: Command): void {\n group\n .command('uninstall')\n .alias('remove')\n .description('Uninstall a plugin from one or more IDEs')\n .argument('<ref>', 'plugin reference: <name> or <name>@<marketplace>')\n .option(\n '--target <list>',\n `comma-separated targets (default: ${[...DEFAULT_TARGETS].join(',')})`\n )\n .option(\n '--dry-run',\n \"don't touch disk; print the files that would be removed\"\n )\n .action(\n async (ref: string, opts: { target?: string; dryRun?: boolean }) => {\n const result = await runUninstall({\n ref,\n ...(opts.target !== undefined ? { targetRaw: opts.target } : {}),\n targetExplicit: opts.target !== undefined,\n ...(opts.dryRun ? { dryRun: true } : {}),\n });\n\n printUninstallSummary(result);\n\n if (result.exitCode !== 0) {\n throw new RushError(\n `Uninstall completed with failures (see output above)`,\n {},\n 'UNINSTALL_PARTIAL_FAILED',\n result.exitCode\n );\n }\n }\n );\n}\n\nexport function printUninstallSummary(result: UninstallCommandResult): void {\n const { ref, results, targetExplicit, dryRun } = result;\n const tag = dryRun ? ' (dry-run)' : '';\n output.log(`Uninstalling ${ref.name}@${ref.marketplace}...${tag}`);\n output.newline();\n\n for (const r of results) {\n const label = targetDisplayName(r.target);\n if (r.status === 'skipped') {\n if (targetExplicit) {\n output.dim(`ⓘ Skipped: ${label} (IDE not installed)`);\n }\n continue;\n }\n if (r.status === 'failed') {\n const firstError = r.errors?.[0] ?? 'unknown error';\n output.error(`✗ Failed to uninstall from ${label}: ${firstError}`);\n continue;\n }\n // ok\n const details = formatArtifactsDetail(r);\n const line = details\n ? `✓ Removed from ${label} (${details})`\n : `✓ Removed from ${label}`;\n output.success(line);\n\n if (dryRun && r.artifacts.files.length > 0) {\n for (const f of r.artifacts.files) {\n output.dim(` would remove: ${f}`);\n }\n }\n }\n\n if (result.registryError) {\n output.newline();\n output.warn(result.registryError);\n output.dim(\n ' The disk has been cleaned; rush-ai registry may be stale. Retry to reconcile.'\n );\n }\n}\n\nfunction formatArtifactsDetail(r: UninstallResult): string {\n const parts: string[] = [];\n if (r.artifacts.files.length > 0) {\n parts.push(\n `${r.artifacts.files.length} file${r.artifacts.files.length > 1 ? 's' : ''}`\n );\n }\n if (r.artifacts.mcpKeys.length > 0) {\n parts.push(\n `${r.artifacts.mcpKeys.length} MCP entr${r.artifacts.mcpKeys.length > 1 ? 'ies' : 'y'}`\n );\n }\n return parts.join(', ');\n}\n\nfunction targetDisplayName(t: InstallTarget): string {\n switch (t) {\n case 'claude-code':\n return 'Claude Code';\n case 'codex':\n return 'Codex';\n case 'cursor':\n return 'Cursor';\n default: {\n const _exhaustive: never = t;\n void _exhaustive;\n return String(t);\n }\n }\n}\n","/**\n * `rush-ai plugin update [<name>] [--target ...]`(task-10)。\n *\n * Source of truth: `specs/cli-commands.md` §2.4\n *\n * 语义:\n * - 省略 `<name>`:检查所有已装插件的 marketplace 是否有新版本,有则 `install --force`\n * - 带 `<name>`:只更这一个\n * - 等价于:对每个目标 plugin 先 `marketplace update <mkt>`(刷 cache),\n * 再 `resolvePlugin` 拿最新 version;若 registry 里的 version 与最新不同或 force\n * 模式 → 走 `install --force`(复用 `runInstall`)。同版本 → 跳过。\n *\n * 由于 update = \"marketplace refresh + plugin install --force\",实现上直接复用\n * `runInstall`:\n * - 先对需要刷新的 marketplaces 走 `cache.update(name)`(directory: source → no-op)\n * - 再逐个 plugin 调 `runInstall({ force: true })`\n *\n * 输出示例:\n * ```\n * Updating 3 plugin(s)...\n *\n * → rush@rush-marketplace: 0.2.0 → 0.2.1\n * ✓ Installed to Claude Code ...\n * → yuanli-infra@rush-marketplace: up to date (1.0.0)\n * → octo@rush-marketplace: 0.5.0 → 0.6.0\n * ✓ Installed to ...\n * ```\n *\n * 退出码:\n * - 0 全部成功或无需更新\n * - 1 任一 plugin 的 install 失败\n * - 2 ref 解析或 marketplace 缺失\n */\n\nimport type { Command } from 'commander';\nimport {\n type InstallTarget,\n type PluginRef,\n RushRegistryStore,\n} from '../../installers/index.js';\nimport {\n MarketplaceCache,\n MarketplaceNotFoundError,\n NotImplementedError,\n type ResolvedMarketplace,\n} from '../../marketplaces/index.js';\nimport { output } from '../../output/logger.js';\nimport {\n PluginResolverError,\n resolvePlugin as realResolvePlugin,\n} from '../../plugins/index.js';\nimport { RushError } from '../../util/errors.js';\nimport { DEFAULT_TARGETS, parsePluginRef, parseTargets } from './common.js';\nimport {\n type InstallCommandInput,\n type InstallCommandResult,\n printInstallSummary,\n runInstall,\n} from './install.js';\n\nexport interface UpdateCommandInput {\n /** 限定 plugin;undefined = 更全部 */\n name?: string;\n targetRaw?: string;\n targetExplicit?: boolean;\n /** `--dry-run`:不 install,只展示 \"哪些插件有新版本 + 会刷哪些 cache\" */\n dryRun?: boolean;\n\n home?: string;\n cache?: MarketplaceCache;\n registry?: RushRegistryStore;\n\n /** 测试/advanced:覆盖 install runner */\n installRunner?: (input: InstallCommandInput) => Promise<InstallCommandResult>;\n /** 测试/advanced:覆盖 marketplace cache update 行为 */\n cacheUpdate?: (name: string) => Promise<void>;\n /** 测试/advanced:覆盖 resolvePlugin */\n resolvePluginFn?: typeof realResolvePlugin;\n}\n\nexport interface UpdatePluginResult {\n ref: PluginRef;\n /** registry 里的旧版本 */\n fromVersion: string;\n /** marketplace 最新版本(resolvePlugin 产出) */\n toVersion: string;\n /** 无需更新(版本相同) */\n upToDate: boolean;\n /** 关联的 install 执行结果(undefined = up-to-date 或 dry-run 跳过) */\n installResult?: InstallCommandResult;\n /** 处理过程中捕获的错误 */\n error?: string;\n /**\n * 错误类别(用于聚合 exitCode):\n * - `resolve`:marketplace 不存在 / resolvePlugin 失败 → 贡献 exitCode=2\n * - `install`:install runner 返回非零 exitCode → 贡献 exitCode=1\n */\n errorKind?: 'resolve' | 'install';\n}\n\nexport interface UpdateCommandResult {\n targets: InstallTarget[];\n dryRun: boolean;\n plugins: UpdatePluginResult[];\n exitCode: 0 | 1 | 2;\n}\n\nexport async function runUpdate(\n input: UpdateCommandInput = {}\n): Promise<UpdateCommandResult> {\n const targets = parseTargets(input.targetRaw);\n const targetExplicit = input.targetExplicit ?? input.targetRaw !== undefined;\n const dryRun = input.dryRun === true;\n\n const cache =\n input.cache ??\n new MarketplaceCache(input.home !== undefined ? { home: input.home } : {});\n const registry =\n input.registry ??\n (await RushRegistryStore.load(\n input.home !== undefined ? { home: input.home } : {}\n ));\n\n // 1. 确定要更新的 plugin 列表\n const refs: PluginRef[] = await collectRefs(input, cache, registry);\n if (refs.length === 0) {\n return {\n targets,\n dryRun,\n plugins: [],\n exitCode: 0,\n };\n }\n\n // 2. 按 marketplace 分组,先刷 cache(同一个 marketplace 只刷一次)\n const uniqueMkts = new Set(refs.map((r) => r.marketplace));\n const cacheUpdate =\n input.cacheUpdate ??\n ((name: string) => cache.update(name).then(() => undefined));\n for (const mktName of uniqueMkts) {\n if (dryRun) continue;\n try {\n await cacheUpdate(mktName);\n } catch (err) {\n if (err instanceof NotImplementedError) continue; // Phase 1.5/2 source 忽略\n if (err instanceof MarketplaceNotFoundError) continue; // has() 已挡,兜底\n // 其他错误挂一个 plugin → 让后续 resolve 阶段报错,下面继续\n }\n }\n\n // 3. 逐个 plugin:resolve 拿最新 version → 对比 registry → 必要时 install --force\n const resolveFn = input.resolvePluginFn ?? realResolvePlugin;\n const installRunner = input.installRunner ?? runInstall;\n const plugins: UpdatePluginResult[] = [];\n\n for (const ref of refs) {\n const fromVersion = registry.get(ref)?.sourceVersion ?? 'unknown';\n let resolved: ResolvedMarketplace;\n try {\n resolved = await cache.get(ref.marketplace);\n } catch (err) {\n plugins.push({\n ref,\n fromVersion,\n toVersion: fromVersion,\n upToDate: false,\n error: err instanceof Error ? err.message : String(err),\n errorKind: 'resolve',\n });\n continue;\n }\n let toVersion: string;\n try {\n const plugin = await resolveFn(ref, resolved);\n toVersion = plugin.version;\n } catch (err) {\n if (err instanceof PluginResolverError) {\n plugins.push({\n ref,\n fromVersion,\n toVersion: fromVersion,\n upToDate: false,\n error: err.message,\n errorKind: 'resolve',\n });\n continue;\n }\n throw err;\n }\n\n if (toVersion === fromVersion) {\n plugins.push({ ref, fromVersion, toVersion, upToDate: true });\n continue;\n }\n\n if (dryRun) {\n plugins.push({ ref, fromVersion, toVersion, upToDate: false });\n continue;\n }\n\n const installInput: InstallCommandInput = {\n ref: `${ref.name}@${ref.marketplace}`,\n ...(input.targetRaw !== undefined ? { targetRaw: input.targetRaw } : {}),\n targetExplicit,\n force: true,\n ...(input.home !== undefined ? { home: input.home } : {}),\n cache,\n registry,\n };\n const result = await installRunner(installInput);\n plugins.push({\n ref,\n fromVersion,\n toVersion,\n upToDate: false,\n installResult: result,\n ...(result.exitCode === 1 ? { errorKind: 'install' as const } : {}),\n });\n }\n\n // 退出码聚合规则(对齐 spec §2.1 单一规则 + 尊重 update = resolve + install 组合):\n // - 任一 resolve 失败(含 marketplace 缺失 / resolvePlugin 失败)→ 2(参数/解析类)\n // - 否则任一 install 阶段 exitCode=1 → 1\n // - 全部干净 → 0\n // resolve + install 同时失败时 resolve 优先(2 > 1),原因:CLI 调用方通常更关心\n // 参数类错误(可修),install 失败往往是可重试的。\n const hasResolveFailure = plugins.some((p) => p.errorKind === 'resolve');\n const hasInstallFailure = plugins.some((p) => p.errorKind === 'install');\n const exitCode: 0 | 1 | 2 = hasResolveFailure ? 2 : hasInstallFailure ? 1 : 0;\n\n return { targets, dryRun, plugins, exitCode };\n}\n\nasync function collectRefs(\n input: UpdateCommandInput,\n cache: MarketplaceCache,\n registry: RushRegistryStore\n): Promise<PluginRef[]> {\n if (input.name !== undefined) {\n const ref = await parsePluginRef(input.name, cache);\n return [ref];\n }\n return registry.list().map(([ref]) => ref);\n}\n\n// ---------------------------------------------------------------------------\n// Commander wrapper\n// ---------------------------------------------------------------------------\n\nexport function registerUpdateCommand(group: Command, _root: Command): void {\n group\n .command('update')\n .description(\n 'Update installed plugin(s) to the latest version available in their marketplaces'\n )\n .argument(\n '[name]',\n 'plugin name or <name>@<marketplace>; omit to update all installed plugins'\n )\n .option(\n '--target <list>',\n `comma-separated targets (default: ${[...DEFAULT_TARGETS].join(',')})`\n )\n .option(\n '--dry-run',\n 'list which plugins would be updated without touching disk'\n )\n .action(\n async (\n name: string | undefined,\n opts: { target?: string; dryRun?: boolean }\n ) => {\n const result = await runUpdate({\n ...(name !== undefined ? { name } : {}),\n ...(opts.target !== undefined ? { targetRaw: opts.target } : {}),\n targetExplicit: opts.target !== undefined,\n ...(opts.dryRun ? { dryRun: true } : {}),\n });\n\n printUpdateSummary(result);\n\n if (result.exitCode !== 0) {\n throw new RushError(\n `Update completed with failures (see output above)`,\n {},\n 'UPDATE_PARTIAL_FAILED',\n result.exitCode\n );\n }\n }\n );\n}\n\nexport function printUpdateSummary(result: UpdateCommandResult): void {\n const { plugins, dryRun } = result;\n if (plugins.length === 0) {\n output.log('(no installed plugins to update)');\n output.dim(\n \" Install one with 'rush-ai plugin install <name>[@<marketplace>]' first.\"\n );\n return;\n }\n\n const header = dryRun\n ? `Checking ${plugins.length} plugin(s) for updates (dry-run)...`\n : `Updating ${plugins.length} plugin(s)...`;\n output.log(header);\n output.newline();\n\n for (const p of plugins) {\n const refStr = `${p.ref.name}@${p.ref.marketplace}`;\n if (p.error) {\n output.error(`→ ${refStr}: ${p.error}`);\n continue;\n }\n if (p.upToDate) {\n output.dim(`→ ${refStr}: up to date (${p.toVersion})`);\n continue;\n }\n if (dryRun) {\n output.log(`→ ${refStr}: ${p.fromVersion} → ${p.toVersion} (dry-run)`);\n continue;\n }\n output.log(`→ ${refStr}: ${p.fromVersion} → ${p.toVersion}`);\n if (p.installResult) {\n printInstallSummary(p.installResult);\n }\n }\n}\n","/**\n * `rush-ai plugin` 命令组入口(task-10 重写)。\n *\n * Source of truth: `specs/cli-commands.md` §2\n *\n * 子命令:\n * - `install <name>[@<mkt>] [--target ...] [--force] [--dry-run]` 见 install.ts\n * - `uninstall <name>[@<mkt>] [--target ...] [--dry-run]` 见 uninstall.ts\n * - `list [--available] [--target ...]` 见 list.ts\n * - `update [<name>] [--target ...]` 见 update.ts\n *\n * 设计原则(与 `commands/marketplace/index.ts` 一致):\n * - 命令 handler 只做参数解析 + 调用 pure runner + 打印汇总\n * - 业务逻辑全在 pure runner 函数里(`runInstall` / `runUninstall` / `runList` /\n * `runUpdate`),便于单测直接调用\n * - 所有磁盘操作通过 `home` 参数注入(测试强制 tmp dir)\n *\n * - 本文件完全替换老的 adapter-based install/uninstall/list/status/update/doctor\n * 实现(即旧 `registerPluginCommand` 里的 `adapter.*` 调用)\n * - 老的 `adapters/` / `assets.ts` / `migration.ts` 在 task-11 才删除;本 task\n * 不再引用它们,让 task-11 成为纯 `rm` 操作\n * - `doctor.ts` / `preflight.ts` / `verify.ts` 依赖 `IdeAdapter`,本 task 不再\n * 挂载对应子命令(`plugin doctor` 暂时消失)。task-11 / 后续 task 会决定它们\n * 是一起删除还是改写成基于新 Installer API\n * 的健康检查——不属于本 task 职责\n */\n\nimport type { Command } from 'commander';\nimport { registerInstallCommand } from './install.js';\nimport { registerListCommand } from './list.js';\nimport { registerUninstallCommand } from './uninstall.js';\nimport { registerUpdateCommand } from './update.js';\n\nexport function registerPluginCommand(program: Command): void {\n const plugin = program\n .command('plugin')\n .description(\n 'Install, uninstall, list, and update plugins across Claude Code, Codex, and Cursor'\n );\n\n registerInstallCommand(plugin, program);\n registerUninstallCommand(plugin, program);\n registerListCommand(plugin, program);\n registerUpdateCommand(plugin, program);\n}\n\n// -- Re-export pure runners for integration / advanced consumers ---------------\nexport type { InstallCommandInput, InstallCommandResult } from './install.js';\nexport { printInstallSummary, runInstall } from './install.js';\nexport type {\n AvailablePluginRow,\n InstalledPluginRow,\n ListCommandInput,\n ListCommandResult,\n} from './list.js';\nexport { printListSummary, runList } from './list.js';\nexport type {\n UninstallCommandInput,\n UninstallCommandResult,\n} from './uninstall.js';\nexport { printUninstallSummary, runUninstall } from './uninstall.js';\nexport type {\n UpdateCommandInput,\n UpdateCommandResult,\n UpdatePluginResult,\n} from './update.js';\nexport { printUpdateSummary, runUpdate } from './update.js';\n","import { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport { basename, resolve } from 'node:path';\nimport type { Command } from 'commander';\nimport { output } from '../../output/logger.js';\n\nconst SKILL_DESCRIPTION =\n 'Print agent usage skills (hand-off, agent-shelf, ...) as raw markdown.';\n\nconst SKILL_HELP_AFTER = `\nExamples:\n $ npx rush-ai skill Print the skills index\n $ npx rush-ai skill hand-off Print the hand-off playbook\n $ npx rush-ai skill agent-shelf Print the agent-shelf playbook\n $ npx rush-ai skill --list List available skills\n\nDesigned to be consumed by AI agents inside IDEs (Cursor, Claude Code,\netc). Output is raw markdown on stdout — pipe it, grep it, feed it to\nyour own context.\n`;\n\n/**\n * Resolve the directory that contains the shipped skills/ markdown.\n *\n * Priority, highest first:\n * 1. `<bundle-dir>/skills/` — after `tsup` builds, index.js sits in\n * `dist/` and tsup copies `skills/` to `dist/skills/`, so the\n * sibling path wins.\n * 2. `<bundle-dir>/../skills/` — when running from source (tests,\n * ts-node, linked monorepo), the caller is roughly\n * `packages/rush-ai/src/commands/skill/`, so walking up lands on\n * the package-root `skills/`.\n * 3. `<bundle-dir>/../../../../skills/` — safety net for deeper\n * nesting if the build layout ever changes.\n */\nfunction resolveSkillsDir(): string | null {\n const baseDir = import.meta.dirname ?? __dirname;\n if (!baseDir) return null;\n\n const candidates = [\n resolve(baseDir, 'skills'),\n resolve(baseDir, '..', 'skills'),\n resolve(baseDir, '..', '..', 'skills'),\n resolve(baseDir, '..', '..', '..', 'skills'),\n resolve(baseDir, '..', '..', '..', '..', 'skills'),\n ];\n\n for (const p of candidates) {\n if (existsSync(p)) return p;\n }\n return null;\n}\n\nfunction listSkills(dir: string): string[] {\n return readdirSync(dir)\n .filter((f) => f.endsWith('.md'))\n .map((f) => basename(f, '.md'))\n .sort();\n}\n\nexport function registerSkillCommand(program: Command): void {\n program\n .command('skill [name]')\n .description(SKILL_DESCRIPTION)\n .addHelpText('after', SKILL_HELP_AFTER)\n .option('--list', 'List available skill names and exit')\n .action((name: string | undefined, opts: { list?: boolean }) => {\n const dir = resolveSkillsDir();\n\n if (!dir) {\n output.error(\n 'Could not locate the skills/ directory. This is a packaging bug — please file an issue.'\n );\n process.exit(2);\n return;\n }\n\n const available = listSkills(dir);\n\n if (opts.list) {\n for (const s of available) output.log(s);\n return;\n }\n\n // Default target is the README (the index).\n const target = name ?? 'README';\n const file = resolve(dir, `${target}.md`);\n\n if (!existsSync(file)) {\n output.error(`Unknown skill \"${target}\".`);\n output.dim(`Available: ${available.join(', ') || '(none)'}`);\n process.exit(1);\n return;\n }\n\n // Raw markdown to stdout — the whole point is to be agent-consumable.\n process.stdout.write(readFileSync(file, 'utf-8'));\n });\n}\n","import { createWriteStream } from 'node:fs';\nimport { mkdir, readFile, stat } from 'node:fs/promises';\nimport path from 'node:path';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\nimport type { Command } from 'commander';\nimport { colorizeDiff, containsDiff } from '../../output/diff.js';\nimport { formatOutput, resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport { isCIMode } from '../../util/ci.js';\nimport { createClient } from '../../util/client.js';\nimport { RushError, TaskFailedError } from '../../util/errors.js';\nimport { requireAuth } from '../../util/require-auth.js';\nimport { consumeSSEStreamWithReconnect } from '../../util/sse.js';\nimport { readStdinIfPiped } from '../../util/stdin.js';\n\ninterface TaskCreateResponse {\n id: string;\n status: string;\n}\n\ninterface TaskResult {\n id: string;\n name?: string;\n status:\n | 'pending'\n | 'running'\n | 'waiting_input'\n | 'completed'\n | 'failed'\n | 'cancelled';\n agent: string;\n prompt: string;\n result?: unknown;\n error?: string;\n template?: string | null;\n podImageName?: string | null;\n previewUrl?: string | null;\n gitRepoUrl?: string | null;\n deployedProductionUrl?: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\ninterface TaskListResponse {\n tasks: TaskResult[];\n total: number;\n}\n\ninterface TaskFile {\n id: string;\n filePath: string;\n fileName: string;\n ossPath?: string;\n ossUrl?: string;\n category: string;\n source: string;\n timestamp?: number;\n url?: string;\n conversationId?: string;\n}\n\ninterface TaskFilesResponse {\n files: TaskFile[];\n totalCount: number;\n}\n\ninterface ConversationInfo {\n id: string;\n title: string | null;\n model: string;\n message_count: number;\n created_at: string;\n updated_at: string;\n created_by_name: string | null;\n}\n\ninterface ConversationsResponse {\n conversations: ConversationInfo[];\n}\n\ninterface UIMessagePart {\n type: string;\n text?: string;\n toolName?: string;\n state?: string;\n reasoning?: string;\n}\n\ninterface UIMessage {\n id: string;\n role: string;\n content: string;\n parts?: UIMessagePart[];\n createdAt?: string | number;\n metadata?: Record<string, unknown>;\n}\n\ninterface MessagesResponse {\n messages: UIMessage[];\n metadata?: {\n id: string;\n title?: string;\n createdAt?: string;\n updatedAt?: string;\n messageCount?: number;\n };\n}\n\nconst MAX_TEXT_LENGTH = 500;\n\n/**\n * Truncate text to MAX_TEXT_LENGTH characters, appending \"...\" if truncated.\n */\nfunction truncateText(text: string): string {\n if (text.length <= MAX_TEXT_LENGTH) return text;\n return text.slice(0, MAX_TEXT_LENGTH) + '...';\n}\n\n/**\n * Extract a short summary of tool invocations from message parts.\n * Returns something like: \"Write(src/App.tsx), Bash(npm run build)\"\n */\nfunction summarizeTools(parts: UIMessagePart[]): string {\n const toolParts = parts.filter(\n (p) => p.type === 'tool-invocation' || p.type.startsWith('tool-')\n );\n if (toolParts.length === 0) return '';\n return toolParts\n .map((p) => {\n if (p.toolName) return p.toolName;\n if (p.type.startsWith('tool-')) return p.type.slice(5);\n return 'unknown';\n })\n .join(', ');\n}\n\n/**\n * Format a timestamp for display.\n */\nfunction formatTimestamp(ts: string | number | undefined): string {\n if (!ts) return '';\n const d = new Date(ts);\n return d.toLocaleString();\n}\n\n/**\n * Render messages in human-readable format.\n */\nfunction renderMessages(\n messages: UIMessage[],\n conversationId: string,\n compact: boolean\n): void {\n output.log(\n output.bold(`Conversation ${conversationId} — ${messages.length} messages`)\n );\n output.newline();\n\n for (const msg of messages) {\n const ts = formatTimestamp(msg.createdAt);\n output.log(`[${msg.role}] ${ts}`);\n\n // Collect text content\n const textParts = (msg.parts ?? []).filter(\n (p) => p.type === 'text' && p.text\n );\n if (textParts.length > 0) {\n for (const tp of textParts) {\n output.log(` ${truncateText(tp.text!)}`);\n }\n } else if (msg.content) {\n output.log(` ${truncateText(msg.content)}`);\n }\n\n // Show tool summary for assistant messages (unless compact)\n if (!compact && msg.role === 'assistant' && msg.parts) {\n const toolSummary = summarizeTools(msg.parts);\n if (toolSummary) {\n output.dim(` Tools: ${toolSummary}`);\n }\n }\n\n output.newline();\n }\n}\n\nconst VALID_CATEGORIES = ['code', 'image', 'document', 'other'];\n\n/**\n * Sanitize a file path to prevent path traversal attacks.\n * Returns null if the path is unsafe.\n */\nfunction sanitizeFilePath(filePath: string, outputDir: string): string | null {\n // Strip leading slashes and drive letters\n let stripped = filePath.replace(/^[/\\\\]+/, '').replace(/^[a-zA-Z]:/, '');\n // Remove any .. components\n stripped = stripped\n .split('/')\n .filter((seg) => seg !== '..' && seg !== '.')\n .join('/');\n\n if (!stripped) return null;\n\n const resolvedOutput = path.resolve(outputDir);\n const target = path.resolve(resolvedOutput, stripped);\n const rel = path.relative(resolvedOutput, target);\n\n // Reject if relative path escapes the output directory\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\n return null;\n }\n\n return target;\n}\n\n/**\n * Download a file via the API proxy (handles private OSS buckets).\n * Falls back to direct URL fetch if ossPath is not available.\n */\nasync function downloadFile(\n file: { ossPath?: string; ossUrl?: string },\n destPath: string,\n client: ReturnType<typeof createClient>\n): Promise<void> {\n let response: Response | null = null;\n\n if (file.ossPath) {\n try {\n const proxyResponse = await client.fetchRaw(\n `/api/files/oss-preview?action=file&path=${encodeURIComponent(file.ossPath)}`\n );\n if (proxyResponse.ok) {\n response = proxyResponse;\n }\n } catch {\n // Proxy failed, will fallback to ossUrl\n }\n }\n\n if (!response && file.ossUrl) {\n response = await fetch(file.ossUrl);\n }\n\n if (!response) {\n throw new Error('No download path available');\n }\n\n if (!response.ok) {\n throw new Error(\n `Download failed: ${response.status} ${response.statusText}`\n );\n }\n if (!response.body) {\n throw new Error('No response body');\n }\n\n await mkdir(path.dirname(destPath), { recursive: true });\n\n const nodeStream = Readable.fromWeb(\n response.body as import('node:stream/web').ReadableStream\n );\n const fileStream = createWriteStream(destPath);\n await pipeline(nodeStream, fileStream);\n}\n\n/**\n * Build a file category summary string, e.g. \"8 code, 2 image, 2 document\"\n */\nfunction buildCategorySummary(files: TaskFile[]): string {\n const counts: Record<string, number> = {};\n for (const f of files) {\n counts[f.category] = (counts[f.category] || 0) + 1;\n }\n return Object.entries(counts)\n .map(([cat, count]) => `${count} ${cat}`)\n .join(', ');\n}\n\nimport { registerDeploySubcommand } from './deploy.js';\nimport { registerDomainSubcommand } from './domain.js';\nimport { registerLinkSubcommand } from './link.js';\nimport { registerPushSubcommand } from './push.js';\nimport { registerVersionsSubcommand } from './versions.js';\n\nconst TASK_HELP_AFTER = `\nWhen to use:\n Call \\`task create\\` whenever the next unit of work belongs on the Rush\n platform instead of your local machine — a specialist Rush agent runs\n it, and you get a task id to track / iterate on.\n\nTypical flows:\n # Hand-off from an IDE conversation → Rush agent builds, you iterate\n $ rush-ai task create -a web-builder -p \"<prompt synthesized from context>\" --json\n $ rush-ai task status <id> --json # includes previewUrl / gitRepoUrl\n $ rush-ai task send <id> -p \"<follow-up>\" # same task, keep iterating\n\n # Quick one-shot with the default agent (\\`rush\\`)\n $ rush-ai task create -a rush -p \"Summarize the latest release notes\"\n\nFor agents: run \\`rush-ai skill hand-off\\` for the full playbook.\n`;\n\nexport function registerTaskCommand(program: Command): void {\n const task = program\n .command('task')\n .description('Create and manage tasks')\n .addHelpText('after', TASK_HELP_AFTER);\n\n registerPushSubcommand(task, program);\n // task link — link a local project to Rush (pod + optional DB / OSS) (J-3, M3-3).\n // Formerly `task init`; the old name remains as a deprecated alias until 0.13.0 (issue #1150).\n registerLinkSubcommand(task, program);\n // task deploy <id> — web-builder publish flow (agent-A task-3)\n registerDeploySubcommand(task, program);\n // task versions <id> — list buildable versions (agent-A task-4)\n registerVersionsSubcommand(task, program);\n // task domain check <prefix> --task <id> — domain prefix preflight (agent-A task-5)\n registerDomainSubcommand(task, program);\n\n task\n .command('create')\n .description('Create a task asynchronously')\n .option(\n '-a, --agent <name>',\n 'Agent name to execute the task (defaults to `rush`)',\n 'rush'\n )\n .option('-p, --prompt <text>', 'Task prompt')\n .option('--skills <skills>', 'Comma-separated skills to add', commaSplit)\n .option('--mcp <servers>', 'Comma-separated MCP servers to add', commaSplit)\n .action(\n async (options: {\n agent: string;\n prompt?: string;\n skills?: string[];\n mcp?: string[];\n }) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n const prompt = options.prompt ?? (await readStdinIfPiped());\n if (!prompt) {\n throw new RushError(\n 'No prompt provided. Use --prompt or pipe via stdin.'\n );\n }\n\n const { data } = await client.post<TaskCreateResponse>('/api/tasks', {\n agent: options.agent,\n prompt,\n skills: options.skills,\n mcpServers: options.mcp,\n });\n\n if (format === 'json') {\n output.log(JSON.stringify(data, null, 2));\n } else {\n output.success(`Task created: ${data.id}`);\n output.dim(`Status: ${data.status}`);\n output.dim(\n `Run \\`rush-ai task status ${data.id}\\` to check progress.`\n );\n }\n }\n );\n\n task\n .command('send')\n .description('Send a message to an existing task')\n .argument('<id>', 'Task ID (project ID)')\n .option('-p, --prompt <text>', 'Message to send')\n .option('--skills <skills>', 'Comma-separated skills to add', commaSplit)\n .option('--mcp <servers>', 'Comma-separated MCP servers to add', commaSplit)\n .option(\n '--file <path>',\n 'Attach a local file as context for the agent (repeatable)',\n collectFile,\n []\n )\n .action(\n async (\n id: string,\n options: {\n prompt?: string;\n skills?: string[];\n mcp?: string[];\n file?: string[];\n }\n ) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n const prompt = options.prompt ?? (await readStdinIfPiped());\n if (!prompt) {\n throw new RushError(\n 'No prompt provided. Use --prompt or pipe via stdin.'\n );\n }\n\n // Upload attachments before the send request so the task starts with\n // file parts already in place. A small bounded concurrency keeps the\n // happy path fast for users attaching many small files while still\n // surfacing the first failure quickly.\n const files: UploadedAttachment[] = [];\n const pendingPaths = options.file ?? [];\n const UPLOAD_CONCURRENCY = 4;\n for (let i = 0; i < pendingPaths.length; i += UPLOAD_CONCURRENCY) {\n const batch = pendingPaths.slice(i, i + UPLOAD_CONCURRENCY);\n if (format !== 'json') {\n for (const p of batch) output.dim(`Uploading ${p}...`);\n }\n const uploaded = await Promise.all(\n batch.map((p) => uploadFileForTask(client, p))\n );\n files.push(...uploaded);\n }\n\n const { data } = await client.post<TaskCreateResponse>(\n `/api/tasks/${encodeURIComponent(id)}/send`,\n {\n prompt,\n skills: options.skills,\n mcpServers: options.mcp,\n ...(files.length > 0 ? { files } : {}),\n }\n );\n\n if (format === 'json') {\n output.log(JSON.stringify(data, null, 2));\n } else {\n output.success(`Message sent to task: ${data.id}`);\n output.dim(`Status: ${data.status}`);\n if (files.length > 0) {\n output.dim(`Attached ${files.length} file(s).`);\n }\n output.dim(\n `Run \\`rush-ai task watch ${data.id}\\` to follow progress.`\n );\n }\n }\n );\n\n task\n .command('status')\n .description('Check task status')\n .argument('<id>', 'Task ID')\n .action(async (id: string) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n const { data } = await client.get<TaskResult>(\n `/api/tasks/${encodeURIComponent(id)}`\n );\n\n if (format === 'json') {\n output.log(JSON.stringify(data, null, 2));\n } else {\n output.log(output.bold(`Task ${data.id}`));\n output.log(` Agent: ${data.agent}`);\n output.log(` Status: ${data.status}`);\n if (data.template) {\n output.log(` Template: ${data.template}`);\n }\n if (data.podImageName) {\n output.log(` Image: ${data.podImageName}`);\n }\n output.log(` Created: ${new Date(data.createdAt).toLocaleString()}`);\n output.log(` Updated: ${new Date(data.updatedAt).toLocaleString()}`);\n if (data.previewUrl) {\n output.log(` Preview: ${data.previewUrl}`);\n }\n if (data.gitRepoUrl) {\n output.log(` Git: ${data.gitRepoUrl}`);\n }\n if (data.deployedProductionUrl) {\n output.log(` Production: ${data.deployedProductionUrl}`);\n }\n if (data.error) {\n output.error(` Error: ${data.error}`);\n }\n }\n\n // In CI mode, a failed task should produce a non-zero exit code\n if (isCIMode(program.opts()) && data.status === 'failed') {\n throw new TaskFailedError(\n `Task ${id} failed: ${data.error ?? 'unknown error'}`,\n { taskId: id, status: data.status }\n );\n }\n });\n\n task\n .command('result')\n .description('Get task result')\n .argument('<id>', 'Task ID')\n .action(async (id: string) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n const { data } = await client.get<{ result: unknown }>(\n `/api/tasks/${encodeURIComponent(id)}/result`\n );\n\n // Try to fetch file summary (non-blocking)\n let filesSummary: { totalCount: number; summary: string } | null = null;\n try {\n const { data: filesData } = await client.get<TaskFilesResponse>(\n `/api/chat/${encodeURIComponent(id)}/files`\n );\n if (filesData.totalCount > 0) {\n filesSummary = {\n totalCount: filesData.totalCount,\n summary: buildCategorySummary(filesData.files),\n };\n }\n } catch {\n // Silently degrade - file summary is an enhancement\n }\n\n if (format === 'json') {\n output.log(\n JSON.stringify(\n { ...data, filesSummary: filesSummary ?? undefined },\n null,\n 2\n )\n );\n } else {\n if (typeof data.result === 'string') {\n output.log(\n containsDiff(data.result) ? colorizeDiff(data.result) : data.result\n );\n } else {\n output.log(JSON.stringify(data.result, null, 2));\n }\n\n if (filesSummary) {\n output.newline();\n output.dim(\n `Files: ${filesSummary.totalCount} files (${filesSummary.summary})`\n );\n output.dim(`Run \\`rush-ai task files ${id}\\` to view details.`);\n }\n }\n });\n\n task\n .command('files')\n .description('List and download task artifact files')\n .argument('<id>', 'Task ID')\n .option('--download <path>', 'Download a specific file by its path')\n .option('--download-all', 'Download all files')\n .option('-o, --output <dir>', 'Output directory for downloads')\n .option(\n '-c, --category <type>',\n 'Filter by category: code, image, document, other'\n )\n .action(\n async (\n id: string,\n options: {\n download?: string;\n downloadAll?: boolean;\n output?: string;\n category?: string;\n }\n ) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n const { data } = await client.get<TaskFilesResponse>(\n `/api/chat/${encodeURIComponent(id)}/files`\n );\n\n let files = data.files;\n\n // Filter by category\n if (options.category && VALID_CATEGORIES.includes(options.category)) {\n files = files.filter((f) => f.category === options.category);\n } else if (options.category) {\n output.warn(\n `Unknown category \"${options.category}\". Showing all files.`\n );\n }\n\n // Single file download\n if (options.download) {\n const file = files.find(\n (f) =>\n f.filePath === options.download || f.fileName === options.download\n );\n if (!file) {\n output.error(`File not found: ${options.download}`);\n process.exit(1);\n }\n if (!file.ossPath && !file.ossUrl) {\n output.error(`File \"${file.fileName}\" has no download path.`);\n process.exit(1);\n }\n\n const outputDir = options.output || '.';\n const destPath = sanitizeFilePath(file.filePath, outputDir);\n if (!destPath) {\n output.error(`Unsafe file path: ${file.filePath}`);\n process.exit(1);\n }\n\n await downloadFile(file, destPath, client);\n\n if (format === 'json') {\n output.log(\n JSON.stringify(\n { downloaded: file.filePath, dest: destPath },\n null,\n 2\n )\n );\n } else {\n output.success(`Downloaded: ${file.filePath} -> ${destPath}`);\n }\n return;\n }\n\n // Batch download\n if (options.downloadAll) {\n const outputDir = options.output || `task-files-${id.slice(0, 8)}`;\n let downloaded = 0;\n let skipped = 0;\n let failed = 0;\n\n for (const file of files) {\n if (!file.ossPath && !file.ossUrl) {\n skipped++;\n if (format !== 'json') {\n output.dim(` Skipped (no download path): ${file.filePath}`);\n }\n continue;\n }\n\n const destPath = sanitizeFilePath(file.filePath, outputDir);\n if (!destPath) {\n skipped++;\n if (format !== 'json') {\n output.warn(` Skipped (unsafe path): ${file.filePath}`);\n }\n continue;\n }\n\n try {\n await downloadFile(file, destPath, client);\n downloaded++;\n if (format !== 'json') {\n output.dim(` Downloaded: ${file.filePath}`);\n }\n } catch {\n failed++;\n if (format !== 'json') {\n output.warn(` Failed: ${file.filePath}`);\n }\n }\n }\n\n if (format === 'json') {\n output.log(\n JSON.stringify(\n { outputDir, downloaded, skipped, failed, total: files.length },\n null,\n 2\n )\n );\n } else {\n output.newline();\n output.success(\n `Downloaded ${downloaded}/${files.length} files to ${outputDir}`\n );\n if (skipped > 0) {\n output.dim(\n ` ${skipped} skipped (no download URL or unsafe path)`\n );\n }\n if (failed > 0) {\n output.warn(` ${failed} failed`);\n }\n }\n return;\n }\n\n // Default: list files\n if (format === 'json') {\n output.log(\n JSON.stringify({ files, totalCount: files.length }, null, 2)\n );\n return;\n }\n\n if (files.length === 0) {\n output.info('No files found.');\n return;\n }\n\n output.log(output.bold(`Files (${files.length} total):`));\n output.newline();\n\n const rows = files.map((f) => ({\n Name: f.fileName,\n Category: f.category,\n Source: f.source,\n Path: f.filePath,\n }));\n\n output.log(formatOutput(rows, format));\n }\n );\n\n task\n .command('messages')\n .description('View conversation messages for a task')\n .argument('<id>', 'Task ID (project ID)')\n .option('-c, --conversation <id>', 'Specific conversation ID')\n .option('--last <n>', 'Show only last N messages')\n .option('--role <role>', 'Filter by role (user, assistant, system)')\n .option('--compact', 'Text-only output, no tool details or reasoning')\n .action(\n async (\n id: string,\n options: {\n conversation?: string;\n last?: string;\n role?: string;\n compact?: boolean;\n }\n ) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n let conversationId = options.conversation;\n\n // If no conversation ID provided, fetch the latest one\n if (!conversationId) {\n const { data: convData } = await client.get<ConversationsResponse>(\n `/api/chat/${encodeURIComponent(id)}/conversations`\n );\n\n if (!convData.conversations || convData.conversations.length === 0) {\n if (format === 'json') {\n output.log(\n JSON.stringify({ messages: [], conversationId: null }, null, 2)\n );\n } else {\n output.info('No conversations found for this task.');\n }\n return;\n }\n\n conversationId = convData.conversations[0].id;\n }\n\n const { data } = await client.get<MessagesResponse>(\n `/api/chat/${encodeURIComponent(id)}/conversations/${encodeURIComponent(conversationId)}`\n );\n\n let messages = data.messages ?? [];\n\n if (messages.length === 0) {\n if (format === 'json') {\n output.log(\n JSON.stringify({ messages: [], conversationId }, null, 2)\n );\n } else {\n output.info('No messages in this conversation.');\n }\n return;\n }\n\n // Filter by role\n if (options.role) {\n messages = messages.filter((m) => m.role === options.role);\n }\n\n // Limit to last N messages\n if (options.last) {\n const n = parseInt(options.last, 10);\n if (!isNaN(n) && n > 0) {\n messages = messages.slice(-n);\n }\n }\n\n if (format === 'json') {\n output.log(\n JSON.stringify(\n { messages, conversationId, metadata: data.metadata },\n null,\n 2\n )\n );\n } else {\n renderMessages(messages, conversationId, !!options.compact);\n }\n }\n );\n\n task\n .command('list')\n .alias('ls')\n .description('List tasks')\n .option('-l, --limit <limit>', 'Maximum number of tasks', '50')\n .option('-s, --status <status>', 'Filter by status')\n .action(async (options: { limit?: string; status?: string }) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n const params = new URLSearchParams();\n if (options.limit) params.set('limit', options.limit);\n if (options.status) params.set('status', options.status);\n\n const { data } = await client.get<TaskListResponse>(\n `/api/tasks?${params.toString()}`\n );\n\n if (format === 'json') {\n output.log(JSON.stringify(data, null, 2));\n return;\n }\n\n if (data.tasks.length === 0) {\n output.info('No tasks found.');\n return;\n }\n\n output.log(output.bold(`Tasks (${data.total} total):`));\n output.newline();\n\n const rows = data.tasks.map((t) => ({\n ID: t.id,\n Name: t.name ?? '',\n Agent: t.agent,\n Status: t.status,\n Created: new Date(t.createdAt).toLocaleString(),\n }));\n\n output.log(formatOutput(rows, format));\n });\n\n task\n .command('watch')\n .description('Watch task execution in real-time')\n .argument('<id>', 'Task ID')\n .option(\n '--since <iso>',\n 'Replay events from this ISO timestamp (default: only new events after the latest user turn)'\n )\n .action(async (id: string, opts: { since?: string }) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n if (opts.since && Number.isNaN(new Date(opts.since).getTime())) {\n throw new RushError(\n `Invalid --since value: \"${opts.since}\" (expected ISO 8601 timestamp)`\n );\n }\n\n if (format !== 'json') {\n output.log(output.bold(`Watching task ${id}...`));\n output.newline();\n }\n\n // waiting_input isn't a terminal status on the server (the stream\n // stays open while the agent is blocked), but from the CLI's POV\n // there's nothing more to watch — the user must answer in the web UI\n // before new events will flow. Treat it as an exit point here.\n const TERMINAL_STATUSES = [\n 'completed',\n 'failed',\n 'cancelled',\n 'waiting_input',\n ];\n\n const eventsPath = opts.since\n ? `/api/tasks/${encodeURIComponent(id)}/events?since=${encodeURIComponent(opts.since)}`\n : `/api/tasks/${encodeURIComponent(id)}/events`;\n\n const connectFn = async () => {\n const response = await client.fetchRaw(eventsPath, { method: 'GET' });\n if (!response.body) {\n throw new Error('No response body for SSE');\n }\n return response.body;\n };\n\n let finalStatus = '';\n\n await consumeSSEStreamWithReconnect(\n connectFn,\n (event) => {\n if (format === 'json') {\n output.log(JSON.stringify(event));\n } else {\n renderTaskEvent(event);\n }\n if (event.type === 'status') {\n finalStatus = event.data;\n }\n },\n {\n isTerminal: (event) =>\n event.type === 'status' && TERMINAL_STATUSES.includes(event.data),\n onReconnect: (attempt) => {\n if (format !== 'json') {\n output.dim(\n `\\u27F3 SSE connection lost. Reconnecting (${attempt}/5)...`\n );\n }\n },\n }\n );\n\n if (format !== 'json') {\n output.newline();\n if (finalStatus === 'completed') {\n output.success('Task completed.');\n } else if (finalStatus === 'failed') {\n output.error('Task failed.');\n } else if (finalStatus === 'cancelled') {\n output.info('Task cancelled.');\n } else if (finalStatus === 'waiting_input') {\n // watch 已经提前退出(waiting_input 不是终态,但客户端超时/\n // 主动中断后最后看到的状态就是它)。提示用户去 UI 回答或\n // 重新 watch。\n output.info(\n 'Agent is waiting for user input. Answer in the web UI, then run `rush-ai task watch` again.'\n );\n }\n }\n });\n\n task\n .command('cancel')\n .description('Cancel a running task')\n .argument('<id>', 'Task ID')\n .action(async (id: string) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n await client.delete(`/api/tasks/${encodeURIComponent(id)}`);\n\n if (format === 'json') {\n output.log(JSON.stringify({ id, status: 'cancelled' }));\n } else {\n output.success(`Task ${id} cancelled.`);\n }\n });\n}\n\nfunction commaSplit(value: string): string[] {\n return value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n}\n\nfunction collectFile(value: string, previous: string[] = []): string[] {\n return [...previous, value];\n}\n\n/** Shape of the attachment list forwarded to the send API. */\ninterface UploadedAttachment {\n url: string;\n mediaType: string;\n filename: string;\n}\n\n/** Shape of /api/files/upload success response (from FileUploadResult). */\ninterface FileUploadResponse {\n success: true;\n url: string;\n ossUrl: string;\n filename: string;\n size: number;\n mediaType: string;\n}\n\nconst EXTENSION_TO_MIME: Record<string, string> = {\n // Images — kept in sync with packages/shared/src/const/file-limits.ts\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n gif: 'image/gif',\n webp: 'image/webp',\n bmp: 'image/bmp',\n svg: 'image/svg+xml',\n ico: 'image/x-icon',\n // Documents / text\n pdf: 'application/pdf',\n txt: 'text/plain',\n md: 'text/markdown',\n markdown: 'text/markdown',\n csv: 'text/csv',\n json: 'application/json',\n html: 'text/html',\n // Code (registered as octet-stream by default; server only uses the ext)\n};\n\nfunction guessMediaType(filePath: string): string {\n const ext = path.extname(filePath).slice(1).toLowerCase();\n return EXTENSION_TO_MIME[ext] ?? 'application/octet-stream';\n}\n\nasync function uploadFileForTask(\n client: ReturnType<typeof createClient>,\n localPath: string\n): Promise<UploadedAttachment> {\n const resolved = path.resolve(localPath);\n\n let fileStat: Awaited<ReturnType<typeof stat>>;\n try {\n fileStat = await stat(resolved);\n } catch {\n throw new RushError(`File not found: ${localPath}`);\n }\n if (!fileStat.isFile()) {\n throw new RushError(`Not a regular file: ${localPath}`);\n }\n\n if (typeof File === 'undefined') {\n throw new RushError(\n 'Node 20+ is required for --file attachments (global File is missing)'\n );\n }\n\n const filename = path.basename(resolved);\n const mediaType = guessMediaType(resolved);\n const buf = await readFile(resolved);\n // Node's undici File is sufficient for FormData; the backend reads\n // formData.get('file') as a File and passes it to OSS.\n const blob = new File([buf], filename, { type: mediaType });\n\n const formData = new FormData();\n formData.append('file', blob);\n\n // scope=owner tells the server to write the object at\n // {OSS_DIR}/users/<ldapId>/... where ldapId comes from the session.\n // /api/tasks/[id]/send later verifies the OSS URL path contains\n // /users/<currentUserLdap>/, so a caller cannot reference another user's\n // attachment even if they guess the URL.\n const { data } = await client.postFormData<FileUploadResponse>(\n '/api/files/upload?scope=owner',\n formData\n );\n\n return {\n url: data.ossUrl,\n mediaType: data.mediaType || mediaType,\n filename: data.filename ?? filename,\n };\n}\n\nfunction renderTaskEvent(event: { type: string; data: string }): void {\n switch (event.type) {\n case 'status':\n output.info(`Status: ${event.data}`);\n break;\n case 'content':\n process.stdout.write(event.data);\n break;\n case 'error':\n output.error(event.data || 'Unknown error');\n break;\n case 'progress':\n output.dim(`Progress: ${event.data}`);\n break;\n case 'raw':\n process.stdout.write(event.data);\n break;\n default:\n output.dim(JSON.stringify(event));\n }\n}\n","import chalk from 'chalk';\n\n/** Detect whether text contains unified diff content. */\nexport function containsDiff(text: string): boolean {\n return /^@@\\s+-\\d+/m.test(text) || /^---\\s+a\\//m.test(text);\n}\n\n/** Colorize unified diff lines. */\nexport function colorizeDiff(text: string): string {\n return text\n .split('\\n')\n .map((line) => {\n if (line.startsWith('+++') || line.startsWith('---'))\n return chalk.bold(line);\n if (line.startsWith('+')) return chalk.green(line);\n if (line.startsWith('-')) return chalk.red(line);\n if (line.startsWith('@@')) return chalk.cyan(line);\n return line;\n })\n .join('\\n');\n}\n","/**\n * Read stdin if data is being piped (non-TTY).\n * Returns null if stdin is a TTY (interactive mode).\n */\nexport async function readStdinIfPiped(): Promise<string | null> {\n if (process.stdin.isTTY) {\n return null;\n }\n\n return new Promise<string>((resolve, reject) => {\n const chunks: Buffer[] = [];\n\n process.stdin.on('data', (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n process.stdin.on('end', () => {\n resolve(Buffer.concat(chunks).toString('utf-8').trim());\n });\n\n process.stdin.on('error', reject);\n });\n}\n","import type { ApiResponse, RushClient } from './client.js';\nimport { ApiError, RushError } from './errors.js';\n\n/**\n * Mirror of `packages/shared/src/const/agent-builtins.ts` — rush-ai is a\n * standalone published npm package and cannot depend on the private\n * `@cortex/shared` workspace. Keep in sync manually.\n */\nexport const BUILTIN_AGENT_NAMES = {\n WEB_BUILDER: 'web-builder',\n SKILL_PUBLISHER: 'skill-publisher',\n RUSH: 'rush',\n} as const;\n\n/**\n * Response shape for `GET /api/tasks/:id`. Kept loose — only the fields\n * we actually consume in the deploy lifecycle are typed here.\n */\nexport interface ApiTaskResult {\n id: string;\n agent: string;\n template?: string | null;\n deployedProductionUrl?: string | null;\n [key: string]: unknown;\n}\n\nexport interface WebBuilderTask {\n /** Raw `/api/tasks/:id` response. */\n task: ApiTaskResult;\n /** Guaranteed non-empty template string. */\n template: string;\n /** `true` when template is the Next.js full-stack template (pod deploy). */\n isNextjs: boolean;\n}\n\n/**\n * HTTP client subset consumed by `resolveWebBuilderTask`. Typed as a\n * structural minimum so tests can pass a plain fake without pulling in the\n * whole `RushClient` surface.\n */\nexport interface ApiTaskClient {\n get: RushClient['get'];\n}\n\nconst NEXTJS_TEMPLATE = 'nextjs-fullstack';\n\n/**\n * Fetch a task and assert it is a deployable web-builder task.\n *\n * Single source of truth for the web-builder gate. All new deploy-lifecycle\n * commands (`task deploy`, `task versions`, `task domain check`) must go\n * through this helper; no command re-implements the four gating checks.\n *\n * Throws `RushError` on:\n * - 404 (task not found)\n * - `task.agent !== 'web-builder'`\n * - `task.template` null / empty / whitespace-only\n *\n * Other HTTP errors propagate as `ApiError` from the underlying client.\n */\nexport async function resolveWebBuilderTask(\n client: ApiTaskClient,\n id: string\n): Promise<WebBuilderTask> {\n let response: ApiResponse<ApiTaskResult>;\n try {\n response = await client.get<ApiTaskResult>(\n `/api/tasks/${encodeURIComponent(id)}`\n );\n } catch (err) {\n if (err instanceof ApiError && err.status === 404) {\n // Spec §\"Error-code convention\": no new error codes — keep the default\n // `RUSH_ERROR` and let callers branch on `meta.reason`.\n throw new RushError(`Task not found: ${id}`, {\n taskId: id,\n reason: 'task_not_found',\n });\n }\n throw err;\n }\n\n const task = response.data;\n\n if (task.agent !== BUILTIN_AGENT_NAMES.WEB_BUILDER) {\n throw new RushError(\n `Only web-builder tasks are deployable. Task uses agent \\`${task.agent}\\`.`,\n { taskId: id, agent: task.agent, reason: 'not_web_builder' }\n );\n }\n\n const template =\n typeof task.template === 'string' ? task.template.trim() : '';\n if (!template) {\n throw new RushError('Project not yet buildable — template is null.', {\n taskId: id,\n reason: 'template_missing',\n });\n }\n\n return {\n task,\n template,\n isNextjs: template === NEXTJS_TEMPLATE,\n };\n}\n\n/**\n * User-facing CLI env value. Exposed here because downstream commands\n * (`deploy`, `publish-pod-stream`) need a type to bind their `--env` option to.\n */\nexport type CliEnv = 'test' | 'production';\n\n/**\n * `POST /api/projects/:id/publish` body wire value for `environment`.\n *\n * The CLI says `production` to the user; the server expects `online`. This\n * divergence is **not a refactor** — `/publish` is one of two endpoints that\n * uses the `online` literal (the other being `/publish-pod`, which hardcodes\n * it server-side). Keep this function pure so call sites read as\n * `toPublishEnv(opts.env)` rather than re-inlining the ternary.\n */\nexport function toPublishEnv(env: CliEnv): 'test' | 'online' {\n return env === 'production' ? 'online' : 'test';\n}\n\n/**\n * `POST /api/projects/:id/deploy-version` body wire value for `environment`.\n *\n * Identity on the surface, but **intentionally exposed as a function** so\n * call sites document which endpoint they're targeting. The `publish` and\n * `deploy-version` endpoints use different vocabularies (`online` vs.\n * `production`) for the same user concept, and a silent drift here is a 400\n * in production.\n */\nexport function toDeployVersionEnv(env: CliEnv): 'test' | 'production' {\n return env;\n}\n","import type { RushClient } from './client.js';\nimport { RushError } from './errors.js';\n\n/**\n * Event shape emitted by `POST /api/projects/:id/publish-pod`.\n *\n * Note: **different key name from the generic SSE helper in `util/sse.ts`**.\n * The publish-pod endpoint sends `{event, message, data?}` where `event` is\n * the event type, not `type`. The shared SSE parser reads `parsed.type`\n * and would silently skip our events, so this module owns a dedicated\n * one-shot line parser.\n */\nexport type PublishPodEventName =\n | 'deploying'\n | 'ready'\n | 'error'\n | 'timeout'\n | string;\n\nexport interface PublishPodEvent {\n event: PublishPodEventName;\n message: string;\n data?: {\n domain?: string;\n url?: string;\n [key: string]: unknown;\n };\n}\n\nconst TERMINAL_EVENTS: ReadonlySet<string> = new Set([\n 'ready',\n 'error',\n 'timeout',\n]);\n\nexport function isTerminalPublishPodEvent(event: PublishPodEvent): boolean {\n return TERMINAL_EVENTS.has(event.event);\n}\n\n/**\n * Parse a single SSE frame line for publish-pod.\n *\n * Input: full frame text (newline-stripped) containing `data: {...}`.\n * Returns `null` for lines that aren't `data:` frames (blank lines, comment\n * frames starting with `:`, or keep-alives) so callers can just skip them.\n *\n * Throws `RushError` when a `data:` frame is present but the payload fails\n * to parse or doesn't match the expected shape — a malformed frame mid-stream\n * is fatal (we can't silently recover: the server might have sent a terminal\n * we couldn't read).\n */\nexport function parsePublishPodEvent(line: string): PublishPodEvent | null {\n const trimmed = line.trimEnd();\n if (trimmed === '' || trimmed.startsWith(':')) {\n return null;\n }\n if (!trimmed.startsWith('data:')) {\n return null;\n }\n\n // `data:` + optional single space (per SSE spec) before the payload\n const payload = trimmed.replace(/^data:\\s?/, '');\n if (payload === '' || payload === '[DONE]') {\n return null;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(payload);\n } catch {\n throw new RushError(\n `Malformed publish-pod SSE payload (not JSON): ${payload.slice(0, 120)}`,\n { reason: 'publish_pod_invalid_frame' }\n );\n }\n\n if (\n !parsed ||\n typeof parsed !== 'object' ||\n typeof (parsed as { event?: unknown }).event !== 'string'\n ) {\n throw new RushError(\n 'Malformed publish-pod SSE payload (missing `event` string)',\n { reason: 'publish_pod_invalid_frame' }\n );\n }\n\n const obj = parsed as Record<string, unknown>;\n const message = typeof obj.message === 'string' ? obj.message : '';\n const data =\n obj.data && typeof obj.data === 'object'\n ? (obj.data as PublishPodEvent['data'])\n : undefined;\n\n return {\n event: obj.event as string,\n message,\n data,\n };\n}\n\nexport interface StreamPublishPodOptions {\n commitId: string;\n customDomainPrefix?: string;\n /** Called for every non-terminal event so callers can render progress. */\n onEvent?: (event: PublishPodEvent) => void;\n}\n\nexport interface StreamPublishPodResult {\n /** The terminal `ready` event's `data.url`, if provided. */\n url?: string;\n /** The terminal `ready` event's `data.domain`, if provided. */\n domain?: string;\n /** Full last event (always a `ready`). Exposed for downstream formatting. */\n readyEvent: PublishPodEvent;\n}\n\n/**\n * One-shot consumption of `POST /api/projects/:id/publish-pod`.\n *\n * Contract (spec §\"SSE handling for publish-pod\"):\n * - **No reconnect.** POST has side effects; re-POSTing would re-trigger a\n * publish. If the stream drops before a terminal event, throw.\n * - **Dedicated parser.** The shared SSE helpers expect `{type, data}`;\n * our endpoint emits `{event, message, data?}`. Reusing them would\n * silently drop events. Hence the bespoke line parser below.\n * - **Terminal events**: `ready` resolves; `error` / `timeout` throws.\n */\nexport async function streamPublishPod(\n client: RushClient,\n projectId: string,\n opts: StreamPublishPodOptions\n): Promise<StreamPublishPodResult> {\n const body: Record<string, unknown> = { commitId: opts.commitId };\n if (opts.customDomainPrefix !== undefined) {\n body.customDomainPrefix = opts.customDomainPrefix;\n }\n\n const response = await client.fetchRaw(\n `/api/projects/${encodeURIComponent(projectId)}/publish-pod`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n },\n body: JSON.stringify(body),\n }\n );\n\n if (!response.body) {\n throw new RushError('publish-pod response has no body', {\n reason: 'publish_pod_no_body',\n });\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let terminal: PublishPodEvent | null = null;\n\n const processFrame = (frame: string): void => {\n // Each SSE \"message\" can have multiple `data:` lines; publish-pod only\n // emits one per message so we parse the first non-null.\n for (const rawLine of frame.split('\\n')) {\n const event = parsePublishPodEvent(rawLine);\n if (!event) continue;\n\n if (isTerminalPublishPodEvent(event)) {\n terminal = event;\n return;\n }\n opts.onEvent?.(event);\n }\n };\n\n try {\n while (!terminal) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n // SSE frames are separated by blank lines (\"\\n\\n\"); process every\n // complete frame we have so far.\n let sepIdx = buffer.indexOf('\\n\\n');\n while (sepIdx !== -1 && !terminal) {\n const frame = buffer.slice(0, sepIdx);\n buffer = buffer.slice(sepIdx + 2);\n processFrame(frame);\n sepIdx = buffer.indexOf('\\n\\n');\n }\n }\n\n // Flush any remaining buffered frame (server may end without trailing\n // blank line).\n if (!terminal && buffer.length > 0) {\n processFrame(buffer);\n buffer = '';\n }\n } finally {\n reader.cancel().catch(() => {});\n }\n\n if (!terminal) {\n throw new RushError('Deployment stream disconnected unexpectedly', {\n reason: 'publish_pod_stream_disconnected',\n });\n }\n\n const finalEvent: PublishPodEvent = terminal;\n if (finalEvent.event === 'error' || finalEvent.event === 'timeout') {\n throw new RushError(\n finalEvent.message || `Deployment ${finalEvent.event}`,\n {\n reason:\n finalEvent.event === 'timeout'\n ? 'publish_pod_timeout'\n : 'publish_pod_error',\n event: finalEvent.event,\n }\n );\n }\n\n return {\n url: finalEvent.data?.url,\n domain: finalEvent.data?.domain,\n readyEvent: finalEvent,\n };\n}\n","/**\n * Derive Rush deployment domain suffix from the configured API base URL.\n *\n * The CLI does not have `window.location`, so it cannot reuse the shared\n * `@cortex/shared/utils/url.getDomainSuffix()` helper (which reads\n * `window.location.hostname`). Instead we look at the configured API origin\n * and match the same convention:\n *\n * rush-test.zhenguanyu.com → rush-dev.zhenguanyu.com (test env)\n * rush.zhenguanyu.com → rush.zhenguanyu.com (production)\n *\n * Anything else (local dev, self-hosted, unknown hostname, unparseable URL)\n * falls back to `rush.zhenguanyu.com`. This mirrors the web UI's\n * `getDomainSuffix()` fallback in `packages/shared/src/utils/url.ts`. The\n * consequence is real: if you run Rush under a custom deployment suffix,\n * the URL the CLI prints may not match the served hostname — until the\n * availability / publish endpoints expose a concrete `deployedDomain`\n * field in their response, there is no safe detection path CLI-side.\n *\n * Kept separate from `config.ts` so tests can exercise the derivation in\n * isolation.\n */\nexport function deriveDomainSuffix(apiBaseUrl: string): string {\n let host: string;\n try {\n host = new URL(apiBaseUrl).hostname;\n } catch {\n return 'rush.zhenguanyu.com';\n }\n if (host.includes('rush-test') || host.includes('local')) {\n return 'rush-dev.zhenguanyu.com';\n }\n return 'rush.zhenguanyu.com';\n}\n\n/**\n * Build the full deploy domain for a given prefix, matching the web UI's\n * `buildDomain({ mode: DEPLOY })` behaviour:\n *\n * test env: `{prefix}-test.{suffix}`\n * production env: `{prefix}.{suffix}`\n */\nexport function buildDeployDomain(\n prefix: string,\n env: 'test' | 'production',\n apiBaseUrl: string\n): string {\n const suffix = deriveDomainSuffix(apiBaseUrl);\n return env === 'test' ? `${prefix}-test.${suffix}` : `${prefix}.${suffix}`;\n}\n\n/** Convenience: full URL (with protocol) for the deploy domain. */\nexport function buildDeployUrl(\n prefix: string,\n env: 'test' | 'production',\n apiBaseUrl: string\n): string {\n return `https://${buildDeployDomain(prefix, env, apiBaseUrl)}`;\n}\n","import { resolve } from 'node:path';\nimport chalk from 'chalk';\nimport type { Command } from 'commander';\nimport { resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport { createClient } from '../../util/client.js';\nimport { readEnvFile } from '../../util/env-file.js';\nimport { RushError } from '../../util/errors.js';\nimport {\n extractRushProjectId,\n getRemoteUrl,\n gitAddAndCommit,\n gitInit,\n gitPushUrl,\n hasUncommittedChanges,\n isGitRepo,\n} from '../../util/git.js';\nimport { requireAuth } from '../../util/require-auth.js';\nimport { runChecks } from '../check/index.js';\n\n/** Partial shape of `GET /api/tasks/:id`. The push flow only cares about\n * identity (`id`) and output (`previewUrl`); other fields are returned by\n * the server but we don't depend on them here. */\ninterface TaskStatusResponse {\n id: string;\n previewUrl?: string | null;\n [key: string]: unknown;\n}\n\n/** Response of `POST /api/repository/sync/pull`. */\ninterface SyncPullResponse {\n success: boolean;\n /**\n * - `pulled`: git pull produced new commits\n * - `up_to_date`: pod already at HEAD\n * - `conflict`: merge conflict → user must rebase locally\n * - `failed`: other server-side failure (body carries details)\n */\n state?: 'pulled' | 'up_to_date' | 'conflict' | 'failed';\n message?: string;\n remoteCommit?: string;\n [key: string]: unknown;\n}\n\n/**\n * Kept for backward-compat with callers that still import it; the new push\n * flow does not show git URLs to the user, so there is nothing to mask.\n */\nexport function maskToken(url: string): string {\n return url.replace(/:([^@]{4})[^@]*@/, ':$1****@');\n}\n\n/**\n * Guidance shown when no rush project can be identified for the working\n * directory. Matches issue #1149's UX spec verbatim: steer users to\n * `task create` (fresh prompt-driven project) or `task init` (adopt an\n * existing local directory). The command name stays `task init` here even\n * though #1150 may rename it — renaming the user-facing verb is out of\n * scope for this PR.\n */\nconst NO_PROJECT_HINT = [\n 'No rush project detected for this directory. Either:',\n ' rush-ai task create -a web-builder -p \"...\" (build a new site from a prompt)',\n ' rush-ai task init (link an existing local project)',\n].join('\\n');\n\nexport function registerPushSubcommand(task: Command, program: Command): void {\n task\n .command('push')\n .description(\n 'Push local changes to an existing Rush project (git push + pod sync/pull)'\n )\n .option('-n, --name <name>', 'Project name (unused; kept for compat)')\n .option('-p, --path <dir>', 'Project directory', '.')\n .action(async (opts: { name?: string; path: string }) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n const projectPath = resolve(opts.path);\n\n // ─── Step 1: Local readiness checks ─────────────────────────────\n if (format !== 'json') {\n output.info('Checking project readiness...');\n }\n\n const report = runChecks(projectPath);\n if (!report.deployable) {\n if (format === 'json') {\n output.log(\n JSON.stringify(\n { error: 'Project has errors that must be fixed', report },\n null,\n 2\n )\n );\n } else {\n output.error('Project has errors that must be fixed before pushing.');\n output.dim('Run `rush-ai check` for details.');\n }\n process.exit(1);\n }\n\n // ─── Step 2: Identify the rush project ──────────────────────────\n //\n // Resolution order (issue #1149):\n // a. Parse `.git/config` remote.origin.url → extract projectId.\n // b. Verify projectId with GET /api/tasks/:id.\n // c. Fallback: .rush/env.md `PROJECT_ID` (for working trees where\n // the git remote was renamed or missing — verify against the\n // same endpoint so we never trust env.md blindly).\n // d. All failed → refuse with a guidance message.\n //\n // `.rush/env.md` is explicitly demoted from \"reuse signal\" to\n // \"fallback identity\" here: the working tree's git remote is the\n // authoritative source of truth.\n const envFile = readEnvFile(projectPath);\n const remoteUrl = isGitRepo(projectPath)\n ? getRemoteUrl(projectPath)\n : null;\n const idFromRemote = remoteUrl ? extractRushProjectId(remoteUrl) : null;\n\n let projectId: string | null = null;\n let taskInfo: TaskStatusResponse | null = null;\n // Tracks how we learned the projectId — determines where we push\n // in step 4 so we never pair a Rush identity with a non-Rush remote\n // (issue #1149 Sparring review P0: \"origin=github + env.md\n // PROJECT_ID would blindly push to GitHub and still report\n // success\").\n let projectSource: 'git-remote' | 'env-md' | null = null;\n\n if (idFromRemote) {\n const verified = await tryVerifyProject(client, idFromRemote);\n if (verified) {\n projectId = idFromRemote;\n taskInfo = verified;\n projectSource = 'git-remote';\n }\n }\n\n if (!projectId && envFile.PROJECT_ID) {\n const verified = await tryVerifyProject(client, envFile.PROJECT_ID);\n if (verified) {\n projectId = envFile.PROJECT_ID;\n taskInfo = verified;\n projectSource = 'env-md';\n }\n }\n\n if (!projectId || !taskInfo || !projectSource) {\n throw new RushError(NO_PROJECT_HINT, {}, 'NO_RUSH_PROJECT', 1);\n }\n\n if (format !== 'json') {\n output.info(`Using Rush project ${projectId}`);\n }\n\n // ─── Step 3: Ensure the working tree has no uncommitted changes ─\n //\n // `task push` intentionally auto-commits (spec §J-2) so IDE flows\n // don't force the user to drop into a terminal mid-edit. Commit\n // message stays `'deploy to rush'` for backward-compat with the\n // old flow (any automation grepping the history still matches).\n if (!isGitRepo(projectPath)) {\n // Edge case: if remote-based identification fell through to\n // env.md, the directory might not even be a git repo. In that\n // case bootstrap one so the subsequent push has something to\n // push — this mirrors the pre-#1149 behaviour.\n gitInit(projectPath);\n }\n\n if (hasUncommittedChanges(projectPath)) {\n gitAddAndCommit(projectPath, 'deploy to rush');\n }\n\n // ─── Step 4: git push ───────────────────────────────────────────\n //\n // Push target depends on HOW we identified the project:\n //\n // projectSource='git-remote' (authoritative)\n // → push to `origin`, with env.md GIT_REMOTE as a CI-only\n // fallback (tokenised URL — users without SSO).\n //\n // projectSource='env-md' (origin is not a Rush URL)\n // → push ONLY to env.md.GIT_REMOTE. Never touch `origin` —\n // it might point at an unrelated repo (e.g. GitHub) and\n // pushing there would succeed, report success to the user,\n // yet leave the Rush project at the old commit (pod\n // sync/pull would be a no-op). Caught by Sparring on #1149.\n if (format !== 'json') {\n output.info('Pushing code to Rush...');\n }\n\n const pushResult = pushWithRouting(\n projectPath,\n projectSource,\n envFile.GIT_REMOTE\n );\n if (!pushResult.success) {\n if (format === 'json') {\n output.log(\n JSON.stringify(\n {\n error: 'Git push failed',\n details: pushResult.stderr,\n projectId,\n },\n null,\n 2\n )\n );\n } else {\n output.error('Git push failed:');\n output.log(pushResult.stderr);\n output.dim(\n 'Check your VPN / SSO session and git credentials. Code was not pushed.'\n );\n output.dim(`Project ID: ${projectId}`);\n }\n process.exit(1);\n }\n\n if (format !== 'json') {\n output.success('Code pushed successfully');\n }\n\n // ─── Step 5: Trigger pod git pull ───────────────────────────────\n //\n // `POST /api/repository/sync/pull` runs `git pull --rebase` inside\n // the project pod under a distributed lock, so Vite HMR picks up\n // the new files within a second or two. Measured end-to-end:\n // git push (~1s) + sync/pull (~1s) + HMR reload (<2s) ≈ 4s, vs\n // 30-60s for the retired restart + pod-ready polling path.\n if (format !== 'json') {\n output.info('Syncing code in the pod...');\n }\n\n let syncResp: SyncPullResponse;\n try {\n const { data } = await client.post<SyncPullResponse>(\n '/api/repository/sync/pull',\n { projectId }\n );\n syncResp = data;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new RushError(\n `Pod sync failed: ${message}`,\n { projectId },\n 'SYNC_PULL_FAILED',\n 1\n );\n }\n\n if (!syncResp.success) {\n if (syncResp.state === 'conflict') {\n throw new RushError(\n 'Pod sync failed: local repository diverged from the pod. ' +\n 'Rebase locally (git pull --rebase origin HEAD) and retry `rush-ai task push`.',\n { projectId, state: 'conflict' },\n 'SYNC_PULL_CONFLICT',\n 1\n );\n }\n throw new RushError(\n `Pod sync failed: ${syncResp.message ?? 'unknown error'}`,\n { projectId, state: syncResp.state },\n 'SYNC_PULL_FAILED',\n 1\n );\n }\n\n // ─── Step 6: Output ─────────────────────────────────────────────\n const previewUrl =\n (taskInfo.previewUrl as string | null | undefined) ??\n envFile.PREVIEW_URL ??\n null;\n const chatUrl = `https://rush.zhenguanyu.com/project/${projectId}`;\n const result = {\n projectId,\n previewUrl,\n chatUrl,\n syncState: syncResp.state ?? 'pulled',\n };\n\n if (format === 'json') {\n output.log(JSON.stringify(result, null, 2));\n return;\n }\n\n output.newline();\n output.success('Push complete!');\n output.log(` ${chalk.bold('Project ID:')} ${result.projectId}`);\n if (result.previewUrl) {\n output.log(` ${chalk.bold('Preview:')} ${result.previewUrl}`);\n }\n output.log(` ${chalk.bold('Chat:')} ${result.chatUrl}`);\n output.log(` ${chalk.bold('Sync:')} ${result.syncState}`);\n output.newline();\n });\n}\n\n/**\n * Verify that a projectId exists via `GET /api/tasks/:id`. Any error\n * (404 / network / 5xx) counts as \"could not verify\" → returns `null` so\n * the caller can fall through to the next resolution strategy.\n *\n * The explicit id-match check (`data.id === id`) defends against a mock or\n * proxy returning a generic OK body for a 200 status.\n */\nasync function tryVerifyProject(\n client: ReturnType<typeof createClient>,\n id: string\n): Promise<TaskStatusResponse | null> {\n try {\n const { data } = await client.get<TaskStatusResponse>(\n `/api/tasks/${encodeURIComponent(id)}`\n );\n if (data && data.id === id) return data;\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Route the `git push` based on how the rush project was identified.\n *\n * The routing rule — re-derived after issue #1149's Sparring review —\n * refuses to push to an `origin` that we could not prove is the rush\n * project. Past behaviour would silently push to a user's GitHub origin\n * (for example) and then trigger the pod `sync/pull` on an unrelated\n * Rush project, yielding a false-positive \"push complete\" in the CLI\n * while the pod kept serving the old commit.\n *\n * Matrix:\n *\n * | source | primary | fallback |\n * |-------------|--------------------|--------------------------|\n * | git-remote | `origin` | env.md GIT_REMOTE (CI) |\n * | env-md | env.md GIT_REMOTE | — (error if unset/masked)|\n *\n * Masked tokens (legacy `****` placeholder values, written by pre-m3-5\n * `task init`) are skipped: they always fail auth, and the resulting\n * \"403 Forbidden\" misleads users into thinking the server rejected their\n * permissions when the real cause is a placeholder secret.\n */\nfunction pushWithRouting(\n projectPath: string,\n source: 'git-remote' | 'env-md',\n envGitRemote: string | undefined\n): { success: boolean; stderr: string } {\n const envRemoteUsable =\n typeof envGitRemote === 'string' &&\n envGitRemote.length > 0 &&\n !envGitRemote.includes('****');\n\n if (source === 'git-remote') {\n const primary = gitPushUrl(projectPath, 'origin');\n if (primary.success) return primary;\n if (!envRemoteUsable) return primary;\n\n const fallback = gitPushUrl(projectPath, envGitRemote as string);\n // Return the fallback error on failure — it's the more actionable\n // one (token probably expired/revoked).\n return fallback;\n }\n\n // source === 'env-md': origin is NOT a rush URL, so the only safe push\n // target is the env.md GIT_REMOTE. Refuse if it's missing or masked.\n if (!envRemoteUsable) {\n return {\n success: false,\n stderr:\n 'Cannot push: .rush/env.md identifies a Rush project but has no usable GIT_REMOTE. ' +\n 'Re-run `rush-ai task init` to refresh the deploy token, or set `origin` to the Rush GitLab URL.',\n };\n }\n return gitPushUrl(projectPath, envGitRemote as string);\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport path from 'node:path';\n\n/**\n * `.rush/env.md` lives in the user's project root. It is **never** committed\n * (the file header says so explicitly, and `ensureRushGitignore` keeps\n * `.rush/` in `.gitignore`).\n *\n * Contract (see `specs/rush-v2/cli/roadmap.spec.md` §M3 invariants 2, 5):\n * - Top of file must carry the `# DO NOT commit` warning. If an existing\n * file lost the warning (user edited it) we re-prepend it on write.\n * - Body is a set of `KEY=VALUE` lines, parsed the usual dotenv way.\n * - `STATE` is a checkpoint for `task link`'s compensating-consistency flow\n * (`pod-only` | `pod-db` | `pod-db-oss` | `complete`). On resume, `task\n * link` reads STATE to skip remote calls it already made.\n * - No logs / error stacks / telemetry ever land here. The serializer only\n * writes fixed keys + a short comment map.\n */\n\nexport type CheckpointState = 'pod-only' | 'pod-db' | 'pod-db-oss' | 'complete';\n\nexport interface RushEnv {\n /** Required once `task link` has created the pod. */\n PROJECT_ID?: string;\n PREVIEW_URL?: string;\n GIT_REMOTE?: string;\n STATE?: CheckpointState;\n // DB (optional)\n DATABASE_URL?: string;\n SUPABASE_URL?: string;\n SUPABASE_ANON_KEY?: string;\n // Rush-proxied object storage (optional; no direct OSS access)\n STORAGE_API_BASE?: string;\n /**\n * Unknown keys are preserved verbatim so hand-edits survive a round-trip.\n * Callers should not rely on this bucket; it is for forward-compat only.\n */\n extras?: Record<string, string>;\n}\n\nexport const ENV_FILE_HEADER = [\n '# Rush Project Environment',\n '',\n '# DO NOT commit — contains credentials',\n '# This file is auto-generated by `rush-ai task link`. Do not write logs / error stacks / telemetry here.',\n '',\n].join('\\n');\n\n/** Resolve `.rush/env.md` for a given project directory. */\nexport function envFilePath(projectPath: string): string {\n return path.join(projectPath, '.rush', 'env.md');\n}\n\n/** Resolve `.rush/agent-handoff.md` for a given project directory. */\nexport function handoffFilePath(projectPath: string): string {\n return path.join(projectPath, '.rush', 'agent-handoff.md');\n}\n\n/**\n * Parse `.rush/env.md`. Returns an empty `RushEnv` if the file does not\n * exist. The parser is tolerant: blank lines, `#` comments, and lines\n * without `=` are ignored. Quoted values have matching outer quotes\n * stripped so `KEY=\"value\"` and `KEY=value` round-trip identically.\n */\nexport function readEnvFile(projectPath: string): RushEnv {\n const file = envFilePath(projectPath);\n if (!existsSync(file)) return {};\n\n const raw = readFileSync(file, 'utf8');\n const env: RushEnv = {};\n const extras: Record<string, string> = {};\n\n const known = new Set<keyof RushEnv>([\n 'PROJECT_ID',\n 'PREVIEW_URL',\n 'GIT_REMOTE',\n 'STATE',\n 'DATABASE_URL',\n 'SUPABASE_URL',\n 'SUPABASE_ANON_KEY',\n 'STORAGE_API_BASE',\n ]);\n\n for (const line of raw.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eq = trimmed.indexOf('=');\n if (eq < 0) continue;\n\n const key = trimmed.slice(0, eq).trim();\n let value = trimmed.slice(eq + 1).trim();\n if (\n value.length >= 2 &&\n ((value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\")))\n ) {\n value = value.slice(1, -1);\n }\n if (!key) continue;\n\n if (known.has(key as keyof RushEnv)) {\n // STATE values are validated at the call site; we store whatever\n // the file says so a resume can detect a corrupted checkpoint.\n (env as Record<string, string>)[key] = value;\n } else {\n extras[key] = value;\n }\n }\n\n if (Object.keys(extras).length > 0) env.extras = extras;\n return env;\n}\n\n/**\n * Serialize a `RushEnv` back into `.rush/env.md`. The header warning is\n * always re-prepended — even if a prior edit dropped it — because the\n * no-commit invariant depends on it being visible.\n *\n * The write is not atomic. If the process dies mid-write the user can\n * re-run `task link`, which treats a missing / partial file as a fresh\n * start rather than trying to salvage.\n */\nexport function writeEnvFile(projectPath: string, env: RushEnv): void {\n const dir = path.join(projectPath, '.rush');\n mkdirSync(dir, { recursive: true });\n\n const lines: string[] = [ENV_FILE_HEADER.trimEnd(), ''];\n\n if (env.PROJECT_ID) lines.push(`PROJECT_ID=${env.PROJECT_ID}`);\n if (env.PREVIEW_URL) lines.push(`PREVIEW_URL=${env.PREVIEW_URL}`);\n if (env.GIT_REMOTE) lines.push(`GIT_REMOTE=${env.GIT_REMOTE}`);\n\n lines.push('');\n lines.push('# Checkpoint state (for idempotent resume)');\n lines.push('# Values: pod-only | pod-db | pod-db-oss | complete');\n if (env.STATE) lines.push(`STATE=${env.STATE}`);\n\n const hasDb = env.DATABASE_URL || env.SUPABASE_URL || env.SUPABASE_ANON_KEY;\n if (hasDb) {\n lines.push('');\n lines.push('# DB (optional)');\n if (env.DATABASE_URL) lines.push(`DATABASE_URL=${env.DATABASE_URL}`);\n if (env.SUPABASE_URL) lines.push(`SUPABASE_URL=${env.SUPABASE_URL}`);\n if (env.SUPABASE_ANON_KEY)\n lines.push(`SUPABASE_ANON_KEY=${env.SUPABASE_ANON_KEY}`);\n }\n\n if (env.STORAGE_API_BASE) {\n lines.push('');\n lines.push('# Rush-proxied object storage (optional)');\n lines.push(\n '# Authentication: use RUSH_PLATFORM_TOKEN from your shell environment (existing Rush mechanism).'\n );\n lines.push(\n '# Platform Token MUST remain server-side only — never expose to the browser.'\n );\n lines.push(`STORAGE_API_BASE=${env.STORAGE_API_BASE}`);\n }\n\n if (env.extras) {\n const extraKeys = Object.keys(env.extras);\n if (extraKeys.length > 0) {\n lines.push('');\n lines.push('# Additional (preserved from prior hand-edits)');\n for (const k of extraKeys) {\n lines.push(`${k}=${env.extras[k]}`);\n }\n }\n }\n\n writeFileSync(file(projectPath), `${lines.join('\\n')}\\n`, 'utf8');\n}\n\nfunction file(projectPath: string): string {\n return envFilePath(projectPath);\n}\n\n/**\n * Idempotently add `.rush/` to the project's `.gitignore`. The user's\n * `.rush/env.md` must never be committed (spec §M3 invariant 2), and we\n * cannot rely on the user to add the entry themselves. Matches any of\n * `.rush`, `.rush/`, `/.rush`, `/.rush/` (with or without trailing\n * comment) on a line by itself, so we won't double-append on re-run.\n */\nexport function ensureRushGitignore(projectPath: string): {\n added: boolean;\n path: string;\n} {\n const gi = path.join(projectPath, '.gitignore');\n const existing = existsSync(gi) ? readFileSync(gi, 'utf8') : '';\n\n const alreadyIgnored = existing\n .split(/\\r?\\n/)\n .map((l) => l.replace(/#.*$/, '').trim())\n .some(\n (l) =>\n l === '.rush' || l === '.rush/' || l === '/.rush' || l === '/.rush/'\n );\n\n if (alreadyIgnored) return { added: false, path: gi };\n\n const needsLeadingNewline = existing.length > 0 && !existing.endsWith('\\n');\n const block = `${needsLeadingNewline ? '\\n' : ''}# rush-ai local artifacts — contains credentials, do not commit\\n.rush/\\n`;\n\n writeFileSync(gi, existing + block, 'utf8');\n return { added: true, path: gi };\n}\n\n/** Valid STATE values. Used by callers that need to guard against a\n * corrupted checkpoint read from disk. */\nexport const CHECKPOINT_STATES: readonly CheckpointState[] = [\n 'pod-only',\n 'pod-db',\n 'pod-db-oss',\n 'complete',\n] as const;\n\nexport function isCheckpointState(v: unknown): v is CheckpointState {\n return (\n typeof v === 'string' &&\n (CHECKPOINT_STATES as readonly string[]).includes(v)\n );\n}\n\n/**\n * Write the agent handoff checklist. `task link` produces this so the\n * calling IDE agent has a machine-consumable list of local code edits\n * to perform (change `.env.local`, run migrations, swap upload code).\n * Overwrites on every call — the handoff is a snapshot of the\n * init outcome, not accumulated state.\n */\nexport function writeHandoffFile(projectPath: string, content: string): void {\n const dir = path.join(projectPath, '.rush');\n mkdirSync(dir, { recursive: true });\n writeFileSync(handoffFilePath(projectPath), content, 'utf8');\n}\n","import type { Command } from 'commander';\nimport { resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport {\n type ApiTaskResult,\n type CliEnv,\n resolveWebBuilderTask,\n toDeployVersionEnv,\n toPublishEnv,\n type WebBuilderTask,\n} from '../../util/api-task.js';\nimport { isCIMode } from '../../util/ci.js';\nimport { createClient, type RushClient } from '../../util/client.js';\nimport { getGlobalConfig } from '../../util/config.js';\nimport { ApiError, RushError, TaskFailedError } from '../../util/errors.js';\nimport {\n type PublishPodEvent,\n streamPublishPod,\n} from '../../util/publish-pod-stream.js';\nimport { requireAuth } from '../../util/require-auth.js';\nimport { buildDeployDomain, buildDeployUrl } from '../../util/rush-domain.js';\nimport { registerPushSubcommand } from './push.js';\n\n/**\n * Allowed domain-prefix shape per spec §5. Client-side pre-check: the server\n * runs the same regex, but catching it here avoids a round-trip + lets us\n * give a more pointed error message.\n */\nconst DOMAIN_PREFIX_REGEX = /^[a-z0-9][a-z0-9-]{0,28}[a-z0-9]$|^[a-z0-9]$/;\n\nexport function validateDomainPrefix(prefix: string): void {\n if (!DOMAIN_PREFIX_REGEX.test(prefix)) {\n throw new RushError(\n `Invalid domain prefix: \"${prefix}\". Use 1-30 lowercase letters/digits/hyphens; must start and end with a letter or digit.`,\n { reason: 'invalid_domain_prefix', domain: prefix }\n );\n }\n}\n\ninterface DeployCliOptions {\n // NEW flow\n version?: string;\n env?: CliEnv;\n domain?: string;\n yes?: boolean;\n // Legacy shim (retained so commander does not reject old invocations)\n name?: string;\n path?: string;\n}\n\ninterface VersionListResponse {\n success?: boolean;\n versions?: Array<{\n id: string;\n versionNumber?: number;\n createdAt?: string;\n [key: string]: unknown;\n }>;\n}\n\ninterface BuildCheckResponse {\n exists: boolean;\n commitHash: string;\n shortHash?: string;\n ossBaseUrl?: string;\n message?: string;\n}\n\ninterface HasProductionResponse {\n hasProduction: boolean;\n}\n\ninterface DomainCheckResponse {\n available: boolean;\n conflictProjectId?: string;\n}\n\ninterface PublishResponse {\n success: boolean;\n error?: string;\n message?: string;\n data?: {\n domain?: string;\n url?: string;\n environment?: string;\n [key: string]: unknown;\n };\n}\n\ntype FlowStep =\n | 'resolve'\n | 'version'\n | 'preflight'\n | 'domain'\n | 'confirm'\n | 'publish';\n\n/**\n * Error that carries the pipeline step where the failure happened. CI mode\n * repackages these as `TaskFailedError` with the step in `meta.step` — this\n * is the structured payload spec §9 promises to CI consumers.\n */\nclass DeployStepError extends RushError {\n public step: FlowStep;\n constructor(\n step: FlowStep,\n message: string,\n meta: Record<string, unknown> = {}\n ) {\n super(message, { ...meta, step });\n // Spec §\"Error-code convention\": no new names/codes — keep this\n // observationally indistinguishable from RushError so CLI consumers\n // branch on `meta.step` / `meta.reason`, not on the class identity.\n this.name = 'RushError';\n this.step = step;\n }\n}\n\nexport function registerDeploySubcommand(\n task: Command,\n program: Command\n): void {\n task\n .command('deploy')\n .description(\n 'Deploy a web-builder task to production. Pass <id> to publish a built version; legacy `task deploy` with no id delegates to `task push` for this release only.'\n )\n .argument(\n '[id]',\n 'Task ID to deploy. Omit to invoke the deprecated legacy `task deploy` shim (forwards to `task push`).'\n )\n // NEW flow options (spec §\"Command signatures — task deploy [id]\")\n //\n // No commander-level defaults for `--version` / `--env`: we need to\n // distinguish \"user did not pass the flag\" from \"user passed the default\n // value explicitly\" in the deprecation shim (spec §\"Deprecation shim\":\n // bare `task deploy --env production` must RushError, not delegate). We\n // apply the defaults inside the action instead.\n .option(\n '--version <hash|latest>',\n 'Commit hash to deploy, or \"latest\" (defaults to latest)'\n )\n .option(\n '--env <test|production>',\n 'Target environment (defaults to production). Next.js projects only deploy to production.'\n )\n .option(\n '--domain <prefix>',\n 'Optional custom domain prefix (1-30 lowercase letters/digits/hyphens)'\n )\n .option('--yes', 'Skip the pre-publish confirmation prompt')\n // LEGACY shim options — declared so commander does not reject old calls\n .option('-n, --name <name>', 'Legacy: project name for `task push` flow')\n .option(\n '-p, --path <dir>',\n 'Legacy: project directory for `task push` flow'\n )\n .action(async (id: string | undefined, opts: DeployCliOptions) => {\n const ciMode = isCIMode(program.opts());\n\n // Deprecation shim dispatch (spec §\"Deprecation shim\")\n if (id === undefined) {\n if (hasNewFlowOptions(opts)) {\n throw new RushError(\n '`task deploy` now requires a task id. Example: `rush-ai task deploy <taskId> --domain my-app`.',\n { reason: 'missing_task_id' }\n );\n }\n output.warn(\n '`task deploy` (no id) is deprecated. Use `rush-ai task push` instead. Falling back for this release.'\n );\n await invokeLegacyPushShim(program, {\n name: opts.name,\n path: opts.path,\n });\n return;\n }\n\n if (opts.name !== undefined || opts.path !== undefined) {\n output.warn(\n 'Legacy flags -n/--name and -p/--path are ignored when a task id is provided.'\n );\n }\n\n try {\n await runDeployFlow(program, id, opts);\n } catch (err) {\n if (ciMode && err instanceof DeployStepError) {\n throw new TaskFailedError(err.message, err.meta);\n }\n throw err;\n }\n });\n}\n\nfunction hasNewFlowOptions(opts: DeployCliOptions): boolean {\n // `--version` / `--env` / `--domain` / `--yes` are all optional with no\n // commander-level defaults (see the `.option(...)` calls above), so\n // `undefined` here means \"the user did not pass this flag\". Passing\n // `--version latest` explicitly — even though it equals the internal\n // default — still counts as using the new flow and must RushError per\n // spec §\"Deprecation shim\".\n return (\n opts.version !== undefined ||\n opts.env !== undefined ||\n opts.domain !== undefined ||\n opts.yes === true\n );\n}\n\nasync function invokeLegacyPushShim(\n program: Command,\n opts: { name?: string; path?: string }\n): Promise<void> {\n // Reach into the already-registered `push` subcommand and invoke its action\n // via commander. This keeps the legacy entry point 1:1 with the `task push`\n // behavior without duplicating that logic here.\n const task = program.commands.find((c) => c.name() === 'task');\n if (!task) {\n throw new RushError('Internal: `task` command not registered', {\n reason: 'internal_task_missing',\n });\n }\n const argv = ['node', 'rush-ai', 'task', 'push'];\n if (opts.name) argv.push('--name', opts.name);\n if (opts.path) argv.push('--path', opts.path);\n await program.parseAsync(argv);\n}\n\nasync function runDeployFlow(\n program: Command,\n id: string,\n opts: DeployCliOptions\n): Promise<void> {\n // Step 1: auth\n requireAuth();\n\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n // Step 2: resolve task (web-builder gate via helper)\n let resolved: WebBuilderTask;\n try {\n resolved = await resolveWebBuilderTask(client, id);\n } catch (err) {\n throw toStepError('resolve', err, { taskId: id });\n }\n\n // Step 3: resolve version\n const commitHash = await resolveCommitHash(client, id, opts.version, format);\n\n // Step 4: template-specific preflight\n await preflight(client, id, commitHash, opts.env ?? 'production', resolved);\n\n // Step 5: domain preflight (only when --domain passed)\n if (opts.domain !== undefined) {\n await domainPreflight(client, id, opts.domain);\n }\n\n // Step 6: confirm (skipped in CI / with --yes)\n const confirmed = await confirmPublish({\n format,\n skip: Boolean(opts.yes) || isCIMode(program.opts()),\n env: opts.env ?? 'production',\n commitHash,\n domain: opts.domain,\n isNextjs: resolved.isNextjs,\n });\n if (!confirmed) {\n output.info('Cancelled.');\n return;\n }\n\n // Step 7: publish\n const publishResult = resolved.isNextjs\n ? await publishNextjs(client, id, commitHash, opts.domain, format)\n : await publishStatic(\n client,\n id,\n commitHash,\n opts.env ?? 'production',\n opts.domain\n );\n\n // Step 8: output (step 9 — CI wrapping — is handled by the `action`)\n emitSuccess({\n format,\n projectId: id,\n commitHash,\n env: opts.env ?? 'production',\n domain: publishResult.domain,\n url: publishResult.url,\n template: resolved.template,\n });\n}\n\nfunction toStepError(\n step: FlowStep,\n err: unknown,\n extraMeta: Record<string, unknown>\n): DeployStepError {\n if (err instanceof DeployStepError) return err;\n if (err instanceof RushError) {\n return new DeployStepError(step, err.message, {\n ...err.meta,\n ...extraMeta,\n });\n }\n if (err instanceof Error) {\n return new DeployStepError(step, err.message, extraMeta);\n }\n return new DeployStepError(step, String(err), extraMeta);\n}\n\nasync function resolveCommitHash(\n client: RushClient,\n projectId: string,\n versionOpt: string | undefined,\n format: string\n): Promise<string> {\n const wantsLatest = !versionOpt || versionOpt === 'latest';\n if (!wantsLatest) {\n return versionOpt;\n }\n\n let response: { data: VersionListResponse };\n try {\n response = await client.get<VersionListResponse>(\n `/api/version/list?projectId=${encodeURIComponent(projectId)}`\n );\n } catch (err) {\n throw toStepError('version', err, { taskId: projectId });\n }\n\n const versions = response.data.versions ?? [];\n if (versions.length === 0) {\n throw new DeployStepError(\n 'version',\n 'No versions yet. Send a message to the task first to produce a buildable commit.',\n { taskId: projectId, reason: 'no_versions' }\n );\n }\n const latest = versions[versions.length - 1];\n if (format !== 'json') {\n output.dim(`Using latest version: ${shortHash(latest.id)}`);\n }\n return latest.id;\n}\n\nasync function preflight(\n client: RushClient,\n projectId: string,\n commitHash: string,\n env: CliEnv,\n resolved: WebBuilderTask\n): Promise<void> {\n if (resolved.isNextjs) {\n if (env === 'test') {\n throw new DeployStepError(\n 'preflight',\n 'Next.js projects only deploy to production. Drop `--env test` or deploy a non-Next.js template.',\n { taskId: projectId, reason: 'nextjs_env_test_unsupported' }\n );\n }\n let hasProd: { data: HasProductionResponse };\n try {\n hasProd = await client.get<HasProductionResponse>(\n `/api/projects/${encodeURIComponent(projectId)}/database/has-production`\n );\n } catch (err) {\n throw toStepError('preflight', err, { taskId: projectId });\n }\n if (!hasProd.data.hasProduction) {\n throw new DeployStepError(\n 'preflight',\n 'Production database not created. Open the Web UI to provision it before deploying.',\n { taskId: projectId, reason: 'no_production_db' }\n );\n }\n return;\n }\n\n // Non-nextjs: build artifacts must be present.\n let check: { data: BuildCheckResponse };\n try {\n check = await client.get<BuildCheckResponse>(\n `/api/build/check?projectId=${encodeURIComponent(projectId)}&commitHash=${encodeURIComponent(commitHash)}`\n );\n } catch (err) {\n throw toStepError('preflight', err, { taskId: projectId, commitHash });\n }\n if (!check.data.exists) {\n throw new DeployStepError(\n 'preflight',\n `Build artifacts missing for commit ${shortHash(commitHash)}. Send a new message to rebuild.`,\n { taskId: projectId, commitHash, reason: 'build_missing' }\n );\n }\n}\n\nasync function domainPreflight(\n client: RushClient,\n projectId: string,\n prefix: string\n): Promise<void> {\n try {\n validateDomainPrefix(prefix);\n } catch (err) {\n throw toStepError('domain', err, { taskId: projectId, domain: prefix });\n }\n\n let check: { data: DomainCheckResponse };\n try {\n check = await client.get<DomainCheckResponse>(\n `/api/projects/check-domain-prefix?prefix=${encodeURIComponent(prefix)}&projectId=${encodeURIComponent(projectId)}`\n );\n } catch (err) {\n throw toStepError('domain', err, { taskId: projectId, domain: prefix });\n }\n if (!check.data.available) {\n throw new DeployStepError(\n 'domain',\n `Domain prefix \"${prefix}\" is taken${check.data.conflictProjectId ? ` by project ${check.data.conflictProjectId}` : ''}.`,\n {\n taskId: projectId,\n domain: prefix,\n conflictProjectId: check.data.conflictProjectId,\n reason: 'domain_taken',\n }\n );\n }\n}\n\ninterface ConfirmArgs {\n format: string;\n skip: boolean;\n env: CliEnv;\n commitHash: string;\n domain?: string;\n isNextjs: boolean;\n}\n\nasync function confirmPublish(args: ConfirmArgs): Promise<boolean> {\n if (args.skip) return true;\n if (args.format === 'json') return true; // JSON mode implies non-interactive\n if (!process.stdin.isTTY) return true; // piped stdin: behave like --yes\n\n output.log(`About to deploy:`);\n output.log(` Version: ${shortHash(args.commitHash)}`);\n output.log(` Env: ${args.env}`);\n if (args.domain) output.log(` Domain: ${args.domain}`);\n output.log(\n ` Template: ${args.isNextjs ? 'nextjs-fullstack (pod)' : 'static (OSS)'}`\n );\n process.stderr.write('Proceed? [y/N] ');\n const answer = await readOneLine();\n return answer.trim().toLowerCase().startsWith('y');\n}\n\nfunction readOneLine(): Promise<string> {\n return new Promise((resolve) => {\n let acc = '';\n const onData = (chunk: Buffer) => {\n acc += chunk.toString('utf-8');\n const nlIdx = acc.indexOf('\\n');\n if (nlIdx !== -1) {\n process.stdin.removeListener('data', onData);\n process.stdin.pause();\n resolve(acc.slice(0, nlIdx));\n }\n };\n process.stdin.resume();\n process.stdin.on('data', onData);\n });\n}\n\ninterface PublishOutcome {\n url?: string;\n domain?: string;\n}\n\nasync function publishNextjs(\n client: RushClient,\n projectId: string,\n commitHash: string,\n domain: string | undefined,\n format: string\n): Promise<PublishOutcome> {\n try {\n const result = await streamPublishPod(client, projectId, {\n commitId: commitHash,\n customDomainPrefix: domain,\n onEvent: (event: PublishPodEvent) => {\n if (format === 'json') return;\n if (event.message) {\n output.dim(`[${event.event}] ${event.message}`);\n }\n },\n });\n // Mirror the static-publish logic: when the user passed `--domain`, the\n // CLI owns the URL shape (same rule the web UI follows in its\n // `buildUrl` calls). Next.js pod publishes only target production.\n if (domain !== undefined) {\n const { api: apiBase } = getGlobalConfig();\n return {\n url: buildDeployUrl(domain, 'production', apiBase),\n domain: buildDeployDomain(domain, 'production', apiBase),\n };\n }\n return { url: result.url, domain: result.domain };\n } catch (err) {\n throw toStepError('publish', err, { taskId: projectId, commitHash });\n }\n}\n\nasync function publishStatic(\n client: RushClient,\n projectId: string,\n commitHash: string,\n env: CliEnv,\n domain: string | undefined\n): Promise<PublishOutcome> {\n const publishBody: Record<string, unknown> = {\n commitId: commitHash,\n // Spec §Environment mapping: publish endpoint uses `online` for prod.\n environment: toPublishEnv(env),\n };\n if (domain !== undefined) publishBody.customDomainPrefix = domain;\n\n let publishResp: { data: PublishResponse };\n try {\n publishResp = await client.post<PublishResponse>(\n `/api/projects/${encodeURIComponent(projectId)}/publish`,\n publishBody\n );\n } catch (err) {\n if (err instanceof ApiError && err.status === 409) {\n throw new DeployStepError(\n 'publish',\n err.message ||\n 'Domain prefix was taken during the publish window (TOCTOU).',\n { taskId: projectId, reason: 'publish_conflict', status: 409 }\n );\n }\n throw toStepError('publish', err, { taskId: projectId, commitHash });\n }\n if (!publishResp.data.success) {\n throw new DeployStepError(\n 'publish',\n publishResp.data.error ||\n publishResp.data.message ||\n 'Publish failed with no error message.',\n { taskId: projectId, reason: 'publish_failed' }\n );\n }\n\n // The static publish upstream currently returns the ingress's default\n // hostname (projectId-based) in `data.domain`/`data.url`, even when we\n // passed a `customDomainPrefix`. The web UI works around this by\n // deriving the URL itself via `buildUrl` — we mirror that here so the\n // CLI reports the actually-served URL to the user.\n //\n // Precedence:\n // 1. custom prefix (`--domain`) → `{prefix}[-test].{suffix}`\n // 2. upstream-returned `data.url` / `data.domain` (fallback for\n // non-custom deploys where projectId-default is correct)\n const { api: apiBase } = getGlobalConfig();\n const resolvedDomain =\n domain !== undefined\n ? buildDeployDomain(domain, env, apiBase)\n : publishResp.data.data?.domain;\n const resolvedUrl =\n domain !== undefined\n ? buildDeployUrl(domain, env, apiBase)\n : publishResp.data.data?.url;\n\n // Spec §8: follow up with deploy-version (best-effort — warn on failure).\n const versionId = commitHash;\n const deployBody = {\n versionId,\n // Spec §Environment mapping: deploy-version uses `production` literal.\n environment: toDeployVersionEnv(env),\n deployedUrl: resolvedUrl ?? '',\n ...(domain !== undefined ? { customDomainPrefix: domain } : {}),\n };\n try {\n await client.post(\n `/api/projects/${encodeURIComponent(projectId)}/deploy-version`,\n deployBody\n );\n } catch (err) {\n const details = err instanceof Error ? err.message : String(err);\n output.warn(\n `deploy-version write failed (best-effort; deploy succeeded): ${details}`\n );\n }\n\n return { url: resolvedUrl, domain: resolvedDomain };\n}\n\ninterface EmitArgs {\n format: string;\n projectId: string;\n commitHash: string;\n env: CliEnv;\n domain?: string;\n url?: string;\n template: string;\n}\n\nfunction emitSuccess(args: EmitArgs): void {\n if (args.format === 'json') {\n output.log(\n JSON.stringify(\n {\n projectId: args.projectId,\n version: args.commitHash,\n shortVersion: shortHash(args.commitHash),\n env: args.env,\n domain: args.domain ?? null,\n url: args.url ?? null,\n template: args.template,\n },\n null,\n 2\n )\n );\n return;\n }\n\n output.newline();\n output.success('Deployed!');\n if (args.url) output.log(` URL: ${args.url}`);\n output.log(` Version: ${shortHash(args.commitHash)}`);\n output.log(` Env: ${args.env}`);\n if (args.domain) output.log(` Domain: ${args.domain}`);\n}\n\nfunction shortHash(hash: string): string {\n return hash.length > 8 ? hash.slice(0, 8) : hash;\n}\n\n// Re-export for index.ts — tests that exercise the legacy shim poke at the\n// push registration through the same module graph.\nexport { registerPushSubcommand };\n\n/**\n * Test seam: exposed strictly so integration tests can assert that helper\n * functions from api-task are wired in. Runtime code does not import this.\n * @internal\n */\nexport const __envMappers = {\n toPublishEnv,\n toDeployVersionEnv,\n};\n\n// Keep ApiTaskResult referenced so downstream type narrowing works for\n// consumers that destructure `resolved.task`.\nexport type { ApiTaskResult };\n","import type { Command } from 'commander';\nimport { resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport { resolveWebBuilderTask } from '../../util/api-task.js';\nimport { createClient } from '../../util/client.js';\nimport { getGlobalConfig } from '../../util/config.js';\nimport { RushError } from '../../util/errors.js';\nimport { requireAuth } from '../../util/require-auth.js';\nimport { buildDeployUrl } from '../../util/rush-domain.js';\n\n/**\n * Allowed domain-prefix shape per spec §5 / §\"CLI execution flow — task\n * domain check\": 1-30 lowercase letters/digits/hyphens, starting and\n * ending with a letter or digit. Single-char aliases (`^[a-z0-9]$`) are\n * explicitly allowed.\n *\n * The server runs the same regex — we pre-check client-side to avoid a\n * round-trip and give a more pointed error message.\n */\nconst DOMAIN_PREFIX_REGEX = /^[a-z0-9][a-z0-9-]{0,28}[a-z0-9]$|^[a-z0-9]$/;\n\nexport function validateDomainPrefix(prefix: string): void {\n if (!DOMAIN_PREFIX_REGEX.test(prefix)) {\n throw new RushError(\n `Invalid domain prefix: \"${prefix}\". Use 1-30 lowercase letters/digits/hyphens; must start and end with a letter or digit.`,\n { reason: 'invalid_domain_prefix', domain: prefix }\n );\n }\n}\n\ninterface DomainCheckResponse {\n available: boolean;\n conflictProjectId?: string;\n}\n\ninterface DomainCheckCliOptions {\n task: string;\n}\n\nexport function registerDomainSubcommand(\n task: Command,\n program: Command\n): void {\n const domain = task\n .command('domain')\n .description('Inspect and validate custom domain prefixes for a task');\n\n domain\n .command('check')\n .description(\n 'Validate a domain prefix and confirm availability for a web-builder task'\n )\n .argument(\n '<prefix>',\n 'Domain prefix (1-30 lowercase letters/digits/hyphens)'\n )\n .requiredOption('--task <id>', 'Web-builder task ID to scope the check')\n .action(async (prefix: string, opts: DomainCheckCliOptions) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n // Step 2: client-side regex pre-check (spec §5 / §\"task domain check\" step 2)\n validateDomainPrefix(prefix);\n\n // Step 3: web-builder gating via the shared helper\n await resolveWebBuilderTask(client, opts.task);\n\n // Step 4: upstream availability check\n const { data } = await client.get<DomainCheckResponse>(\n `/api/projects/check-domain-prefix?prefix=${encodeURIComponent(\n prefix\n )}&projectId=${encodeURIComponent(opts.task)}`\n );\n\n // Step 5: emit\n if (format === 'json') {\n output.log(\n JSON.stringify(\n {\n prefix,\n taskId: opts.task,\n available: data.available,\n conflictProjectId: data.conflictProjectId ?? null,\n },\n null,\n 2\n )\n );\n if (!data.available) {\n // Non-fatal in --json mode: consumer parses the payload. But the\n // exit-code contract (RushError = exit 2) must still hold so CI\n // wrappers can detect the conflict branch.\n throw new RushError(\n `Domain prefix \"${prefix}\" is taken${\n data.conflictProjectId\n ? ` by project ${data.conflictProjectId}`\n : ''\n }.`,\n {\n reason: 'domain_taken',\n domain: prefix,\n conflictProjectId: data.conflictProjectId,\n }\n );\n }\n return;\n }\n\n if (data.available) {\n // Derive the actual deploy URL from the configured API base — the\n // availability endpoint itself doesn't return a suffix, but we know\n // it from the profile's `api` (config.ts) by the same host→suffix\n // rule the web UI's `buildUrl` uses. This prints a clickable URL\n // instead of a literal `<suffix>` placeholder.\n const { api } = getGlobalConfig();\n const url = buildDeployUrl(prefix, 'production', api);\n output.success(`Prefix available: ${url}`);\n } else {\n throw new RushError(\n `Domain prefix \"${prefix}\" is taken${\n data.conflictProjectId\n ? ` by project ${data.conflictProjectId}`\n : ''\n }.`,\n {\n reason: 'domain_taken',\n domain: prefix,\n conflictProjectId: data.conflictProjectId,\n }\n );\n }\n });\n}\n\n// Test seam so unit tests can exercise the regex constant directly without\n// round-tripping through commander.\nexport { DOMAIN_PREFIX_REGEX };\n","import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport type { Command } from 'commander';\nimport { resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport { isCIMode } from '../../util/ci.js';\nimport { createClient } from '../../util/client.js';\nimport {\n ensureRushGitignore,\n isCheckpointState,\n type RushEnv,\n readEnvFile,\n writeEnvFile,\n writeHandoffFile,\n} from '../../util/env-file.js';\nimport { RushError } from '../../util/errors.js';\nimport { requireAuth } from '../../util/require-auth.js';\n\n/**\n * `task link` provisions the remote pieces that a local project needs to\n * run on Rush (pod + optional Supabase DB + optional Rush-proxied object\n * storage) and writes everything the calling IDE agent needs to finish\n * wiring up locally into `.rush/env.md` + `.rush/agent-handoff.md`.\n *\n * The command is non-atomic and **resumable**: each successful remote\n * call flips a STATE checkpoint in `.rush/env.md` so a re-run after a\n * failure picks up from the last good point rather than re-provisioning\n * resources that already exist.\n *\n * Contract surface (see `specs/rush-v2/cli/roadmap.spec.md` §M3 / §M4,\n * and `specs/rush-v2/cli/task-storage-api.spec.md`):\n * - Never mutates user code; artifacts live in `.rush/` only.\n * - Never writes logs, error stacks, or telemetry into `.rush/env.md`.\n * - Auto-appends `.rush/` to `.gitignore` (idempotent).\n * - The --oss flag records a `STORAGE_API_BASE` pointing at the\n * Rush-proxied object storage API. Authentication reuses the existing\n * `RUSH_PLATFORM_TOKEN` env var (server-side only); no per-project\n * token is minted and `.rush/env.md` never stores a bearer secret.\n *\n * History: renamed from `task init` in 0.10.0 (issue #1150). `init`\n * remains a commander alias until 0.13.0; invoking via the alias prints a\n * one-time deprecation warning to stderr, suppressed in JSON / CI mode so\n * machine-readable pipes stay clean.\n */\n\n/**\n * Detect whether the user invoked this command via the deprecated `init`\n * alias rather than the canonical `link` name.\n *\n * Primary signal: the `task` command's `args` (commander populates\n * `command.args` with the operands + unknown tokens). The first non-flag\n * token is the subcommand name the user typed (pre-resolution).\n *\n * Fallback signal: scan `rawArgv` for every `'task'` occurrence and\n * inspect the next non-flag token. We iterate candidates because a\n * global option value could happen to equal `'task'` (e.g. an upstream\n * `--profile task`); the real subcommand is the `task` followed by\n * `init` or `link`. The fallback guards against commander internals\n * differing across versions / environments (observed CI vs local\n * divergence where `parent.args` came in empty under concurrent test\n * load, losing the `init` token before our action ran).\n *\n * We cannot rely on `thisCommand.name()` (commander reports the\n * canonical name regardless of invocation) nor on a hypothetical \"used\n * alias\" API — it does not exist in commander 13.\n *\n * `rawArgv` defaults to an empty array so unit tests can assert the\n * primary-signal behavior without the test process's argv leaking in.\n * Production call sites pass `process.argv` explicitly.\n */\nexport function invokedViaInitAlias(\n parentArgs: readonly string[],\n rawArgv: readonly string[] = []\n): boolean {\n // Primary: parent.args first non-flag token.\n for (const token of parentArgs) {\n if (!token || token.startsWith('-')) continue;\n if (token === 'init') return true;\n if (token === 'link') return false;\n // Any other first operand is unexpected — fall through to argv scan\n // rather than guessing.\n break;\n }\n\n // Fallback: find every `task` in argv and inspect the next non-flag\n // token. A hit on `init` or `link` wins; anything else keeps scanning\n // (handles `--some-option task` value collisions).\n for (let i = 0; i < rawArgv.length; i += 1) {\n if (rawArgv[i] !== 'task') continue;\n for (let j = i + 1; j < rawArgv.length; j += 1) {\n const token = rawArgv[j];\n if (!token || token.startsWith('-')) continue;\n if (token === 'init') return true;\n if (token === 'link') return false;\n // Some other operand — likely `--profile task othervalue`; keep\n // looking for the real `task init|link` pair.\n break;\n }\n }\n return false;\n}\n\nconst DEPRECATION_WARNING =\n '`task init` is renamed to `task link`. Support for `init` will be removed in 0.13.0.';\n\ninterface TaskDeployResponse {\n id: string;\n status: string;\n gitPushUrl: string;\n chatUrl: string;\n previewUrl: string | null;\n}\n\ninterface DatabaseCreateResponse {\n success: boolean;\n action?: string;\n databaseKey?: string;\n supabaseUrl?: string;\n postgresUrl?: string;\n message?: string;\n error?: string;\n}\n\ninterface DetectedNeeds {\n db: {\n hasEnvLocal: boolean;\n hasSupabaseMigrations: boolean;\n suggestion: boolean;\n };\n oss: {\n publicDirSizeKB: number;\n largeAssetCount: number;\n suggestion: boolean;\n };\n}\n\nconst PUBLIC_ASSET_THRESHOLD_KB = 50; // per-file threshold; >50KB counts as a candidate\nconst PUBLIC_DIR_OSS_HINT_KB = 500; // total public/ size that tips us toward suggesting OSS\n\ninterface LinkCliOptions {\n db?: boolean;\n oss?: boolean;\n name?: string;\n path: string;\n yes?: boolean;\n json?: boolean;\n}\n\nexport function registerLinkSubcommand(task: Command, program: Command): void {\n task\n .command('link')\n // Deprecated alias — to be removed in 0.13.0. See `invokedViaInitAlias`\n // above for the runtime warning path; commander itself treats the alias\n // as fully transparent (same help, same flags, same action).\n .alias('init')\n .description(\n 'Link a local codebase to Rush (pod + optional DB + optional OSS token) and produce .rush/env.md + .rush/agent-handoff.md'\n )\n .option('--db', 'Create a Supabase database for this project')\n .option('--oss', 'Issue an OSS upload token for this project')\n .option('-n, --name <slug>', 'Project name (defaults to directory name)')\n .option('-p, --path <dir>', 'Project directory', '.')\n .option('-y, --yes', 'Skip interactive prompts; accept detected defaults')\n .action(async (opts: LinkCliOptions, thisCommand: Command) => {\n const format = resolveFormat(program.opts());\n\n // Deprecation warning for users still invoking `task init`. Printed\n // BEFORE `requireAuth` so users see it even when the auth check\n // short-circuits — otherwise an unauthenticated user on the alias\n // path would never learn the name is changing. Gated on JSON / CI\n // mode so machine-readable pipes stay clean (the warning is\n // advisory, not a behavioral change). Stderr-only via `output.warn`.\n const parentArgs = thisCommand.parent?.args ?? [];\n // Commander's top-level `program.rawArgs` preserves the literal\n // argv that was passed to `parseAsync`, even in environments where\n // `command.parent.args` is trimmed by the time the action runs\n // (observed in CI under concurrent turbo+vitest workers). Using it\n // as the fallback instead of `process.argv` makes the signal\n // immune to test-worker argv interference.\n const rawArgv = (program as unknown as { rawArgs?: string[] }).rawArgs;\n if (\n invokedViaInitAlias(parentArgs, rawArgv ?? []) &&\n format !== 'json' &&\n !isCIMode(program.opts())\n ) {\n output.warn(DEPRECATION_WARNING);\n }\n\n requireAuth();\n const projectPath = path.resolve(opts.path);\n\n // Step 0: Read any existing `.rush/env.md`. A partially-provisioned\n // project (crashed mid-run) re-enters through this path.\n const existing = readEnvFile(projectPath);\n const resumed = Boolean(existing.PROJECT_ID);\n if (resumed && existing.STATE && !isCheckpointState(existing.STATE)) {\n throw new RushError(\n `.rush/env.md has an invalid STATE='${existing.STATE}'. Delete the file to start fresh or fix it manually.`,\n { state: existing.STATE }\n );\n }\n\n const detected = detectNeeds(projectPath);\n const { wantDb, wantOss } = resolveWants(\n opts,\n detected,\n !!opts.json || format === 'json'\n );\n\n if (format !== 'json') {\n if (resumed) {\n output.info(\n `Resuming \\`task link\\` for project ${existing.PROJECT_ID} (state=${existing.STATE ?? 'unknown'})`\n );\n } else {\n output.info('Provisioning Rush project...');\n }\n }\n\n // Snapshot of what we'll do; written back to env.md as checkpoints advance.\n const env: RushEnv = { ...existing };\n\n // Step 1 — Pod. If already provisioned (STATE >= pod-only), skip.\n if (!env.PROJECT_ID) {\n const projectName =\n opts.name ??\n path.basename(projectPath) ??\n `rush-${Date.now().toString(36)}`;\n const client = createClient();\n const { data: created } = await client.post<TaskDeployResponse>(\n '/api/tasks/deploy',\n { name: projectName }\n );\n if (!created.id || !created.gitPushUrl) {\n throw new RushError(\n 'Rush API did not return a project id / git push URL; cannot continue.'\n );\n }\n\n env.PROJECT_ID = created.id;\n env.PREVIEW_URL = created.previewUrl ?? undefined;\n // Persist the unmasked git push URL so `task push` can reuse the\n // project without re-minting a deploy token. `.rush/env.md` is\n // gitignored and DO-NOT-commit-branded; the file is the sole\n // on-disk store of runtime credentials (DB URL, OSS token, etc.)\n // and the embedded git token is no different. JSON output +\n // terminal display still mask it via `maskToken` below.\n env.GIT_REMOTE = created.gitPushUrl;\n env.STATE = 'pod-only';\n writeEnvFile(projectPath, env);\n ensureRushGitignore(projectPath);\n if (format !== 'json') output.success(`Project created: ${created.id}`);\n }\n\n // After step 1 we must have a projectId — either from resume or from\n // the just-completed create. The explicit guard narrows the type and\n // keeps TypeScript (and biome's noNonNullAssertion rule) happy for\n // the rest of the function without scattering `!` everywhere.\n if (!env.PROJECT_ID) {\n throw new RushError(\n 'Internal error: PROJECT_ID unset after provisioning step.'\n );\n }\n const projectId = env.PROJECT_ID;\n\n // Step 2 — DB (optional). The DB endpoint is idempotent on the pod\n // side (workflow short-circuits if a DB already exists), so re-running\n // after a pod-only checkpoint is safe. We gate on \"user asked for DB\n // AND we haven't stored DB creds yet\" rather than on STATE — STATE\n // can be `complete` from a prior pod-only run, and we want\n // `task link --db` to still provision the DB in that case.\n const alreadyHasDbCreds = Boolean(env.DATABASE_URL || env.SUPABASE_URL);\n if (wantDb && !alreadyHasDbCreds) {\n if (format !== 'json') output.info('Creating Supabase database...');\n const client = createClient();\n const { data: db } = await client.post<DatabaseCreateResponse>(\n `/api/projects/${encodeURIComponent(projectId)}/database/create`,\n {}\n );\n if (!db.success && db.action !== 'skipped') {\n throw new RushError(\n `Database create failed: ${db.error ?? db.message ?? 'unknown'}`,\n { projectId: env.PROJECT_ID }\n );\n }\n if (db.postgresUrl) env.DATABASE_URL = db.postgresUrl;\n if (db.supabaseUrl) env.SUPABASE_URL = db.supabaseUrl;\n env.STATE = 'pod-db';\n writeEnvFile(projectPath, env);\n if (format !== 'json') output.success('Database ready');\n } else if (!wantDb && env.STATE === 'pod-only') {\n // User didn't ask for DB → advance the checkpoint past the DB stage\n // so OSS provisioning (if requested) can run next time without\n // re-entering the DB branch.\n env.STATE = 'pod-db';\n writeEnvFile(projectPath, env);\n }\n\n // Step 3 — Rush-proxied storage (optional). No server round-trip: the\n // storage API is unconditionally available for any existing task, and\n // authentication reuses the existing Platform Token. We just record\n // the endpoint in env.md so the IDE agent can point the app at it.\n if (wantOss && !env.STORAGE_API_BASE) {\n env.STORAGE_API_BASE = storageApiBaseForProject(projectId);\n env.STATE = 'pod-db-oss';\n writeEnvFile(projectPath, env);\n if (format !== 'json') output.success('Storage API endpoint recorded');\n } else if (!wantOss && env.STATE === 'pod-db') {\n env.STATE = 'pod-db-oss';\n writeEnvFile(projectPath, env);\n }\n\n // Step 4 — complete\n env.STATE = 'complete';\n writeEnvFile(projectPath, env);\n ensureRushGitignore(projectPath);\n\n // Step 5 — handoff for the IDE agent\n writeHandoffFile(\n projectPath,\n renderHandoff({\n projectId: projectId,\n previewUrl: env.PREVIEW_URL,\n db: wantDb\n ? {\n databaseUrl: env.DATABASE_URL,\n supabaseUrl: env.SUPABASE_URL,\n }\n : null,\n storage: wantOss\n ? {\n storageApiBase:\n env.STORAGE_API_BASE ?? storageApiBaseForProject(projectId),\n }\n : null,\n detected,\n })\n );\n\n const result = {\n projectId: projectId,\n previewUrl: env.PREVIEW_URL ?? null,\n state: env.STATE,\n db: wantDb\n ? {\n databaseUrl: env.DATABASE_URL ?? null,\n supabaseUrl: env.SUPABASE_URL ?? null,\n }\n : null,\n storage: wantOss\n ? {\n storageApiBase:\n env.STORAGE_API_BASE ?? storageApiBaseForProject(projectId),\n }\n : null,\n files: {\n env: path.join(projectPath, '.rush', 'env.md'),\n handoff: path.join(projectPath, '.rush', 'agent-handoff.md'),\n },\n };\n\n if (format === 'json') {\n output.log(JSON.stringify(result, null, 2));\n return;\n }\n\n output.newline();\n output.success('Link complete');\n output.log(` ${chalk.bold('Project ID:')} ${result.projectId}`);\n if (result.previewUrl) {\n output.log(` ${chalk.bold('Preview:')} ${result.previewUrl}`);\n }\n output.log(` ${chalk.bold('Env file:')} ${result.files.env}`);\n output.log(` ${chalk.bold('Handoff:')} ${result.files.handoff}`);\n output.newline();\n output.dim(\n 'Next: read .rush/agent-handoff.md and apply the local changes it lists.'\n );\n });\n}\n\n/** Decide what to provision given CLI flags + detection results. In\n * non-interactive mode (JSON / no TTY / --yes) explicit flags win and\n * detection is only used to populate the handoff. Interactive sessions\n * default to detection-driven but do not currently prompt via readline\n * — we favor explicit flags to keep the command reproducible. */\nexport function resolveWants(\n opts: LinkCliOptions,\n detected: DetectedNeeds,\n jsonMode: boolean\n): { wantDb: boolean; wantOss: boolean } {\n // Explicit flags always win.\n if (opts.db !== undefined || opts.oss !== undefined) {\n return {\n wantDb: !!opts.db,\n wantOss: !!opts.oss,\n };\n }\n // Non-interactive: don't guess. Be conservative — provision only the\n // pod so re-run with an explicit flag is predictable.\n if (jsonMode || !process.stdout.isTTY || opts.yes) {\n return { wantDb: false, wantOss: false };\n }\n // Interactive fallback without readline: surface detection hints and\n // let the user re-run with flags. This is the safest default; we can\n // wire up a proper prompt in a follow-up.\n if (detected.db.suggestion || detected.oss.suggestion) {\n output.newline();\n output.info('Detection hints:');\n if (detected.db.suggestion) {\n output.log(\n ' • .env / supabase/migrations found — consider `--db` to provision a Supabase database.'\n );\n }\n if (detected.oss.suggestion) {\n output.log(\n ` • public/ has ${detected.oss.largeAssetCount} asset(s) (${detected.oss.publicDirSizeKB} KB total) — consider \\`--oss\\` to move them behind signed uploads.`\n );\n }\n output.dim(\n 'Re-run `rush-ai task link` with `--db` / `--oss` to include those steps.'\n );\n output.newline();\n }\n return { wantDb: false, wantOss: false };\n}\n\nexport function detectNeeds(projectPath: string): DetectedNeeds {\n // DB signals: `.env.local` / `.env` with DATABASE_URL or SUPABASE vars,\n // or a `supabase/migrations/` directory.\n const envFiles = ['.env.local', '.env', '.env.development'];\n let hasEnvLocal = false;\n for (const f of envFiles) {\n const p = path.join(projectPath, f);\n if (!existsSync(p)) continue;\n try {\n const content = readFileSync(p, 'utf8');\n if (\n /\\b(DATABASE_URL|SUPABASE_URL|POSTGRES_URL|USER_POSTGRESQL_URL)\\s*=/.test(\n content\n )\n ) {\n hasEnvLocal = true;\n break;\n }\n } catch {\n // unreadable — ignore\n }\n }\n const hasSupabaseMigrations = existsSync(\n path.join(projectPath, 'supabase', 'migrations')\n );\n\n // OSS signals: large / many files in public/.\n const publicDir = path.join(projectPath, 'public');\n let publicDirSizeKB = 0;\n let largeAssetCount = 0;\n if (existsSync(publicDir)) {\n try {\n for (const entry of walk(publicDir, 3)) {\n const st = statSync(entry);\n if (!st.isFile()) continue;\n const kb = Math.round(st.size / 1024);\n publicDirSizeKB += kb;\n if (kb > PUBLIC_ASSET_THRESHOLD_KB) largeAssetCount += 1;\n }\n } catch {\n // permission problem — treat as no signal\n }\n }\n\n return {\n db: {\n hasEnvLocal,\n hasSupabaseMigrations,\n suggestion: hasEnvLocal || hasSupabaseMigrations,\n },\n oss: {\n publicDirSizeKB,\n largeAssetCount,\n suggestion:\n publicDirSizeKB >= PUBLIC_DIR_OSS_HINT_KB || largeAssetCount >= 3,\n },\n };\n}\n\nfunction* walk(dir: string, depth: number): Generator<string> {\n if (depth < 0) return;\n let entries: string[] = [];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const name of entries) {\n const p = path.join(dir, name);\n let st: ReturnType<typeof statSync>;\n try {\n st = statSync(p);\n } catch {\n continue;\n }\n if (st.isDirectory()) {\n yield* walk(p, depth - 1);\n } else {\n yield p;\n }\n }\n}\n\ninterface HandoffData {\n projectId: string;\n previewUrl?: string;\n db: { databaseUrl?: string; supabaseUrl?: string } | null;\n storage: {\n storageApiBase: string;\n } | null;\n detected: DetectedNeeds;\n}\n\n/** Storage API base URL for the given taskId. Kept centralized so tests and\n * handoff rendering agree on a single format. Host defaults to the shared Rush\n * host; future work can override via a client config if we ever need to point\n * at a staging deployment. */\nexport function storageApiBaseForProject(taskId: string): string {\n const host = 'https://rush.zhenguanyu.com';\n return `${host}/api/tasks/${encodeURIComponent(taskId)}/storage`;\n}\n\nexport function renderHandoff(data: HandoffData): string {\n const lines: string[] = [];\n lines.push('# Rush Agent Handoff');\n lines.push('');\n lines.push(\n `This file lists the local code changes needed to finish wiring up project \\`${data.projectId}\\` to the Rush-provisioned backend. Apply them in order. When you're done, run \\`rush-ai task push\\` to validate.`\n );\n lines.push('');\n lines.push(\n '> Credentials needed below live in `.rush/env.md` (never commit).'\n );\n lines.push('');\n lines.push('## Project');\n lines.push('');\n lines.push(`- Project ID: \\`${data.projectId}\\``);\n if (data.previewUrl) lines.push(`- Preview URL: ${data.previewUrl}`);\n lines.push('');\n\n lines.push('## Checklist');\n lines.push('');\n\n if (data.db) {\n lines.push('### Database');\n lines.push('');\n lines.push(\n '- [ ] Point the app at the new DB: replace `DATABASE_URL` / `SUPABASE_URL` in `.env.local` with the values from `.rush/env.md`.'\n );\n if (data.detected.db.hasSupabaseMigrations) {\n lines.push(\n '- [ ] Run `supabase/migrations/*.sql` against the new database (or `supabase db push` if the CLI is wired up).'\n );\n } else {\n lines.push(\n '- [ ] Create the schema you need — no migrations were detected in `supabase/migrations/`.'\n );\n }\n lines.push(\n '- [ ] (Optional) Migrate any existing local data to the new database.'\n );\n lines.push('');\n }\n\n if (data.storage) {\n lines.push('### File uploads (Rush-proxied object storage)');\n lines.push('');\n lines.push(\n `- [ ] Route uploads through the Rush storage proxy at \\`${data.storage.storageApiBase}\\` (\\`POST /upload\\`, \\`GET /object/<key>\\`, \\`DELETE /object/<key>\\`, \\`GET /list\\`).`\n );\n lines.push(\n '- [ ] **Double-hop pattern** (required): browser → user-app server route → rush-app storage API. Only the user-app server holds `RUSH_PLATFORM_TOKEN`; the browser must never carry it.'\n );\n lines.push('');\n lines.push(' Example (Next.js server route inside your app):');\n lines.push('');\n lines.push(' ```ts');\n lines.push(' // app/api/upload/route.ts');\n lines.push(' export async function POST(req: Request) {');\n lines.push(' const formData = await req.formData();');\n lines.push(\n ' const rushRes = await fetch(`${process.env.STORAGE_API_BASE}/upload`, {'\n );\n lines.push(' method: \"POST\",');\n lines.push(\n ' headers: { Authorization: `Bearer ${process.env.RUSH_PLATFORM_TOKEN}` },'\n );\n lines.push(' body: formData,');\n lines.push(' });');\n lines.push(' return Response.json(await rushRes.json());');\n lines.push(' }');\n lines.push(' ```');\n lines.push('');\n lines.push(\n '- [ ] Display uploaded files directly via `<img src={url} />` — the returned URL is already the Rush-hosted proxy endpoint.'\n );\n if (data.detected.oss.suggestion) {\n lines.push(\n `- [ ] (Optional) Move the ${data.detected.oss.largeAssetCount} large asset(s) under \\`public/\\` into the storage proxy so they load from the Rush-hosted endpoint.`\n );\n }\n lines.push('');\n }\n\n lines.push('### Verify');\n lines.push('');\n lines.push(\n '- [ ] `rush-ai task push` — the CLI will reuse `PROJECT_ID` from `.rush/env.md`, skip pod provisioning, and push the edited code.'\n );\n lines.push(\n '- [ ] Open the preview URL and exercise DB / upload paths end-to-end.'\n );\n lines.push('');\n\n lines.push('## Constraints');\n lines.push('');\n lines.push(\n '- `.rush/env.md` must never be committed. `.rush/` is in `.gitignore`.'\n );\n lines.push(\n '- Pushed projects use `template = null` and **cannot** go through `rush-ai task deploy` / custom domains. The preview URL is all you get.'\n );\n if (data.storage) {\n lines.push(\n '- Platform Token (`RUSH_PLATFORM_TOKEN`) is server-side only. Never expose it to the browser or ship it in a public bundle.'\n );\n lines.push(\n '- Uploaded objects are publicly readable via their storage URL; do not upload secrets / private data through the proxy.'\n );\n }\n lines.push('');\n\n return lines.join('\\n');\n}\n","import type { Command } from 'commander';\nimport { formatOutput, resolveFormat } from '../../output/format.js';\nimport { output } from '../../output/logger.js';\nimport { resolveWebBuilderTask } from '../../util/api-task.js';\nimport { createClient } from '../../util/client.js';\nimport { RushError } from '../../util/errors.js';\nimport { requireAuth } from '../../util/require-auth.js';\n\n/**\n * Single entry in `GET /api/version/list?projectId=<id>`.\n *\n * `deployedToTest` / `deployedToProduction` are **authoritative** — spec\n * §\"Version-list derived fields\" explicitly forbids computing these by\n * comparing against `task.deployed_*_version_id`, because the task API\n * only exposes `deployedProductionUrl` for web-builder tasks and can\n * lag the version list. This also keeps `versions` untouched by\n * deploy-version write failures (deploy-version is best-effort — see\n * spec §8).\n */\ninterface VersionEntry {\n id: string;\n versionNumber?: number | string;\n /**\n * ISO 8601 timestamp of the commit. The API returns this as `timestamp`\n * (see `packages/shared/src/types/version.ts` — `Version.timestamp`).\n * We also accept `createdAt` as a fallback for future API revisions or\n * self-hosted variants.\n */\n timestamp?: string;\n createdAt?: string;\n deployedToTest?: boolean;\n deployedToProduction?: boolean;\n [key: string]: unknown;\n}\n\ninterface VersionListResponse {\n success?: boolean;\n versions?: VersionEntry[];\n}\n\ninterface VersionsCliOptions {\n last?: string;\n}\n\nconst MARK_YES = '✓';\nconst MARK_NO = '';\n\nexport function registerVersionsSubcommand(\n task: Command,\n program: Command\n): void {\n task\n .command('versions')\n .description('List buildable versions for a web-builder task')\n .argument('<id>', 'Task ID')\n .option(\n '--last <n>',\n 'Show only the last N versions (after sort, preserving order)'\n )\n .action(async (id: string, opts: VersionsCliOptions) => {\n requireAuth();\n const format = resolveFormat(program.opts());\n const client = createClient();\n\n // Web-builder gate — same helper as `task deploy` / `task domain check`.\n await resolveWebBuilderTask(client, id);\n\n const { data } = await client.get<VersionListResponse>(\n `/api/version/list?projectId=${encodeURIComponent(id)}`\n );\n\n const versions = data.versions ?? [];\n const clamped = clampToLast(versions, opts.last);\n\n if (format === 'json') {\n output.log(JSON.stringify(clamped, null, 2));\n return;\n }\n\n if (clamped.length === 0) {\n output.info(\n 'No versions yet. Send a message to the task to produce a buildable commit.'\n );\n return;\n }\n\n const rows = clamped.map((v, idx) => ({\n '#': String(idx + 1),\n Version: v.versionNumber != null ? String(v.versionNumber) : '',\n Commit: shortHash(v.id),\n Created: formatCreatedAt(v.timestamp ?? v.createdAt),\n Test: v.deployedToTest ? MARK_YES : MARK_NO,\n Production: v.deployedToProduction ? MARK_YES : MARK_NO,\n }));\n\n output.log(formatOutput(rows, format));\n });\n}\n\n/**\n * Trim to the trailing N entries. Preserves the list's original order\n * (the API returns oldest→newest; `--last` just slices the tail).\n *\n * Spec §\"CLI execution flow — task versions <id>\" step 5: \"truncates to\n * last n entries (after sort, preserving order)\".\n */\nexport function clampToLast<T>(\n items: readonly T[],\n lastOpt: string | undefined\n): T[] {\n if (lastOpt === undefined) return [...items];\n const n = Number.parseInt(lastOpt, 10);\n if (!Number.isFinite(n) || n <= 0) {\n throw new RushError(\n `Invalid --last value: \"${lastOpt}\" (expected positive integer).`,\n { reason: 'invalid_last', last: lastOpt }\n );\n }\n if (n >= items.length) return [...items];\n return items.slice(items.length - n);\n}\n\nfunction shortHash(hash: string): string {\n if (!hash) return '';\n return hash.length > 8 ? hash.slice(0, 8) : hash;\n}\n\nfunction formatCreatedAt(ts: string | undefined): string {\n if (!ts) return '';\n const date = new Date(ts);\n if (Number.isNaN(date.getTime())) return ts;\n return date.toLocaleString();\n}\n","import type { Command } from 'commander';\nimport { registerAgentCommand } from './agent/index.js';\nimport { registerAuthCommand } from './auth/index.js';\nimport { registerCheckCommand } from './check/index.js';\nimport { registerCompletionCommand } from './completion/index.js';\nimport { registerConfigCommand } from './config/index.js';\nimport { registerDoctorCommand } from './doctor/index.js';\nimport { registerMarketplaceCommand } from './marketplace/index.js';\nimport { registerMcpCommand } from './mcp/index.js';\nimport { registerPluginCommand } from './plugin/index.js';\nimport { registerSkillCommand } from './skill/index.js';\nimport { registerTaskCommand } from './task/index.js';\n\nexport function registerCommands(program: Command): void {\n registerAuthCommand(program);\n registerAgentCommand(program);\n registerTaskCommand(program);\n registerSkillCommand(program);\n registerCheckCommand(program);\n registerMcpCommand(program);\n registerMarketplaceCommand(program);\n registerPluginCommand(program);\n registerCompletionCommand(program);\n registerConfigCommand(program);\n registerDoctorCommand(program);\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { output } from '../output/logger.js';\n\nconst CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;\nconst FETCH_TIMEOUT_MS = 3000;\nconst REGISTRY_URL = 'https://registry.npmjs.org/rush-ai/latest';\n\nfunction parseVersion(v: string): number[] | null {\n const segments = v.replace(/^v/, '').split('-')[0].split('.').map(Number);\n if (segments.some((n) => Number.isNaN(n))) return null;\n return segments;\n}\n\nfunction isNewerVersion(current: string, latest: string): boolean {\n const c = parseVersion(current);\n const l = parseVersion(latest);\n if (!c || !l) return false;\n for (let i = 0; i < Math.max(c.length, l.length); i++) {\n const cv = c[i] ?? 0;\n const lv = l[i] ?? 0;\n if (lv > cv) return true;\n if (lv < cv) return false;\n }\n return false;\n}\n\nasync function writeLastCheck(checkFile: string): Promise<void> {\n try {\n await mkdir(dirname(checkFile), { recursive: true });\n await writeFile(checkFile, JSON.stringify({ lastCheck: Date.now() }));\n } catch {\n // Truly silent — filesystem may not be writable\n }\n}\n\nexport async function checkForUpdate(currentVersion: string): Promise<void> {\n const rushDir = join(homedir(), '.rush');\n const checkFile = join(rushDir, 'update-check.json');\n\n try {\n if (process.env.CI) return;\n\n let lastCheck = 0;\n try {\n const data = JSON.parse(await readFile(checkFile, 'utf-8'));\n lastCheck = data.lastCheck ?? 0;\n } catch {\n // Missing or corrupt — will check\n }\n\n if (Date.now() - lastCheck < CHECK_INTERVAL_MS) return;\n\n // Write lastCheck immediately to prevent retry storms on failure\n await writeLastCheck(checkFile);\n\n const response = await fetch(REGISTRY_URL, {\n signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),\n });\n if (!response.ok) return;\n\n const { version: latest } = (await response.json()) as {\n version: string;\n };\n\n if (isNewerVersion(currentVersion, latest)) {\n output.newline();\n output.warn(\n `Update available: ${currentVersion} → ${latest}\\n` +\n ` Run \\`npm update -g rush-ai\\` to update.`\n );\n }\n } catch {\n // Silent — never block CLI usage.\n // lastCheck was already written above, so next invocation won't retry.\n }\n}\n"],"mappings":"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","names":["chalk","page","data","totalPages","truncate","resolve","chalk","existsSync","readFileSync","join","execFileSync","existsSync","readFileSync","join","existsSync","join","execFileSync","join","existsSync","readFileSync","unignored","existsSync","join","existsSync","readFileSync","join","existsSync","readFileSync","join","VITE_CONFIG_FILES","file","devScript","chalk","existsSync","join","existsSync","join","chalk","existsSync","dirExists","existsSync","execFileSync","detectShell","execFileSync","existsSync","readFileSync","homedir","resolve","resolve","homedir","existsSync","readFileSync","chalk","displayResults","fsConstants","access","mkdir","readFile","rm","writeFile","homedir","dirname","resolve","resolve","readFile","resolve","path","resolve","readFile","path","resolve","fsConstants","access","resolve","resolve","access","fsConstants","resolve","homedir","mkdir","dirname","pathExists","rm","readFile","access","fsConstants","writeFile","randomUUID","fsConstants","access","mkdir","readFile","rename","rm","writeFile","homedir","dirname","resolve","pathExists","stats","runUpdate","truncate","randomUUID","mkdir","readdir","readFile","rename","rm","stat","homedir","dirname","join","resolve","execFileSync","isAbsolute","resolve","homedir","mkdir","resolve","join","rm","dirname","randomUUID","rename","path","readFile","stat","readdir","fsConstants","access","mkdir","readdir","readFile","rename","rm","stat","writeFile","homedir","basename","dirname","resolve","isAbsolute","RUSH_AI_PLUGIN_NAME","RUSH_AI_MARKETPLACE_NAME","RUSH_MCP_SERVER_KEY","randomUUID","fsConstants","access","copyFile","mkdir","readFile","rename","rm","stat","writeFile","dirname","pathExists","atomicWrite","homedir","pathExists","parsePluginKey","rm","mkdir","resolve","readFile","dirname","basename","rename","file","readdir","walk","writeFile","access","fsConstants","stat","randomUUID","fsConstants","access","cp","mkdir","readFile","rename","rm","stat","writeFile","homedir","dirname","resolve","randomUUID","fsConstants","access","mkdir","readFile","rename","rm","stat","writeFile","dirname","pathExists","resolve","readFile","resolve","resolve","readFile","rm","mkdir","dirname","randomUUID","writeFile","rename","stat","homedir","dirExists","readFile","resolve","cp","path","readdir","access","fsConstants","lstat","rm","unlink","homedir","resolve","access","lstat","readFile","readlink","stat","homedir","isAbsolute","resolve","pathExists","resolve","rm","path","lstat","unlink","homedir","appendFile","mkdir","homedir","dirname","resolve","access","readFile","homedir","isAbsolute","pathRelative","pathResolve","fsConstants","access","readdir","stat","extname","resolve","dirExists","fileExists","walk","fsConstants","access","readFile","resolve","resolve","access","fsConstants","readFile","RUSH_AI_PLUGIN_NAME","RUSH_AI_MARKETPLACE_NAME","RUSH_MCP_SERVER_KEY","isAbsolute","pathResolve","pathRelative","defaultRushAiBinaryResolver","homedir","RUSH_AI_PLUGIN_NAME","RUSH_AI_MARKETPLACE_NAME","errorMessage","access","readFile","runList","registerListCommand","statusIcon","formatRegistryError","targetDisplayName","runUpdate","registerUpdateCommand","registerListCommand","registerUpdateCommand","existsSync","readFileSync","basename","resolve","resolve","existsSync","basename","file","readFileSync","mkdir","readFile","stat","path","chalk","resolve","resolve","chalk","existsSync","mkdirSync","readFileSync","writeFileSync","file","resolve","chalk","resolve","DOMAIN_PREFIX_REGEX","validateDomainPrefix","existsSync","readdirSync","readFileSync","path","chalk","path","chalk","existsSync","readFileSync","readdirSync","shortHash","path","file","mkdir","stat","readFile","mkdir","readFile","writeFile","homedir","dirname","join","mkdir","dirname","writeFile","join","homedir","readFile","chalk"]}
|