arcane-cli 1.11.3 → 1.11.5
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/assets/.arcane/agents/agent-policies-template.md +1 -2
- package/dist/assets/.arcane/governance/agent-policies.md +2 -3
- package/dist/assets/.arcane/governance/git-conventions.md +20 -35
- package/dist/assets/.arcane/governance/spell-authoring-standards.md +4 -1
- package/dist/assets/.arcane/governance/universal-agent-rules.md +1 -1
- package/dist/assets/.arcane/image-prompts/adelaide-image-prompt.md +1 -1
- package/dist/assets/.arcane/image-prompts/alexander-image-prompt.md +1 -1
- package/dist/assets/.arcane/image-prompts/bess-image-prompt.md +1 -1
- package/dist/assets/.arcane/image-prompts/circe-image-prompt.md +1 -1
- package/dist/assets/.arcane/image-prompts/custodio-image-prompt.md +1 -1
- package/dist/assets/.arcane/image-prompts/iris-image-prompt.md +1 -1
- package/dist/assets/.arcane/image-prompts/kellar-image-prompt.md +1 -1
- package/dist/assets/.arcane/image-prompts/lafayette-image-prompt.md +1 -1
- package/dist/assets/.arcane/image-prompts/lince-image-prompt.md +1 -1
- package/dist/assets/.arcane/image-prompts/mercurio-image-prompt.md +1 -1
- package/dist/assets/.arcane/image-prompts/merlin-image-prompt.md +1 -1
- package/dist/assets/.arcane/image-prompts/prospero-image-prompt.md +1 -1
- package/dist/assets/.arcane/spells/_fragments/branch-naming.md +9 -0
- package/dist/assets/.arcane/spells/_fragments/branch-rename-gate.md +7 -0
- package/dist/assets/.arcane/spells/spell-architect.md +1 -1
- package/dist/assets/.arcane/spells/spell-commit-work.md +1 -1
- package/dist/assets/.arcane/spells/spell-create-pull-request.md +21 -0
- package/dist/assets/.arcane/spells/spell-full-cycle.md +1 -1
- package/dist/assets/.arcane/spells/spell-open-session.md +29 -7
- package/dist/assets/.github/instructions/agent-output.instructions.md +10 -13
- package/dist/index.js +815 -68
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/modules/copier.ts","../src/modules/manifest.ts","../src/config/profiles.ts","../src/modules/registry.ts","../src/modules/user-tier.ts","../src/modules/spell-compiler.ts","../src/modules/banner.ts","../src/modules/agents.ts","../src/config/agent-roles.ts","../src/config/agent-profiles.ts","../src/modules/naming.ts","../src/modules/agent-loader.ts","../src/modules/agent-schema.ts","../src/modules/agent-generator.ts","../src/modules/merger.ts","../src/modules/hub.ts","../src/modules/git.ts","../src/modules/ado-context.ts","../src/modules/push-safety.ts","../src/modules/secrets-scan.ts","../src/modules/component-install.ts","../src/commands/add.ts","../src/commands/update.ts","../src/modules/merge3.ts","../src/modules/registry-changes.ts","../src/modules/update-installs.ts","../src/modules/npm-registry.ts","../src/commands/status.ts","../src/modules/version-check.ts","../src/modules/diagram-generator.ts","../src/commands/uninstall.ts","../src/commands/doctor.ts","../src/config/incidents.ts","../src/modules/incident-gate.ts","../src/modules/denylist-scan.ts","../src/modules/exec.ts","../src/modules/platform-policy.ts","../src/modules/placeholders.ts","../src/commands/ward.ts","../src/modules/ward.ts","../src/commands/unblock-push.ts","../src/commands/block-push.ts","../src/commands/report.ts","../src/modules/show-report/generate.ts","../src/modules/show-report/model.ts","../src/modules/show-report/plan-parser.ts","../src/modules/show-report/queue-parser.ts","../src/modules/show-report/ledger-parser.ts","../src/modules/show-report/decisions-parser.ts","../src/modules/show-report/sources.ts","../src/modules/show-report/render.ts","../src/modules/show-report/theme.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { createRequire } from \"node:module\";\nimport { fileURLToPath } from \"node:url\";\nimport { dirname, join } from \"node:path\";\nimport { runInit } from \"./commands/init.js\";\nimport { runAdd } from \"./commands/add.js\";\nimport { runUpdate } from \"./commands/update.js\";\nimport { runStatus } from \"./commands/status.js\";\nimport { runUninstall } from \"./commands/uninstall.js\";\nimport { runDoctor } from \"./commands/doctor.js\";\nimport { runWardCli } from \"./commands/ward.js\";\nimport { runUnblockPush } from \"./commands/unblock-push.js\";\nimport { runBlockPush } from \"./commands/block-push.js\";\nimport { runReport } from \"./commands/report.js\";\nimport { agentsTargetDir, runAgentsInit, runAgentsList, runAgentsSync } from \"./modules/agents.js\";\nimport { printWelcome } from \"./modules/banner.js\";\nimport { userTierRoot } from \"./modules/user-tier.js\";\nimport type {\n Profile,\n AgentInitOptions,\n AgentProfileId,\n InstallScope,\n ManifestFlags,\n NamingStrategy,\n AgentSyncOptions,\n} from \"./types.js\";\n\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"../package.json\") as { version: string };\n\n/**\n * Resolved path to the bundled assets directory.\n * Points to src/assets/ during tests (vitest runs source directly) and\n * dist/assets/ in the published package (tsup bundles to dist/index.js).\n * Can be overridden via ARCANE_ASSETS_DIR env var for testing the built binary.\n */\nexport const ASSETS_DIR =\n process.env[\"ARCANE_ASSETS_DIR\"] ??\n join(dirname(fileURLToPath(import.meta.url)), \"assets\");\n\nconst program = new Command();\n\n/**\n * ARC-045 decision 3 / CS-04: `--user` points init/update/status/uninstall\n * at the per-user tier (`~/.arcane`) instead of the current repository. One\n * description string, so the four commands describe the same thing.\n */\nconst USER_FLAG_DESCRIPTION =\n \"Operate on the per-user tier at ~/.arcane — spells shared by every repository on this machine, fanned out to ~/.agents/skills (Codex, Copilot) and ~/.claude/commands (Claude Code)\";\n\n/**\n * The same flag on `spell agents`, which moves a different set of files: the\n * roster and its rendered agent files, not spells.\n */\nconst USER_TIER_AGENTS_DESCRIPTION =\n \"Operate on the per-user tier at ~/.arcane — one agent roster shared by every repository on this machine, rendered to ~/.copilot/agents (VS Code agent modes)\";\n\nfunction targetDirFor(opts: { user?: boolean }): string {\n return opts.user ? userTierRoot() : process.cwd();\n}\n\n/**\n * PRD D-02: one flag per manifest question, on both `init` and `update`, so a\n * harness can finish setup without a terminal. A flag answers an unset\n * question or tightens push_policy; it never loosens push_policy, which stays\n * `spell unblock-push`'s job (ARC-034 decision 6).\n */\nfunction withManifestFlags(command: Command): Command {\n return command\n .option(\"--role <role>\", \"Answer the hub question: hub | consumer\")\n .option(\"--tracking-mode <mode>\", \"Answer the tracking question: internal | external (external needs --external-provider)\")\n .option(\"--external-provider <provider>\", \"With --tracking-mode external: ado | github | jira | other\")\n .option(\"--content-sensitivity <level>\", \"Answer the content question: standard | sensitive\")\n .option(\n \"--push-policy <policy>\",\n \"Answer the push question: open | guarded | blocked. Can tighten a recorded policy, never loosen it (use `spell unblock-push`)\",\n )\n .option(\"--subject-root <path>\", \"Answer the subject question with a directory (docs profile)\");\n}\n\nfunction manifestFlagsFrom(opts: ManifestFlags): ManifestFlags {\n return {\n role: opts.role,\n trackingMode: opts.trackingMode,\n externalProvider: opts.externalProvider,\n contentSensitivity: opts.contentSensitivity,\n pushPolicy: opts.pushPolicy,\n subjectRoot: opts.subjectRoot,\n };\n}\n\nprogram\n .name(\"spell\")\n .description(\"Arcane framework CLI — scaffold and manage governance files\")\n .version(pkg.version)\n .action(async () => {\n // Commander routes an unrecognized subcommand here too (no root\n // `.arguments()` is declared, so it doesn't emit `command:*`) — the only\n // way to tell \"no subcommand\" from \"an unrecognized one\" is the leftover\n // positional args.\n if (program.args.length > 0) {\n const [attempted] = program.args;\n console.error(`spell: unrecognized command '${attempted}'`);\n console.error();\n console.error(\n `Spells are prompts, not CLI commands — run /spell-${attempted} in your agent client.`,\n );\n console.error();\n console.error(\"Real CLI commands:\");\n for (const cmd of program.commands) {\n console.error(` ${cmd.name().padEnd(14)} ${cmd.description()}`);\n }\n process.exitCode = 1;\n return;\n }\n // Bare `spell` with no subcommand — show the welcome (banner + real repo\n // state + Spell Loop), then the command reference.\n await printWelcome(pkg.version, process.cwd());\n program.outputHelp();\n });\n\nwithManifestFlags(\n program\n .command(\"init\")\n .description(\"Scaffold Arcane framework files into the current repository\")\n .option(\"--profile <profile>\", \"Profile: full | lite | methodology | docs | governance-only\")\n .option(\"--force\", \"Overwrite existing files without error\")\n .option(\"--dry-run\", \"Preview what would be installed without making changes\")\n .option(\"--user\", USER_FLAG_DESCRIPTION),\n)\n .action(\n async (opts: { profile?: string; force?: boolean; dryRun?: boolean; user?: boolean } & ManifestFlags) => {\n await runInit(\n {\n profile: opts.profile as Profile | undefined,\n force: opts.force,\n dryRun: opts.dryRun,\n user: opts.user,\n manifestFlags: manifestFlagsFrom(opts),\n },\n targetDirFor(opts),\n ASSETS_DIR,\n pkg.version,\n );\n },\n );\n\nprogram\n .command(\"add <components...>\")\n .description(\"Add one or more components to an existing Arcane installation, in order; stops at the first failure\")\n .option(\"--force\", \"Overwrite existing files without error\")\n .option(\"--dry-run\", \"Preview what would be added without making changes\")\n .action(\n async (\n components: string[],\n opts: { force?: boolean; dryRun?: boolean },\n ) => {\n await runAdd(\n components,\n { force: opts.force, dryRun: opts.dryRun },\n process.cwd(),\n ASSETS_DIR,\n pkg.version,\n );\n },\n );\n\nwithManifestFlags(\n program\n .command(\"update\")\n .description(\"Update installed components to the current package version\")\n .option(\"--dry-run\", \"Preview what would be updated without making changes\")\n .option(\n \"--prune\",\n \"Also delete orphaned managed files (tracked before this update, no longer part of any current component). Hash-checked: an edited file is reported, never silently deleted\",\n )\n .option(\"--user\", USER_FLAG_DESCRIPTION)\n .option(\n \"--add-new\",\n \"Also install the components your profile gained since your install (never an initOnly component, never over an existing file). Without it a terminal asks and a script installs nothing\",\n )\n .option(\n \"--default-scope <scope>\",\n \"With --user: remember repo | user as the answer `spell init` pre-selects for NEW repositories on this machine. Never rewrites an existing repository, and the question is still asked\",\n ),\n)\n .action(async (opts: { dryRun?: boolean; prune?: boolean; user?: boolean; addNew?: boolean; defaultScope?: string } & ManifestFlags) => {\n if (opts.defaultScope !== undefined) {\n if (!opts.user) {\n console.error(\n \"\\n ✗ --default-scope applies to the user tier only. It records a preference in\" +\n \"\\n ~/.arcane, not in a repository. Re-run it as:\" +\n \"\\n\\n spell update --user --default-scope \" + opts.defaultScope + \"\\n\",\n );\n process.exit(1);\n }\n if (opts.defaultScope !== \"repo\" && opts.defaultScope !== \"user\") {\n console.error(\n \"\\n ✗ --default-scope takes repo or user, not: \" + opts.defaultScope + \"\\n\",\n );\n process.exit(1);\n }\n }\n await runUpdate(\n {\n dryRun: opts.dryRun,\n prune: opts.prune,\n user: opts.user,\n addNew: opts.addNew,\n manifestFlags: manifestFlagsFrom(opts),\n ...(opts.defaultScope === undefined\n ? {}\n : { defaultScope: opts.defaultScope as InstallScope }),\n },\n targetDirFor(opts),\n ASSETS_DIR,\n pkg.version,\n );\n });\n\nprogram\n .command(\"status\")\n .description(\"Show installed components and check for updates\")\n .option(\"--user\", USER_FLAG_DESCRIPTION)\n .action(async (opts: { user?: boolean }) => {\n await runStatus(targetDirFor(opts), pkg.version, { user: opts.user });\n });\n\nprogram\n .command(\"uninstall\")\n .description(\"Remove all installed Arcane framework files\")\n .option(\"--yes\", \"Skip confirmation prompt\")\n .option(\"--dry-run\", \"Preview what would be removed without deleting files\")\n .option(\"--user\", USER_FLAG_DESCRIPTION)\n .action(async (opts: { yes?: boolean; dryRun?: boolean; user?: boolean }) => {\n await runUninstall({ yes: opts.yes, dryRun: opts.dryRun, user: opts.user }, targetDirFor(opts));\n });\n\nprogram\n .command(\"block-push\")\n .description(\"Enforce push_policy: blocked on this repository (pre-push hook + disabled push URLs); only ever tightens\")\n .action(async () => {\n await runBlockPush(process.cwd());\n });\n\nprogram\n .command(\"unblock-push\")\n .description(\"Lift this repository's push block (interactive terminal only)\")\n .action(async () => {\n await runUnblockPush(process.cwd());\n });\n\nprogram\n .command(\"doctor\")\n .description(\"Check your environment for Arcane prerequisites\")\n .option(\"--fix\", \"Automatically create missing session continuity files\")\n .option(\"--leaks\", \"Run only the on-demand secrets/credential scan (ARC-037), skipping the default checks\")\n .action(async (opts: { fix?: boolean; leaks?: boolean }) => {\n await runDoctor(\n process.cwd(),\n { fix: opts.fix, leaks: opts.leaks, packageVersion: pkg.version },\n ASSETS_DIR,\n );\n });\n\n// ─── spell ward ───────────────────────────────────────────────────────────────\n\nprogram\n .command(\"ward\")\n .description(\"Scan the repo for leaked identifiers -- third-party trademarks and the operator's own personal identifiers alike (T18)\")\n .option(\n \"--terms <terms>\",\n \"Comma-separated denylist terms, in addition to the built-in vendor-identifier list. Seed this from your OWN identity too, not just org/venture names -- usernames, machine names, and personal handles leak the same way a private venture name does (I1)\",\n )\n .option(\n \"--terms-file <path>\",\n \"Read denylist terms from a file instead of --terms, to avoid shell-history exposure (ARC-041). Same comma/newline-separated format as ARCANE_ORG_TOKENS.\",\n )\n .option(\"--gate\", \"Exit non-zero if any leak is found (for CI)\")\n .action(async (opts: { terms?: string; termsFile?: string; gate?: boolean }) => {\n await runWardCli(process.cwd(), opts);\n });\n\n// ─── spell report ─────────────────────────────────────────────────────────────\n\nprogram\n .command(\"report\")\n .description(\n \"Generate a program's Show Report (show-report.json + .html) from docs/plans/<slug>/PLAN.md, OPERATOR-QUEUE.md and the verification ledger -- offline, no network (ARC-042)\",\n )\n .option(\"--plan <path>\", \"Report on one PLAN.md instead of auto-discovering docs/plans/*/PLAN.md\")\n .option(\"--out <dir>\", \"Write show-report.{json,html} into this directory instead of next to the plan\")\n .option(\n \"--refresh\",\n \"Accepted for forward compatibility; external-data snapshotting is not implemented yet, so this regenerates from local sources\",\n )\n .option(\n \"--theme <mode>\",\n \"Force the in-page toggle's initial state: auto | light | dark (default auto, byte-for-byte unchanged). Pair with --out for a one-off themed copy so it doesn't overwrite the tracked show-report.html\",\n )\n .action(async (opts: { plan?: string; out?: string; refresh?: boolean; theme?: string }) => {\n await runReport(process.cwd(), opts, ASSETS_DIR);\n });\n\n// ─── spell agents ─────────────────────────────────────────────────────────────\n\nconst agentsCommand = program\n .command(\"agents\")\n .description(\"Manage the portable agent system (init, sync, list)\");\n\nagentsCommand\n .command(\"init\")\n .description(\n \"Interactive agent roster setup — choose profile, naming strategy, and install definitions\",\n )\n .option(\n \"--profile <profile>\",\n \"Agent profile: base | full | custom\",\n )\n .option(\n \"--naming <strategy>\",\n \"Naming strategy: arcanos | generic | random | custom\",\n )\n .option(\"--force\", \"Reinitialize even if roster already exists\")\n .option(\"--dry-run\", \"Preview what would be created without writing files\")\n .option(\"--user\", USER_TIER_AGENTS_DESCRIPTION)\n .action(\n async (opts: {\n profile?: string;\n naming?: string;\n force?: boolean;\n dryRun?: boolean;\n user?: boolean;\n }) => {\n const scope: InstallScope = opts.user ? \"user\" : \"repo\";\n await runAgentsInit(\n {\n profile: opts.profile as AgentProfileId | undefined,\n naming: opts.naming as NamingStrategy | undefined,\n force: opts.force,\n dryRun: opts.dryRun,\n scope,\n } satisfies AgentInitOptions,\n agentsTargetDir(scope, process.cwd()),\n ASSETS_DIR,\n );\n },\n );\n\nagentsCommand\n .command(\"sync\")\n .description(\n \"Regenerate all client instruction files from canonical agent definitions\",\n )\n .option(\"--dry-run\", \"Preview what would be generated without writing files\")\n .option(\"--no-openclaw\", \"Skip OpenClaw workspace output\")\n .option(\"--no-copilot\", \"Skip Copilot output\")\n .option(\"--no-claude\", \"Skip Claude output\")\n .option(\"--no-codex\", \"Skip Codex output\")\n .option(\"--user\", USER_TIER_AGENTS_DESCRIPTION)\n .action(\n async (opts: {\n dryRun?: boolean;\n openclaw?: boolean;\n copilot?: boolean;\n claude?: boolean;\n codex?: boolean;\n user?: boolean;\n }) => {\n const scope: InstallScope = opts.user ? \"user\" : \"repo\";\n await runAgentsSync(\n {\n dryRun: opts.dryRun,\n openclaw: opts.openclaw,\n copilot: opts.copilot,\n claude: opts.claude,\n codex: opts.codex,\n scope,\n } satisfies AgentSyncOptions,\n agentsTargetDir(scope, process.cwd()),\n ASSETS_DIR,\n );\n },\n );\n\nagentsCommand\n .command(\"list\")\n .description(\"Show the current agent roster with assigned names and roles\")\n .option(\"--user\", USER_TIER_AGENTS_DESCRIPTION)\n .action(async (opts: { user?: boolean }) => {\n const scope: InstallScope = opts.user ? \"user\" : \"repo\";\n await runAgentsList(agentsTargetDir(scope, process.cwd()), scope);\n });\n\n// Only parse argv when executed directly — not when imported in tests.\n// Use realpathSync to resolve symlinks (e.g. npm global bin → dist/index.js).\nimport { realpathSync } from \"node:fs\";\nif (realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)) {\n await program.parseAsync(process.argv);\n}\n\nexport { program };\n","import { select, confirm, input } from \"@inquirer/prompts\";\nimport { dirname, join } from \"node:path\";\nimport chalk from \"chalk\";\nimport { copyFile, copyDirectory, fileExists } from \"../modules/copier.js\";\nimport {\n readManifest,\n writeManifest,\n isValidSubjectRoot,\n ManifestNotFoundError,\n} from \"../modules/manifest.js\";\nimport { getComponent, getProfile, listProfiles } from \"../modules/registry.js\";\nimport {\n CLAUDE_PRECEDENCE_NOTE,\n USER_FANOUT_PATHS,\n USER_TIER_COMPONENTS,\n VSCODE_USER_TIER_NOTE,\n componentForScope,\n componentForSpellScope,\n preferredSpellScope,\n describeFanoutOutcomes,\n spellIdFromStorePath,\n SPELL_FANOUT_CLIENTS,\n syncUserTierFanout,\n userTierRoot,\n} from \"../modules/user-tier.js\";\nimport {\n printDryRun,\n printStep,\n printSuccess,\n printSection,\n printInfo,\n printNextStep,\n printWarning,\n} from \"../modules/banner.js\";\nimport { runAgentsInit } from \"../modules/agents.js\";\nimport {\n askTrackingQuestions,\n hasManifestFlags,\n manifestFlagLine,\n resolveManifestFlags,\n ManifestFlagError,\n} from \"../modules/hub.js\";\nimport {\n applyBlockedPushControls,\n hookInForce,\n installClosedPrWarningHook,\n describeConfigScope,\n ARCANE_HOOKS_DIR,\n} from \"../modules/push-safety.js\";\nimport { installSecretsPrecommitHook } from \"../modules/secrets-scan.js\";\nimport {\n correctUnbornMasterDefault,\n ensureLocalPullRebase,\n inspectGitRepository,\n} from \"../modules/git.js\";\nimport type {\n ArcaneManifest,\n ContentSensitivity,\n ExternalProvider,\n HubRole,\n InstallScope,\n InstalledComponent,\n Profile,\n RegistryComponent,\n PushPolicy,\n SpellInitOptions,\n TrackingMode,\n} from \"../types.js\";\n\nconst VALID_PROFILES: Profile[] = [\"full\", \"lite\", \"governance-only\", \"methodology\", \"docs\"];\n\n/** Menu glyph per profile. Falls back to 📋 for any profile not listed. */\nconst PROFILE_ICONS: Partial<Record<Profile, string>> = {\n full: \"🧙\",\n lite: \"⚡\",\n docs: \"📖\",\n};\n\n/** True for the capability-scoped spell components (see registry.ts). */\nfunction isSpellComponent(name: string): boolean {\n return name.startsWith(\"spells-\");\n}\n\n// ─── Category labels for preview ─────────────────────────────────────────────\n\ninterface ComponentGroup {\n icon: string;\n label: string;\n count: number;\n}\n\nfunction categorizeComponents(components: RegistryComponent[]): ComponentGroup[] {\n const groups: ComponentGroup[] = [];\n\n // Spells arrive as several capability components rather than one\n // monolith, and each carries a spell's canonical source plus its three\n // generated client shims (ARC-045 / CS-03), so count canonical files --\n // one per spell -- rather than components or shims.\n const spellFiles = components.filter((c) => isSpellComponent(c.name)).flatMap((c) => c.files);\n\n const spellCount = spellFiles.filter((f) => f.startsWith(\".arcane/spells/\")).length;\n if (spellCount > 0) {\n groups.push({ icon: \"✨\", label: \"Spells (Copilot, Claude Code, Codex)\", count: spellCount });\n }\n\n const governance = components.filter(\n (c) =>\n !isSpellComponent(c.name) &&\n c.name !== \"venture-template\" &&\n c.name !== \"agent-definitions\" &&\n c.files.some((f) => f.startsWith(\".arcane/governance/\")),\n );\n if (governance.length > 0) {\n groups.push({\n icon: \"📋\",\n label: \"Governance Docs\",\n count: governance.reduce((n, c) => n + c.files.length, 0),\n });\n }\n\n const agentDef = components.find((c) => c.name === \"agent-definitions\");\n if (agentDef) {\n groups.push({\n icon: \"🛡️\",\n label: \"Agent Policies\",\n count: agentDef.files.length,\n });\n }\n\n const templates = components.find((c) => c.name === \"venture-template\");\n if (templates) {\n groups.push({\n icon: \"📄\",\n label: \"Templates\",\n count: templates.files.length,\n });\n }\n\n return groups;\n}\n\n/**\n * Builds a hover description string for the profile selection menu.\n * Shows the category breakdown so users know what they're getting.\n */\nfunction buildProfileDescription(components: RegistryComponent[], profile: Profile): string {\n const groups = categorizeComponents(components);\n const parts = groups.map((g) => `${g.icon} ${g.count} ${g.label}`);\n if (profile === \"full\" || profile === \"lite\") {\n // Insert agent roles before governance docs (spells + agents first, then the rest)\n const govIndex = groups.findIndex((g) => g.label === \"Governance Docs\");\n parts.splice(govIndex >= 0 ? govIndex : parts.length, 0, \"🤖 12 Agent Roles\");\n }\n return \"\\n\" + parts.join(\"\\n\");\n}\n\n// ─── Main ─────────────────────────────────────────────────────────────────────\n\n/**\n * Runs the `spell init` command.\n *\n * @param options CLI flags (profile, force, dryRun)\n * @param targetDir Directory to initialize (defaults to cwd in CLI)\n * @param assetsDir Path to the bundled assets root (dist/assets/ in prod, src/assets/ in tests)\n * @param packageVersion Current package version string (passed from CLI entry to avoid require path issues)\n */\nexport async function runInit(\n options: SpellInitOptions,\n targetDir: string,\n assetsDir: string,\n packageVersion: string,\n): Promise<void> {\n // The user tier is a different install with a different shape -- see\n // runInitUser. `targetDir` is the store root (~/.arcane) in that case.\n if (options.user) {\n if (hasManifestFlags(options.manifestFlags)) {\n console.error(\n \"\\n ✗ The manifest-question flags describe a repository; the user tier has none of those fields.\\n\",\n );\n process.exit(1);\n return;\n }\n await runInitUser(options, targetDir, assetsDir, packageVersion);\n return;\n }\n\n // Validate profile option if provided\n if (options.profile && !VALID_PROFILES.includes(options.profile)) {\n throw new Error(\n `Invalid profile \"${options.profile}\". Valid options: ${VALID_PROFILES.join(\", \")}`,\n );\n }\n\n // PRD D-02: validated before anything is written. A fresh install has no\n // recorded answers, so a valid flag always applies.\n let flags: Partial<ArcaneManifest> = {};\n try {\n flags = resolveManifestFlags(options.manifestFlags, {}, join(targetDir, \".arcane.json\"));\n } catch (err) {\n if (!(err instanceof ManifestFlagError)) throw err;\n console.error(`\\n ✗ ${err.message}\\n`);\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n\n // Check if already initialized\n try {\n await readManifest(targetDir);\n console.log(chalk.hex(\"#a855f7\")(`\\n ✦ Arcane v${packageVersion}\\n`));\n console.log('Already initialized. Run \"spell update\" to update existing files.');\n if (hasManifestFlags(options.manifestFlags)) {\n console.log(\"Pass the same manifest-question flags to `spell update` to record them here.\");\n }\n return;\n } catch (err) {\n if (!(err instanceof ManifestNotFoundError)) throw err;\n // ManifestNotFoundError = not initialized — proceed\n }\n\n console.log(chalk.hex(\"#a855f7\")(`\\n ✦ Arcane v${packageVersion}\\n`));\n\n // ── Git-state checks (EF-05, EF-32) ─────────────────────────────────────\n // Never runs `git init` or creates a commit -- see\n // features/init-git-state-contract/PRD.md's \"Design decision\" section for\n // why that's declined rather than deferred. Only acts where it's provably\n // safe (an unborn repo has no commits to conflict with) or where the\n // operator already explicitly confirmed continuing (uncommitted changes).\n let repoNotFound = false;\n let repoHasCommits = false;\n if (!options.dryRun) {\n const gitState = await inspectGitRepository(targetDir);\n repoHasCommits = gitState.status !== \"not-repository\" && gitState.status !== \"no-commits\";\n\n if (gitState.status === \"not-repository\") {\n repoNotFound = true;\n } else {\n if (gitState.status === \"no-commits\") {\n const correction = await correctUnbornMasterDefault(targetDir);\n if (correction.corrected) {\n printInfo(\n `Repointed the unborn branch from \"${correction.from}\" to \"${correction.to}\" (avoids the Git for Windows default-branch trap).`,\n );\n } else if (correction.blockedReason === \"target-unreadable\") {\n printWarning(\n 'This repo\\'s \"main\" branch ref exists but could not be read (broken ref). Left the current branch alone — ' +\n \"repointing onto an unreadable ref risks attaching your staged work to history Arcane cannot see. \" +\n \"Check `git for-each-ref refs/heads/main` before committing.\",\n );\n }\n }\n\n if (gitState.status === \"ready\" && gitState.uncommittedChanges > 0) {\n printWarning(\n `This repo has ${gitState.uncommittedChanges} uncommitted change${gitState.uncommittedChanges === 1 ? \"\" : \"s\"}.`,\n );\n printInfo(\"Running spell init will add new files to your working tree.\");\n console.log();\n const continueAnyway = await confirm({\n message: \"Continue anyway?\",\n default: true,\n });\n if (!continueAnyway) {\n console.log(\"\\nCancelled. Commit or stash your changes first.\");\n return;\n }\n console.log();\n }\n\n const rebaseResult = await ensureLocalPullRebase(targetDir);\n if (rebaseResult.action === \"explicit-false-preserved\") {\n printWarning(\n \"This repo's pull.rebase is explicitly set to false, but Arcane's governance mandates rebase-and-fast-forward (see .arcane/governance/git-conventions.md). Left as-is since you set it explicitly.\",\n );\n }\n }\n }\n\n // ── Step 1: Resolve profile (prompt if not provided) ───────────────────\n let profile: Profile = options.profile as Profile;\n if (!profile) {\n const profiles = listProfiles();\n profile = (await select({\n message: \"Select an installation profile:\\n\",\n choices: profiles.map((p) => {\n const profileComponents = getProfile(p.id);\n return {\n value: p.id,\n name: `${PROFILE_ICONS[p.id] ?? \"📋\"} ${p.displayName} — ${p.description}`,\n description: buildProfileDescription(profileComponents, p.id),\n };\n }),\n })) as Profile;\n }\n\n // ── Step 2: Resolve components ─────────────────────────────────────────\n const profileComponents = getProfile(profile);\n\n // ── Step 2b: Spell scope (ARC-045 decision 4 / CS-05) ──────────────────\n // Asked here rather than beside the other manifest questions (Steps 5a-5e)\n // because it changes WHAT gets installed, not just what gets recorded: the\n // preview below and the copy loop both have to reflect it.\n //\n // Asked only when this machine actually has a user tier -- most do not, and\n // a question about a store that does not exist is noise -- and only in an\n // interactive install, the same gate every other manifest question uses.\n // The question is always asked; only which answer is pre-selected changes.\n // No on purpose unless this machine says otherwise (ARC-048): opting out is\n // committed to .arcane.json and inherited by every clone, so a shared or\n // team repository must not stop carrying its spells just because the machine\n // that happened to run `spell init` had a tier installed. An operator who\n // has decided for their own machine sets `default_spell_scope` in the store\n // and gets their answer pre-selected -- in NEW repositories only, since this\n // is read at init time and rewrites no existing manifest.\n let spell_scope: InstallScope | undefined;\n if (!options.profile && !options.dryRun) {\n let storeVersion: string | undefined;\n let preferred: InstallScope = \"repo\";\n try {\n const store = await readManifest(userTierRoot());\n storeVersion = store.version;\n preferred = preferredSpellScope(store);\n } catch {\n // No user tier on this machine (or an unreadable one) -- do not ask.\n }\n if (storeVersion !== undefined) {\n console.log();\n const useTier = await confirm({\n message: `This machine has an Arcane user tier (v${storeVersion}). Use it for this repository's spells instead of installing copies here?`,\n default: preferred === \"user\",\n });\n // Recorded either way, like the hub question: \"asked and declined\" is\n // worth telling apart from \"predates the field\".\n spell_scope = useTier ? \"user\" : \"repo\";\n }\n }\n const components = profileComponents.map((component) =>\n componentForSpellScope(component, spell_scope ?? \"repo\"),\n );\n\n // ── Step 3: Preview what will be installed ─────────────────────────────\n const groups = categorizeComponents(components);\n\n console.log();\n printSection(`📦 Profile: ${profile}`);\n console.log();\n\n // Show agent setup as upcoming interactive step\n const hasAgents = profile === \"full\" || profile === \"lite\";\n\n // Build summary parts (same style as hover description)\n const previewParts = groups.map((g) => `${g.icon} ${g.count} ${g.label}`);\n if (hasAgents) {\n // Insert agent roles before governance docs\n const govIndex = groups.findIndex((g) => g.label === \"Governance Docs\");\n previewParts.splice(govIndex >= 0 ? govIndex : previewParts.length, 0, \"🤖 12 Agent Roles\");\n }\n console.log(` ${previewParts.join(\" · \")}`);\n\n console.log();\n\n // ── Step 4: Confirm (skip in non-interactive mode) ─────────────────────\n if (!options.dryRun && !options.profile) {\n const proceed = await confirm({\n message: \"Proceed with installation?\",\n default: true,\n });\n if (!proceed) {\n console.log(\"\\nCancelled.\");\n return;\n }\n }\n\n // ── Step 5: Copy files ─────────────────────────────────────────────────\n const installedComponents: InstalledComponent[] = [];\n let fileCount = 0;\n\n for (const component of components) {\n const installedFiles: string[] = [];\n const fileHashes: Record<string, string> = {};\n\n for (const file of component.files) {\n const srcPath = join(assetsDir, component.sourceOverrides?.[file] ?? file);\n if (options.dryRun) {\n printDryRun(`Would copy: ${file}`);\n } else {\n try {\n fileHashes[file] = await copyFile(srcPath, targetDir, file, { force: options.force });\n } catch (err) {\n // skipExisting components silently skip files that already exist\n if (component.skipExisting && err instanceof Error && err.message.includes(\"already exists\")) {\n continue;\n }\n throw err;\n }\n }\n installedFiles.push(file);\n fileCount++;\n }\n\n for (const dir of component.directories ?? []) {\n const srcDirPath = join(assetsDir, dir);\n if (options.dryRun) {\n printDryRun(`Would copy directory: ${dir}/`);\n } else {\n const copied = await copyDirectory(srcDirPath, targetDir, dir, { force: options.force });\n for (const { path: copiedPath, hash } of copied) {\n installedFiles.push(copiedPath);\n fileHashes[copiedPath] = hash;\n }\n fileCount += copied.length;\n }\n }\n\n if (!options.dryRun) {\n installedComponents.push({\n name: component.name,\n files: installedFiles,\n installedVersion: packageVersion,\n fileHashes,\n });\n }\n }\n\n // Show compact category summary (not 60 individual file paths)\n if (!options.dryRun) {\n for (const group of groups) {\n printStep(`${group.icon} ${group.count} ${group.label}`);\n }\n }\n\n if (options.dryRun) {\n // Show what agent setup would install\n if (hasAgents) {\n const agentFiles = await import(\"node:fs\").then((fsModule) =>\n fsModule.promises.readdir(join(assetsDir, \"agents\")),\n );\n const yamlFiles = agentFiles.filter((f) => f.endsWith(\".yaml\")).sort();\n printDryRun(\"Would run agent setup (interactive):\");\n printDryRun(\"Would write: .arcane/agents.yaml\");\n for (const f of yamlFiles) {\n printDryRun(`Would copy: .arcane/agents/${f}`);\n }\n }\n printDryRun(`Would initialize with profile \"${profile}\" — ${fileCount} files`);\n return;\n }\n\n // ── Step 5a: Hub question ───────────────────────────────────────────────\n // Explicit opt-in only -- never inferred from repo content (a `ventures/`\n // directory can legitimately exist in a consumer repo too, e.g. one this\n // very init run just created via a governance-only profile). Written\n // explicitly either way so a future `spell update` retrofit wizard can\n // tell \"asked and declined\" apart from \"predates this feature\".\n let role: HubRole | undefined = flags.role;\n if (role === undefined && !options.profile) {\n console.log();\n const isHub = await confirm({\n message: \"Will this repo manage other ventures as a hub? (idea books, spell-manifest promotion, venture registry)\",\n default: false,\n });\n role = isHub ? \"hub\" : \"consumer\";\n }\n\n // ── Step 5b: Tracking-mode question (EF-14) ─────────────────────────────\n // governance-only/methodology profiles have no code-tracking surface --\n // default without asking, even under --profile (deterministic, no prompt\n // to skip). full/lite ask once, interactively only, mirroring the hub\n // question's own gating exactly -- a scripted full/lite install leaves\n // this unset, resolved later by spell update's retrofit wizard.\n let tracking_mode: TrackingMode | undefined;\n let external_provider: ExternalProvider | null | undefined;\n if (flags.tracking_mode !== undefined) {\n tracking_mode = flags.tracking_mode;\n external_provider = flags.external_provider ?? null;\n } else if (profile === \"governance-only\" || profile === \"methodology\" || profile === \"docs\") {\n tracking_mode = \"internal\";\n external_provider = null;\n } else if (!options.profile) {\n console.log();\n ({ tracking_mode, external_provider } = await askTrackingQuestions(targetDir));\n }\n\n // ── Step 5c: Subject shape (EF-07) ──────────────────────────────────────\n // Asked for the docs profile only. Other profiles describe code or a\n // venture portfolio, where \"what is this repo about\" is already answered by\n // business_root or by the code itself; a docs/records repo is the case where\n // the repository IS one subject and nothing currently expresses that.\n // Interactive only, like every other manifest question.\n let subject_root: string | null | undefined = flags.subject_root;\n if (subject_root === undefined && profile === \"docs\" && !options.profile) {\n console.log();\n const shape = await select({\n message: \"What does this repository hold?\",\n choices: [\n {\n value: \"root\",\n name: \"One subject, at the repository root (documents sit alongside Arcane's files)\",\n },\n { value: \"subdir\", name: \"One subject, in its own directory\" },\n { value: \"portfolio\", name: \"Several subjects or ventures (decide per-document later)\" },\n ],\n });\n if (shape === \"root\") {\n // \".\" is deliberate, not a placeholder: it lets an existing archive come\n // under governance without being restructured first (EF-07 / MH-02).\n subject_root = \".\";\n } else if (shape === \"subdir\") {\n const answer = await input({\n message: \"Directory holding the subject's documents:\",\n default: \"docs\",\n validate: (v) =>\n isValidSubjectRoot(v.trim() || \"docs\") ||\n \"Must be a relative path inside the repository (no leading /, drive letter, or ..).\",\n });\n subject_root = answer.trim() || \"docs\";\n }\n else if (shape === \"portfolio\") {\n // Explicit null, not unset: records that the question was asked and\n // answered, so spell update's retrofit doesn't put it again every run.\n subject_root = null;\n }\n }\n\n // ── Step 5d: Content sensitivity (EF-12) ────────────────────────────────\n // Asked on every interactive install, not just docs: a code repo can hold\n // sensitive records too. Declared once for the whole repository -- per-file\n // detection is unreliable for general documents, which is why the docs-mode\n // PRD rejects scanning as a primary mechanism.\n let content_sensitivity: ContentSensitivity | undefined = flags.content_sensitivity;\n if (content_sensitivity === undefined && !options.profile) {\n console.log();\n content_sensitivity = (await select({\n message: \"How should agents treat this repository's contents?\",\n choices: [\n { value: \"standard\", name: \"Standard — agents may quote contents in journals and decisions\" },\n {\n value: \"sensitive\",\n name: \"Sensitive — agents reference documents by path, never transcribe them\",\n },\n ],\n default: \"standard\",\n })) as ContentSensitivity;\n }\n\n // ── Step 5e: Push policy (EF-09) ────────────────────────────────────────\n // Default \"open\" — strictly additive, so every existing repository and every\n // scripted install behaves exactly as before. Asked interactively only.\n let push_policy: PushPolicy | undefined = flags.push_policy;\n if (push_policy === undefined && !options.profile) {\n console.log();\n push_policy = (await select({\n message: \"Should this repository be allowed to push to a remote?\",\n choices: [\n { value: \"open\", name: \"Yes — normal repository\" },\n { value: \"guarded\", name: \"Sensitive, but keep push working — remind me instead\" },\n {\n value: \"blocked\",\n name: \"No — block pushes (a pre-push hook plus a disabled push URL)\",\n },\n ],\n default: \"open\",\n })) as PushPolicy;\n }\n\n // ── Step 6: Write manifest ─────────────────────────────────────────────\n const manifest: ArcaneManifest = {\n version: packageVersion,\n profile,\n installedAt: new Date().toISOString(),\n components: installedComponents,\n ...(spell_scope ? { spell_scope } : {}),\n ...(role ? { role } : {}),\n ...(tracking_mode ? { tracking_mode, external_provider } : {}),\n ...(subject_root !== undefined ? { subject_root } : {}),\n ...(content_sensitivity ? { content_sensitivity } : {}),\n ...(push_policy ? { push_policy } : {}),\n };\n await writeManifest(targetDir, manifest);\n\n printSuccess(`Initialized with profile \"${profile}\" — ${fileCount} files installed`);\n\n // A scripted install leaves unflagged questions unset (PRD D-02): say which,\n // and the exact line that answers them.\n const flagLine = options.profile ? manifestFlagLine(manifest) : undefined;\n if (flagLine) {\n printInfo(\"Some setup questions were left unanswered. Answer them without a prompt with:\");\n for (const line of flagLine) printInfo(` ${line}`);\n }\n\n // ── Push-safety controls (EF-09 R2/R3/R4/R7) ────────────────────────────\n // Wrapped: a failure here must not surface as a raw stack trace after\n // \"Initialized successfully\", leaving a manifest that says \"blocked\" while\n // the operator believes they are protected.\n if (push_policy === \"blocked\" || push_policy === \"guarded\") {\n try {\n if (push_policy === \"blocked\") {\n const controls = await applyBlockedPushControls(targetDir, {\n emit: (m) => (m.level === \"warning\" ? printWarning(m.text) : printInfo(m.text)),\n });\n // A flag-driven, non-interactive init must not read as success while\n // the recorded block is only partly in force (ARC-034 decision 5).\n if (!hookInForce(controls.hook) || controls.urls.some((u) => u.status === \"failed\")) {\n printWarning(\n 'push_policy is recorded as \"blocked\", but the block is not fully in force. Resolve the ' +\n \"cause above, then run `spell block-push`; `spell doctor` shows exactly what is missing.\",\n );\n process.exitCode = 1;\n }\n } else {\n printInfo(\n \"Marked as push-guarded. No technical control was installed — check the remote is the \" +\n \"one you intend before pushing. `spell doctor` will keep reminding you.\",\n );\n }\n } catch (err) {\n printWarning(\n `Could not fully apply push-safety controls: ${err instanceof Error ? err.message : String(err)}. ` +\n `The manifest records push_policy: \"${push_policy}\", but this repository may not actually be ` +\n \"protected. Run `spell doctor` to see exactly what is and isn't in place.\",\n );\n }\n }\n\n // ── Closed-PR push warning (ARC-035 decision 4) ─────────────────────────\n // Every tier except \"blocked\" gets this: a \"blocked\" repo already refuses\n // every push outright and has no use for a warning about which PR it\n // would have gone to (installing here would also silently downgrade that\n // stronger control, since both share the \"pre-push\" hook slot). Wrapped\n // the same way as the blocks above -- a failure here must not surface as\n // a raw stack trace after \"Initialized successfully\".\n if (push_policy !== \"blocked\") {\n try {\n const hook = await installClosedPrWarningHook(targetDir);\n if (hook.status === \"installed\") {\n printInfo(\"Installed a pre-push hook that warns when the current branch's PR is already closed or merged.\");\n }\n // Other outcomes (refused-*, already-ours) mirror the blocking hook's\n // own install path and are silent here on purpose: this hook is a\n // convenience warning, not a security control, so a refusal doesn't\n // leave the repository in a worse-than-before state the way a silently\n // skipped block would -- `spell doctor` still surfaces the refusal\n // reason for the shared hooks-path collision case.\n } catch {\n // Never let this convenience warning's own failure surface as a raw\n // stack trace after \"Initialized successfully\" -- it is strictly\n // additive, so a failure here means the repo is exactly as protected\n // as it was before this block ran, not less.\n }\n }\n\n // ── Secrets pre-commit hook (ARC-037 decision 2) ────────────────────────\n // Deliberately push_policy-independent: every profile gets this, since\n // credential leakage is a risk regardless of whether history may reach a\n // remote. Mandatory, not an interactive question -- see secrets-scan.ts's\n // own reasoning for why. Wrapped the same way as the push-safety block\n // above: a failure here must not surface as a raw stack trace after\n // \"Initialized successfully\".\n try {\n const hook = await installSecretsPrecommitHook(targetDir);\n if (hook.status === \"installed\") {\n printInfo(\"Installed a pre-commit hook that scans staged content for leaked credentials.\");\n } else if (hook.status === \"refused-unreadable-config\") {\n printWarning(\n \"Did not install the secrets pre-commit hook: git could not report the current \" +\n \"core.hooksPath (unreadable config, or a git too old for `--show-scope`).\",\n );\n } else if (hook.status === \"refused-default-hooks\") {\n printWarning(\n `Did not install the secrets pre-commit hook: this repository has hooks in git's default ` +\n `directory (${hook.hooks.join(\", \")}). Setting core.hooksPath would silently stop them ` +\n `running. Move them under \\`${ARCANE_HOOKS_DIR}\\` yourself and re-run.`,\n );\n } else if (hook.status === \"refused-foreign-hooks-path\") {\n printWarning(\n `Did not install the secrets pre-commit hook: core.hooksPath is already \"${hook.existing}\" ` +\n `(${describeConfigScope(hook.scope)}), so another hook manager owns it.`,\n );\n }\n // \"already-ours\": nothing to report, matches the prior install exactly.\n } catch (err) {\n printWarning(\n `Could not install the secrets pre-commit hook: ${err instanceof Error ? err.message : String(err)}. ` +\n \"Run `spell doctor --leaks` directly to scan on demand instead.\",\n );\n }\n\n // ── Step 7: Offer agent setup (for full and lite profiles) ─────────────\n let agentsConfigured = false;\n if (hasAgents && !options.profile) {\n console.log();\n const setupAgents = await confirm({\n message: \"🤖 Set up your agent team? (names, roles, AI tool configs)\",\n default: true,\n });\n\n if (setupAgents) {\n await runAgentsInit({}, targetDir, assetsDir);\n agentsConfigured = true;\n }\n }\n\n // ── Step 8: Print next steps ───────────────────────────────────────────\n console.log();\n printSection(\"🚀 Next steps\");\n console.log();\n let step = 1;\n\n if (!agentsConfigured && profile !== \"governance-only\") {\n printNextStep(step++, \"Run `spell agents init` to set up your agent team\");\n }\n if (agentsConfigured) {\n printNextStep(\n step++,\n \"Edit .arcane/agents.yaml to customize roles → `spell agents sync` to regenerate AI tool configs\",\n );\n }\n if (profile !== \"governance-only\") {\n printNextStep(\n step++,\n \"Review .arcane/governance/ docs and customize for your project\",\n );\n }\n if (repoNotFound) {\n printNextStep(\n step++,\n 'Initialize Git for this project: `git init -b main` (avoids defaulting to \"master\" on systems where that\\'s the system default)',\n );\n }\n const wroteGitattributes = installedComponents.some((c) => c.files.includes(\".gitattributes\"));\n if (wroteGitattributes && repoHasCommits) {\n printWarning(\n \"Installed .gitattributes, which normalizes text to LF. In a repository with history, files \" +\n \"committed with CRLF can show as modified once Git renormalizes them (for example after \" +\n \"`git add --renormalize .`). Review and commit that as its own change.\",\n );\n }\n printNextStep(\n step++,\n `Commit the new files: git add .arcane .github .claude AGENTS.md CLAUDE.md .arcane.json${wroteGitattributes ? \" .gitattributes\" : \"\"}`,\n );\n console.log();\n}\n\n// ─── spell init --user ────────────────────────────────────────────────────────\n\n/**\n * `spell init --user` (ARC-045 decision 3 / CS-04). `storeRoot` is `~/.arcane`,\n * resolved by the CLI entry. Deliberately none of the repository ceremony\n * above: no profile (the tier is spells-only and un-profiled), no git-state\n * checks (the store is not a repository), none of the manifest questions\n * (hub role, tracking mode, sensitivity and push policy describe a\n * repository), no hooks, no agent setup. Copies the canonical spells into the\n * store through the same copier and hash record a repository install uses,\n * fans the user-level shims out to the clients' home directories, and records\n * both in the store manifest so `update`/`status`/`uninstall --user` know\n * exactly what Arcane wrote -- and nothing else.\n */\nasync function runInitUser(\n options: SpellInitOptions,\n storeRoot: string,\n assetsDir: string,\n packageVersion: string,\n): Promise<void> {\n if (options.profile) {\n throw new Error(\n '\"--profile\" does not apply to \"--user\": the user tier always installs every spell and nothing else.',\n );\n }\n if (hasManifestFlags(options.manifestFlags)) {\n throw new Error(\n 'The manifest-question flags do not apply to \"--user\": they describe a repository, and the user tier is not one.',\n );\n }\n\n try {\n await readManifest(storeRoot);\n console.log(chalk.hex(\"#a855f7\")(`\\n ✦ Arcane v${packageVersion}\\n`));\n console.log(\n `Already initialized (user tier at ${storeRoot}). Run \"spell update --user\" to update it.`,\n );\n return;\n } catch (err) {\n if (!(err instanceof ManifestNotFoundError)) throw err;\n }\n\n console.log(chalk.hex(\"#a855f7\")(`\\n ✦ Arcane v${packageVersion}\\n`));\n\n // The store's parent is the home directory by construction (userTierRoot),\n // and the fan-out paths are relative to it.\n const homeDir = dirname(storeRoot);\n const components = USER_TIER_COMPONENTS.map((name) =>\n componentForScope(getComponent(name), \"user\"),\n );\n const spellIds = components\n .flatMap((c) => c.files)\n .map((file) => spellIdFromStorePath(file))\n .filter((id): id is string => id !== undefined);\n\n printSection(`📦 User tier — ${storeRoot}`);\n console.log();\n console.log(\n ` ✨ ${spellIds.length} Spells → ${storeRoot}/spells/ · 🔗 ${spellIds.length * 2} client files → ~/.agents/skills (Codex, Copilot), ~/.claude/commands (Claude Code)`,\n );\n console.log();\n\n if (options.dryRun) {\n for (const component of components) {\n for (const file of component.files) printDryRun(`Would copy: ${file}`);\n }\n // The store does not exist yet in a dry run, so the shims cannot be\n // rendered from it; list the paths and flag the one thing that would\n // change the outcome -- a same-named file already there, which the real\n // run leaves alone rather than claiming.\n for (const id of spellIds) {\n for (const client of [\"codex\", \"claude\"] as const) {\n const relativePath = USER_FANOUT_PATHS[client](id);\n if (await fileExists(join(homeDir, relativePath))) {\n printDryRun(`Would leave alone (exists, not written by Arcane): ~/${relativePath}`);\n } else {\n printDryRun(`Would write: ~/${relativePath}`);\n }\n }\n }\n printDryRun(\n `Would initialize the user tier — ${spellIds.length} spells, ${spellIds.length * 2} client files`,\n );\n return;\n }\n\n const installedComponents: InstalledComponent[] = [];\n let fileCount = 0;\n for (const component of components) {\n const installedFiles: string[] = [];\n const fileHashes: Record<string, string> = {};\n for (const file of component.files) {\n const srcPath = join(assetsDir, component.sourceOverrides?.[file] ?? file);\n fileHashes[file] = await copyFile(srcPath, storeRoot, file, { force: options.force });\n installedFiles.push(file);\n fileCount++;\n }\n installedComponents.push({\n name: component.name,\n files: installedFiles,\n installedVersion: packageVersion,\n fileHashes,\n });\n }\n\n const fanout = await syncUserTierFanout({ homeDir, storeRoot, spellIds, fallbackDir: assetsDir, ownedClients: SPELL_FANOUT_CLIENTS });\n\n const manifest: ArcaneManifest = {\n version: packageVersion,\n // The tier is un-profiled; \"full\" records the profile its component set\n // is drawn from, and every command reads `scope` before `profile`.\n profile: \"full\",\n installedAt: new Date().toISOString(),\n components: installedComponents,\n scope: \"user\",\n fanout: fanout.record,\n };\n await writeManifest(storeRoot, manifest);\n\n printStep(`✨ ${fileCount} Spells installed to ${storeRoot}/spells/`);\n for (const line of describeFanoutOutcomes(fanout.outcomes)) {\n if (line.startsWith(\"!\")) printWarning(line.slice(2));\n else printStep(`🔗 ${line}`);\n }\n printSuccess(\n `Initialized the user tier — ${fileCount} spells, ${Object.keys(fanout.record).length} client files recorded`,\n );\n\n console.log();\n printSection(\"🧭 Clients\");\n console.log();\n for (const line of VSCODE_USER_TIER_NOTE) printInfo(line);\n printInfo(CLAUDE_PRECEDENCE_NOTE);\n\n console.log();\n printSection(\"🚀 Next steps\");\n console.log();\n printNextStep(1, \"Reload VS Code, and start a fresh Codex or Claude Code session, so the pickers see the new files\");\n printNextStep(2, \"`spell status --user` shows the tier; `spell update --user` refreshes it after upgrading the CLI\");\n console.log();\n}\n","import {\n mkdir,\n access,\n readdir,\n readFile,\n rm,\n rmdir,\n writeFile,\n} from \"node:fs/promises\";\nimport { createHash } from \"node:crypto\";\nimport path from \"node:path\";\n\n/**\n * CRLF and lone CR -> LF, at the byte level (no decoding, so any content is\n * handled exactly). Arcane's managed files are text, and git rewrites their\n * line endings on checkout (`text=auto eol=lf`, `core.autocrlf`); a hash that\n * saw those bytes read a file nobody had edited as an operator edit -- found\n * live in the CS-03 migration fixture (TODO.md, \"a recorded file hash is\n * byte-exact\").\n */\nfunction normalizeLineEndings(content: Buffer): Buffer {\n if (!content.includes(0x0d)) return content;\n const out = Buffer.allocUnsafe(content.length);\n let o = 0;\n for (let i = 0; i < content.length; i++) {\n const byte = content[i]!;\n if (byte === 0x0d) {\n out[o++] = 0x0a;\n if (content[i + 1] === 0x0a) i++;\n continue;\n }\n out[o++] = byte;\n }\n return out.subarray(0, o);\n}\n\n/**\n * SHA-256 hex digest of content with line endings normalized to LF -- the\n * hash `fileHashes` and the user tier's `fanout` record carry (ARC-038\n * decision 1). Two files that differ only in CRLF/LF hash the same.\n */\nexport function hashContent(content: Buffer | string): string {\n const bytes = typeof content === \"string\" ? Buffer.from(content, \"utf-8\") : content;\n return createHash(\"sha256\").update(normalizeLineEndings(bytes)).digest(\"hex\");\n}\n\n/** `hashContent` of a file on disk. */\nexport async function hashFile(filePath: string): Promise<string> {\n return hashContent(await readFile(filePath));\n}\n\n/**\n * True when a file still matches a hash Arcane recorded for it -- the\n * line-ending-normalized digest (what `hashFile`/`copyFile` record now), or\n * the raw-bytes digest (what they recorded before normalization existed), so\n * an install that predates this change is read correctly without a\n * migration: a file byte-identical or LF-identical to what was recorded is\n * \"untouched\", and only genuinely different content reads as an edit.\n */\nexport async function fileMatchesHash(filePath: string, recordedHash: string): Promise<boolean> {\n const content = await readFile(filePath);\n if (hashContent(content) === recordedHash) return true;\n return createHash(\"sha256\").update(content).digest(\"hex\") === recordedHash;\n}\n\n/**\n * Validates that relativePath stays within targetDir.\n * Throws if the resolved path escapes the target directory boundary.\n */\nexport function validateTargetPath(\n targetDir: string,\n relativePath: string,\n): void {\n const resolvedBase = path.resolve(targetDir);\n const resolvedFull = path.resolve(targetDir, relativePath);\n\n if (\n resolvedFull !== resolvedBase &&\n !resolvedFull.startsWith(resolvedBase + path.sep)\n ) {\n throw new Error(\n `Path traversal detected: \"${relativePath}\" escapes target directory \"${targetDir}\"`,\n );\n }\n}\n\n/**\n * Creates a directory and all parent directories if they don't exist.\n */\nexport async function ensureDir(dirPath: string): Promise<void> {\n await mkdir(dirPath, { recursive: true });\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Copies srcPath into targetDir at relativePath.\n * Validates path traversal before any I/O.\n * Throws if the destination already exists and opts.force is not set.\n * Returns the copied content's SHA-256 hex digest (ARC-038 decision 1) --\n * read once into memory and reused for both the hash and the write, rather\n * than hashing srcPath and then separately copying it: reading every source\n * file twice measurably slowed a full `spell init` (~90 files) once every\n * file started being hashed, enough to push some CI/test runs over their\n * timeout under contention. One read, one write; no redundant I/O.\n */\nexport async function copyFile(\n srcPath: string,\n targetDir: string,\n relativePath: string,\n opts: { force?: boolean } = {},\n): Promise<string> {\n validateTargetPath(targetDir, relativePath);\n\n const destPath = path.resolve(targetDir, relativePath);\n await ensureDir(path.dirname(destPath));\n\n if (!opts.force && (await fileExists(destPath))) {\n throw new Error(\n `Destination already exists: \"${relativePath}\". Use --force to overwrite.`,\n );\n }\n\n const content = await readFile(srcPath);\n const hash = hashContent(content);\n await writeFile(destPath, content);\n return hash;\n}\n\n/**\n * Deletes `relativePath` under `targetDir`, refusing (with the same named\n * error every write path already raises) any path that resolves outside it.\n * The paths this is called with come from `.arcane.json`, which is repository\n * content, so a manifest edited -- or generated by a hostile tool -- to track\n * `../../something` must never reach `rm` (TODO.md, \"two delete paths apply\n * manifest-controlled paths without the traversal guard\"). Missing files are\n * fine (`force`); nothing is ever removed recursively here.\n */\nexport async function removeWithin(targetDir: string, relativePath: string): Promise<void> {\n validateTargetPath(targetDir, relativePath);\n await rm(path.resolve(targetDir, relativePath), { force: true });\n}\n\n/**\n * Removes the directories a just-deleted file leaves behind, walking up from\n * its parent and stopping at the first non-empty one -- or at `targetDir`,\n * which is never removed. `rmdir` on purpose: it removes exactly an empty\n * directory and refuses a non-empty one, so a file that appears between the\n * emptiness check and the removal is never taken with it.\n *\n * Without this, pruning a component's files leaves its skeleton behind: a\n * repository that opts out of carrying spells (ARC-045 decision 4 / CS-05)\n * would keep 41 empty `.agents/skills/<id>/` directories and the empty\n * client folders above them, which git does not track and nothing else\n * cleans up.\n */\nexport async function removeEmptyAncestors(targetDir: string, relativePath: string): Promise<void> {\n validateTargetPath(targetDir, relativePath);\n const base = path.resolve(targetDir);\n let dir = path.dirname(path.resolve(targetDir, relativePath));\n while (dir !== base && dir.startsWith(base + path.sep)) {\n try {\n if ((await readdir(dir)).length > 0) return;\n await rmdir(dir);\n } catch {\n return; // already gone, unreadable, or no longer empty\n }\n dir = path.dirname(dir);\n }\n}\n\nexport interface CopiedFile {\n path: string;\n hash: string;\n}\n\n/**\n * Recursively copies a directory from srcDir to targetDir/relativeDir.\n * Validates the target path to prevent traversal attacks.\n * Returns each copied file's relative path and content hash (ARC-038\n * decision 1), for manifest tracking.\n */\nexport async function copyDirectory(\n srcDir: string,\n targetDir: string,\n relativeDir: string,\n opts: { force?: boolean } = {},\n): Promise<CopiedFile[]> {\n validateTargetPath(targetDir, relativeDir);\n\n const copiedFiles: CopiedFile[] = [];\n const entries = await readdir(srcDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcEntryPath = path.join(srcDir, entry.name);\n const relEntryPath = `${relativeDir}/${entry.name}`;\n\n if (entry.isDirectory()) {\n const subFiles = await copyDirectory(srcEntryPath, targetDir, relEntryPath, opts);\n copiedFiles.push(...subFiles);\n } else {\n const hash = await copyFile(srcEntryPath, targetDir, relEntryPath, opts);\n copiedFiles.push({ path: relEntryPath, hash });\n }\n }\n\n return copiedFiles;\n}\n\n/**\n * Every file under srcDir, as a path relative to targetDir/relativeDir with\n * forward slashes -- the list `copyDirectory` would write, without writing.\n * Lets a plan name each destination before any of them is touched.\n */\nexport async function listDirectoryFiles(srcDir: string, relativeDir: string): Promise<string[]> {\n const files: string[] = [];\n for (const entry of await readdir(srcDir, { withFileTypes: true })) {\n const rel = `${relativeDir}/${entry.name}`;\n if (entry.isDirectory()) files.push(...(await listDirectoryFiles(path.join(srcDir, entry.name), rel)));\n else files.push(rel);\n }\n return files;\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type {\n ArcaneManifest,\n ContentSensitivity,\n ExternalProvider,\n IdeaTags,\n InstallScope,\n InstalledComponent,\n Profile,\n PushPolicy,\n TrackingMode,\n} from \"../types.js\";\n\nexport class ManifestNotFoundError extends Error {\n constructor(manifestPath: string) {\n super(`Manifest not found at \"${manifestPath}\". Run \"spell init\" first.`);\n this.name = \"ManifestNotFoundError\";\n }\n}\n\nexport class ManifestCorruptError extends Error {\n constructor(manifestPath: string) {\n super(\n `Manifest at \"${manifestPath}\" contains invalid JSON. It may be corrupted.`,\n );\n this.name = \"ManifestCorruptError\";\n }\n}\n\n/**\n * Distinct from ManifestCorruptError: the file IS valid JSON, but a field\n * holds a value outside its supported enum (EF-14 point 2 -- \"reject\n * unsupported values rather than silently treating them as a provider\").\n */\nexport class ManifestInvalidFieldError extends Error {\n readonly field: string;\n constructor(manifestPath: string, field: string, value: unknown) {\n super(\n `Manifest at \"${manifestPath}\" has an unsupported value for \"${field}\": ${JSON.stringify(value)}.`,\n );\n this.name = \"ManifestInvalidFieldError\";\n this.field = field;\n }\n}\n\nconst MANIFEST_FILE = \".arcane.json\";\n\nconst VALID_TRACKING_MODES: TrackingMode[] = [\"internal\", \"external\"];\nconst VALID_EXTERNAL_PROVIDERS: ExternalProvider[] = [\"ado\", \"github\", \"jira\", \"other\"];\nconst VALID_CONTENT_SENSITIVITY: ContentSensitivity[] = [\"standard\", \"sensitive\"];\nconst VALID_PUSH_POLICIES: PushPolicy[] = [\"open\", \"guarded\", \"blocked\"];\n\n/** The enum fields' accepted values, for messages that list them (PRD D-02 flags). */\nexport const MANIFEST_ENUM_VALUES: Readonly<Record<string, readonly string[]>> = {\n tracking_mode: VALID_TRACKING_MODES,\n external_provider: VALID_EXTERNAL_PROVIDERS,\n content_sensitivity: VALID_CONTENT_SENSITIVITY,\n push_policy: VALID_PUSH_POLICIES,\n};\nconst VALID_INSTALL_SCOPES: InstallScope[] = [\"repo\", \"user\"];\n\n/**\n * True when a `fanout` key is a home-relative POSIX path that stays inside\n * the home directory: not absolute, not drive-qualified, no backslashes, no\n * `..` segment, one line. `uninstall --user` and `update --user` delete what\n * the record lists (when its hash still matches), so a key must never be\n * able to point that deletion outside the home directory -- the same\n * reasoning `isValidSubjectRoot` applies to a path handed to spells.\n */\nexport function isValidFanoutPath(file: string): boolean {\n return (\n file.length > 0 &&\n !file.startsWith(\"/\") &&\n !/^[a-zA-Z]:/.test(file) &&\n !file.includes(\"\\\\\") &&\n !file.split(\"/\").includes(\"..\") &&\n !/[\\r\\n\\0]/.test(file)\n );\n}\n\n/**\n * True when a `fanout` value is safe to store: a plain object mapping\n * home-relative paths (see isValidFanoutPath) to hex digests (CS-04). The\n * digest is what `update`/`uninstall --user` compare against before deleting\n * or rewriting a file outside the store, so a malformed entry must be\n * rejected here rather than read as \"no record\" (which would make the file\n * look unclaimed) or as a matching hash (which would make it look untouched).\n */\nexport function isValidFanoutRecord(value: unknown): value is Record<string, string> {\n return (\n isRecord(value) &&\n Object.entries(value).every(\n ([file, hash]) =>\n isValidFanoutPath(file) && typeof hash === \"string\" && /^[0-9a-f]{64}$/.test(hash),\n )\n );\n}\n\n/**\n * `subject_root` is a free-form relative path, so it gets shape rules rather\n * than an enum. It is resolved against the repository root and handed to\n * spells, so a value that escapes the repo (absolute, or containing `..`)\n * would point agents at files outside the project entirely. \".\" is explicitly\n * legal: it means the repository root IS the subject tree (EF-07).\n */\n/**\n * True when a subject_root value is safe to store. Exported so the interactive\n * prompt can reject a bad value in place rather than writing it and failing on\n * every subsequent command until the operator hand-edits .arcane.json.\n */\nexport function isValidSubjectRoot(value: string): boolean {\n const invalid =\n value.trim() === \"\" ||\n path.isAbsolute(value) ||\n // Windows drive-relative (\"C:docs\") and UNC-ish values also escape.\n /^[a-zA-Z]:/.test(value) ||\n value.startsWith(\"\\\\\\\\\") ||\n value.startsWith(\"\\\\\") ||\n value.split(/[\\\\/]/).includes(\"..\") ||\n // A stored subject_root is interpolated into agent prompts downstream, so\n // keep it a single path-shaped token rather than a multi-line payload.\n /[\\r\\n\\0]/.test(value);\n return !invalid;\n}\n\nfunction validateSubjectRoot(value: string, filePath: string): void {\n if (!isValidSubjectRoot(value)) {\n throw new ManifestInvalidFieldError(filePath, \"subject_root\", value);\n }\n}\n\n/**\n * True when a secretsScanExcludePrefixes value is safe to store: an array of\n * non-empty strings. Deliberately looser than `isValidSubjectRoot` -- a scan\n * exclusion is a path *prefix* match, not a path handed to spells, so it has\n * no traversal-escape risk to guard against; it only needs to be a real,\n * matchable string.\n */\nexport function isValidSecretsScanExcludePrefixes(value: unknown): value is string[] {\n return (\n Array.isArray(value) &&\n value.every((entry) => typeof entry === \"string\" && entry.length > 0)\n );\n}\n\nfunction validateSecretsScanExcludePrefixes(value: unknown, filePath: string): void {\n if (!isValidSecretsScanExcludePrefixes(value)) {\n throw new ManifestInvalidFieldError(filePath, \"secretsScanExcludePrefixes\", value);\n }\n}\n\n/**\n * True when an `idea_tags` value is safe to store (PRD D-10): a known mode and\n * an array of non-blank strings. Tags are interpolated into IDEAS.md entries,\n * so anything else is rejected rather than guessed at.\n */\nexport function isValidIdeaTags(value: unknown): value is IdeaTags {\n return (\n isRecord(value) &&\n (value[\"mode\"] === \"extend\" || value[\"mode\"] === \"replace\") &&\n Array.isArray(value[\"tags\"]) &&\n value[\"tags\"].every((tag) => typeof tag === \"string\" && tag.trim() !== \"\")\n );\n}\n\nfunction manifestPath(targetDir: string): string {\n return path.join(targetDir, MANIFEST_FILE);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * The field validation readManifest applies, exported so values arriving by\n * another route (the init/update flags, PRD D-02) are held to exactly the\n * same contract. Throws ManifestInvalidFieldError.\n */\nexport function validateManifestFields(manifest: Partial<ArcaneManifest>, filePath: string): void {\n if (\n manifest.tracking_mode !== undefined &&\n !VALID_TRACKING_MODES.includes(manifest.tracking_mode)\n ) {\n throw new ManifestInvalidFieldError(filePath, \"tracking_mode\", manifest.tracking_mode);\n }\n if (\n manifest.external_provider !== undefined &&\n manifest.external_provider !== null &&\n !VALID_EXTERNAL_PROVIDERS.includes(manifest.external_provider)\n ) {\n throw new ManifestInvalidFieldError(filePath, \"external_provider\", manifest.external_provider);\n }\n if (\n manifest.content_sensitivity !== undefined &&\n !VALID_CONTENT_SENSITIVITY.includes(manifest.content_sensitivity)\n ) {\n throw new ManifestInvalidFieldError(\n filePath,\n \"content_sensitivity\",\n manifest.content_sensitivity,\n );\n }\n if (\n manifest.push_policy !== undefined &&\n !VALID_PUSH_POLICIES.includes(manifest.push_policy)\n ) {\n throw new ManifestInvalidFieldError(filePath, \"push_policy\", manifest.push_policy);\n }\n if (manifest.subject_root !== undefined && manifest.subject_root !== null) {\n if (typeof manifest.subject_root !== \"string\") {\n throw new ManifestInvalidFieldError(filePath, \"subject_root\", manifest.subject_root);\n }\n validateSubjectRoot(manifest.subject_root, filePath);\n }\n if (manifest.secretsScanExcludePrefixes !== undefined) {\n validateSecretsScanExcludePrefixes(manifest.secretsScanExcludePrefixes, filePath);\n }\n if (manifest.scope !== undefined && !VALID_INSTALL_SCOPES.includes(manifest.scope)) {\n throw new ManifestInvalidFieldError(filePath, \"scope\", manifest.scope);\n }\n // ARC-045 decision 4 / CS-05. Validated rather than defaulted on a bad\n // value: a typo here would otherwise read as \"repo\" and silently reinstall\n // spells into a repository that meant to opt out.\n if (manifest.spell_scope !== undefined && !VALID_INSTALL_SCOPES.includes(manifest.spell_scope)) {\n throw new ManifestInvalidFieldError(filePath, \"spell_scope\", manifest.spell_scope);\n }\n if (manifest.fanout !== undefined && !isValidFanoutRecord(manifest.fanout)) {\n throw new ManifestInvalidFieldError(filePath, \"fanout\", manifest.fanout);\n }\n if (manifest.idea_tags !== undefined && !isValidIdeaTags(manifest.idea_tags)) {\n throw new ManifestInvalidFieldError(filePath, \"idea_tags\", manifest.idea_tags);\n }\n}\n\n/**\n * Reads .arcane.json from targetDir.\n * Throws ManifestNotFoundError if the file is missing.\n * Throws ManifestCorruptError if the file contains invalid JSON.\n * Throws ManifestInvalidFieldError if tracking_mode/external_provider hold\n * an unsupported value.\n */\nexport async function readManifest(targetDir: string): Promise<ArcaneManifest> {\n const filePath = manifestPath(targetDir);\n let content: string;\n\n try {\n content = await readFile(filePath, \"utf-8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n throw new ManifestNotFoundError(filePath);\n }\n throw err;\n }\n\n let manifest: ArcaneManifest;\n try {\n manifest = JSON.parse(content) as ArcaneManifest;\n } catch {\n throw new ManifestCorruptError(filePath);\n }\n\n validateManifestFields(manifest, filePath);\n return manifest;\n}\n\n/**\n * Resolves the effective `secretsScanExcludePrefixes` list for a target\n * directory (ARC-037). Self-hosted-aware: falls back to reading\n * src/assets/.arcane.json when there is no root .arcane.json, mirroring\n * doctor.ts's own self-hosted-manifest detection (this repo's own case --\n * it ships no root manifest, only the self-hosted marker file). Shared by\n * the build-time scan (scripts/copy-assets.ts) and `spell doctor --leaks`,\n * so a repository configures its exclusions in exactly one place regardless\n * of which path runs the scan.\n */\nexport async function resolveSecretsScanExcludePrefixes(targetDir: string): Promise<string[]> {\n try {\n const manifest = await readManifest(targetDir);\n return manifest.secretsScanExcludePrefixes ?? [];\n } catch {\n // No root manifest -- fall through to the self-hosted case below.\n }\n\n try {\n const raw = await readFile(path.join(targetDir, \"src\", \"assets\", \".arcane.json\"), \"utf-8\");\n const parsed: unknown = JSON.parse(raw);\n if (isRecord(parsed) && parsed[\"selfHosted\"] === true && parsed[\"tracking_mode\"] === \"internal\") {\n const prefixes = parsed[\"secretsScanExcludePrefixes\"];\n if (isValidSecretsScanExcludePrefixes(prefixes)) return prefixes;\n }\n } catch {\n // No self-hosted manifest either -- scan with no exclusions.\n }\n\n return [];\n}\n\n/**\n * Writes the manifest to .arcane.json in targetDir with 2-space indentation,\n * preserving the existing file's line endings and trailing-newline shape.\n *\n * On a `core.autocrlf=true` checkout the working copy is CRLF. Writing LF over\n * it leaves a file git reports as modified with no content change -- and the\n * clean-tree gate in `spell update` then refuses the next run, so Arcane\n * dirties a tree it just demanded be clean. Same class of problem as the\n * line-ending-normalized hashing in copier.ts (1.1.1), solved at the write\n * rather than at the compare.\n *\n * A manifest that does not exist yet keeps today's exact bytes: LF, no\n * trailing newline. The trailing newline is deliberately NOT added\n * unconditionally -- that would dirty every existing consumer's manifest\n * exactly once, reproducing the symptom this fixes.\n */\nexport async function writeManifest(\n targetDir: string,\n manifest: ArcaneManifest,\n): Promise<void> {\n const filePath = manifestPath(targetDir);\n\n // Advisory read: it only tells us how to format bytes we are about to\n // overwrite. Unlike readManifest above, a failure here must never fail a\n // write that would otherwise succeed, so every error degrades to the\n // new-file defaults rather than rethrowing.\n let existing = \"\";\n try {\n existing = await readFile(filePath, \"utf-8\");\n } catch {\n existing = \"\";\n }\n\n const eol = existing.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n const trailingNewline = /\\r?\\n$/.test(existing) ? eol : \"\";\n const body = JSON.stringify(manifest, null, 2).replaceAll(\"\\n\", eol);\n\n await writeFile(filePath, body + trailingNewline, \"utf-8\");\n}\n\n/**\n * Creates a new .arcane.json in targetDir with an empty components array.\n * Returns the created manifest.\n */\nexport async function createManifest(\n targetDir: string,\n profile: Profile,\n version: string,\n): Promise<ArcaneManifest> {\n const manifest: ArcaneManifest = {\n version,\n profile,\n installedAt: new Date().toISOString(),\n components: [],\n };\n await writeManifest(targetDir, manifest);\n return manifest;\n}\n\n/**\n * Pure function — returns a new manifest with the component appended.\n * Does not write to disk.\n */\nexport function addComponent(\n manifest: ArcaneManifest,\n component: InstalledComponent,\n): ArcaneManifest {\n return {\n ...manifest,\n components: [...manifest.components, component],\n };\n}\n\n/**\n * Pure function — returns a new manifest with the named component removed.\n * Does not write to disk.\n */\nexport function removeComponent(\n manifest: ArcaneManifest,\n name: string,\n): ArcaneManifest {\n return {\n ...manifest,\n components: manifest.components.filter((c) => c.name !== name),\n };\n}\n\n/**\n * Returns true if a component with the given name exists in the manifest.\n */\nexport function hasComponent(manifest: ArcaneManifest, name: string): boolean {\n return manifest.components.some((c) => c.name === name);\n}\n","/**\n * Profile configuration — the canonical source of truth for Arcane profiles.\n *\n * A profile bundles a curated set of components for a common use case.\n * Changing which components an EXISTING profile bundles requires only editing\n * this file. Adding a NEW profile id additionally touches the `Profile` union\n * in types.ts, `VALID_PROFILES` and the presentation/gating branches in\n * commands/init.ts, the --profile help string in index.ts, and the retrofit\n * mirror in modules/hub.ts -- the registry itself needs no change.\n *\n * The special value \"*\" for `components` means \"all registered components\"\n * and is expanded by the registry at runtime.\n */\n\nimport type { Profile } from \"../types.js\";\n\nexport interface ProfileConfig {\n id: Profile;\n displayName: string;\n description: string;\n /**\n * Component names included in this profile.\n * Use the special value \"*\" to mean \"all registered components\" (full install).\n */\n components: string[] | \"*\";\n}\n\nexport const PROFILE_CONFIGS: ProfileConfig[] = [\n {\n id: \"full\",\n displayName: \"Full Suite\",\n description: \"All components — spells, governance, agents, templates\",\n components: \"*\",\n },\n {\n id: \"lite\",\n displayName: \"Essentials\",\n description: \"Spells + core governance — fast start\",\n components: [\n \"spells-session\",\n \"spells-capture\",\n \"spells-delivery\",\n \"spells-review\",\n \"spells-planning\",\n \"spells-build\",\n \"spells-venture\",\n \"spells-meta\",\n \"agent-output-instructions\",\n \"git-conventions\",\n \"testing-standards\",\n \"framework-decisions\",\n \"session-continuity\",\n \"line-ending-baseline\",\n ],\n },\n {\n id: \"methodology\",\n displayName: \"Methodology Suite\",\n description: \"Spells + governance — full methodology without security/infra docs\",\n components: [\n \"spells-session\",\n \"spells-capture\",\n \"spells-delivery\",\n \"spells-review\",\n \"spells-planning\",\n \"spells-build\",\n \"spells-venture\",\n \"spells-meta\",\n \"agent-output-instructions\",\n \"git-conventions\",\n \"testing-standards\",\n \"framework-decisions\",\n \"session-continuity\",\n \"decision-documentation-standard\",\n \"agent-work-queue-model\",\n \"development-methodology\",\n \"spell-authoring-standards\",\n \"line-ending-baseline\",\n ],\n },\n {\n id: \"docs\",\n displayName: \"Docs / Records\",\n description: \"Documentation and records repositories — no code, test, or deployment workflows\",\n components: [\n // Deliberate exclusions, per docs-mode PRD MH-01: \"spells-build\"\n // (implementation, tests, stack experts, release, deployment, PRD\n // enchantment, asset tooling), \"spells-review\" (adversarial CODE review\n // -- its own workflow validates test coverage against source, which a\n // docs repo has neither of), and \"spells-venture\" (hub-only).\n //\n // MH-01 requires every retained spell to complete its core workflow\n // without source code, tests, CI, or an external tracker. Asserted --\n // exclusions included, which MH-01 requires explicitly -- in\n // test/docs-profile-registry-split.test.ts.\n \"spells-session\",\n \"spells-capture\",\n \"spells-delivery\",\n \"spells-planning\",\n \"spells-meta\",\n \"spells-docs\",\n \"docs-baseline\",\n \"line-ending-baseline\",\n \"records-conventions\",\n \"agent-output-instructions\",\n \"git-conventions\",\n // Four retained docs spells link to agent-policies; it carries no code\n // implication, so shipping it closes a dangling reference cheaply.\n \"agent-policies\",\n \"framework-decisions\",\n \"session-continuity\",\n \"decision-documentation-standard\",\n \"agent-work-queue-model\",\n \"development-methodology\",\n \"naming-conventions\",\n \"universal-agent-rules\",\n \"spell-authoring-standards\",\n ],\n },\n {\n id: \"governance-only\",\n displayName: \"Governance Only\",\n description: \"Standards docs only — no spells or agents\",\n components: [\n \"agent-output-instructions\",\n \"git-conventions\",\n \"testing-standards\",\n \"framework-decisions\",\n \"decision-documentation-standard\",\n \"agent-work-queue-model\",\n \"naming-conventions\",\n \"agent-policies\",\n \"threat-model\",\n \"hardening-checklist\",\n \"authentication-strategy\",\n \"new-business-setup\",\n \"agent-approved-paths\",\n \"portable-bootstrap\",\n \"development-methodology\",\n \"cicd-standards\",\n \"poc-management-pattern\",\n \"product-excellence-standards\",\n \"rca-process-standard\",\n \"universal-agent-rules\",\n \"spell-authoring-standards\",\n \"external-verification-standards\",\n \"web-discoverability-standards\",\n \"mobile-release-standards\",\n \"records-conventions\",\n \"compliance-standards\",\n \"line-ending-baseline\",\n ],\n },\n];\n","import type { Profile, RegistryComponent, ProfileDefinition, RegistryRetirement } from \"../types.js\";\nimport { PROFILE_CONFIGS } from \"../config/profiles.js\";\n\nexport class ComponentNotFoundError extends Error {\n constructor(name: string, available: string[]) {\n super(\n `Component \"${name}\" not found. Available components: ${available.join(\", \")}`,\n );\n this.name = \"ComponentNotFoundError\";\n }\n}\n\n// ─── Component definitions ───────────────────────────────────────────────────\n// File paths are relative to src/assets/ (source) / dist/assets/ (published).\n\nconst COMPONENTS: RegistryComponent[] = [\n // Governance\n {\n name: \"git-conventions\",\n description: \"Git branching, commit message, and PR conventions\",\n files: [\".arcane/governance/git-conventions.md\"],\n },\n {\n name: \"testing-standards\",\n description: \"Testing framework selection, coverage thresholds, and CI gates\",\n files: [\".arcane/governance/testing-standards.md\"],\n },\n {\n name: \"framework-decisions\",\n description: \"Offline reference for legacy framework ADR citations\",\n files: [\".arcane/governance/framework-decisions.md\"],\n },\n {\n name: \"decision-documentation-standard\",\n description: \"ADR format and decision-logging conventions\",\n files: [\".arcane/governance/decision-documentation-standard.md\"],\n },\n {\n name: \"agent-work-queue-model\",\n description: \"Work queue model for AI agent task assignment and tracking\",\n files: [\".arcane/governance/agent-work-queue-model.md\"],\n },\n {\n name: \"naming-conventions\",\n description: \"Machine, agent, and service naming rules (three-tier model)\",\n files: [\".arcane/governance/naming-conventions.md\"],\n },\n {\n name: \"agent-policies\",\n description: \"Agent autonomy levels, power levels, and operational constraints\",\n files: [\".arcane/governance/agent-policies.md\"],\n },\n {\n name: \"threat-model\",\n description: \"Security threat model covering STRIDE categories and mitigations\",\n files: [\".arcane/governance/threat-model.md\"],\n },\n {\n name: \"hardening-checklist\",\n description: \"Production hardening checklist for infrastructure and agents\",\n files: [\".arcane/governance/hardening-checklist.md\"],\n },\n {\n name: \"authentication-strategy\",\n description: \"Authentication patterns, token management, and identity strategy\",\n files: [\".arcane/governance/authentication-strategy.md\"],\n },\n {\n name: \"new-business-setup\",\n description: \"Step-by-step playbook for bootstrapping a new business unit\",\n files: [\".arcane/governance/new-business-setup.md\"],\n },\n {\n name: \"agent-approved-paths\",\n description: \"Approved repository paths and access boundaries for agents\",\n files: [\".arcane/governance/agent-approved-paths.md\"],\n },\n {\n name: \"portable-bootstrap\",\n description: \"Portable AI context bootstrap for onboarding new AI clients\",\n files: [\".arcane/governance/portable-bootstrap.md\"],\n },\n // Governance — methodology\n {\n name: \"development-methodology\",\n description: \"Spell Loop development methodology\",\n files: [\".arcane/governance/development-methodology.md\"],\n },\n {\n name: \"cicd-standards\",\n description: \"CI/CD pipeline standards and quality gates\",\n files: [\".arcane/governance/cicd-standards.md\"],\n },\n {\n name: \"poc-management-pattern\",\n description: \"POC lifecycle management pattern\",\n files: [\".arcane/governance/poc-management-pattern.md\"],\n },\n {\n name: \"product-excellence-standards\",\n description: \"Product quality gate standards and spell-enchant requirements\",\n files: [\".arcane/governance/product-excellence-standards.md\"],\n },\n {\n name: \"spell-authoring-standards\",\n description: \"Spell Quality Rubric — the authoring bar for spell prompts (8 dimensions)\",\n files: [\".arcane/governance/spell-authoring-standards.md\"],\n },\n {\n name: \"rca-process-standard\",\n description: \"Root cause analysis process standard\",\n files: [\".arcane/governance/rca-process-standard.md\"],\n },\n {\n name: \"universal-agent-rules\",\n description: \"Universal rules for all AI agents operating in any Arcane-managed repo\",\n files: [\".arcane/governance/universal-agent-rules.md\"],\n },\n // VS Code Copilot instructions — installed to .github/instructions/\n {\n name: \"agent-output-instructions\",\n description: \"Copilot enforcement rules: PR link format, merge strategy, and branch naming\",\n files: [\".github/instructions/agent-output.instructions.md\"],\n },\n // Repository baseline files (EF-10 + EF-17). skipExisting: these are\n // user-owned once written -- a repo with its own .gitattributes has an\n // intentional policy Arcane must not overwrite. Sources are stored under a\n // non-dotfile path and mapped via sourceOverrides; see RegistryComponent.\n {\n name: \"docs-baseline\",\n description: \"Ignore baseline for document repositories (.gitignore)\",\n files: [\".gitignore\"],\n sourceOverrides: {\n \".gitignore\": \"docs-baseline/gitignore\",\n },\n skipExisting: true,\n initOnly: true,\n },\n // Split out of docs-baseline so every profile ships it (#281, PRD D-09);\n // same asset source. See COMPONENT_FILE_MOVES for manifests written before.\n {\n name: \"line-ending-baseline\",\n description: \"Line-ending and binary-format baseline (.gitattributes): text normalizes to LF, binary formats are never rewritten\",\n files: [\".gitattributes\"],\n sourceOverrides: {\n \".gitattributes\": \"docs-baseline/gitattributes\",\n },\n skipExisting: true,\n initOnly: true,\n },\n // NOTE: the `mcp-config-template` component (`.mcp.json`, BC-22/I12) was\n // retired on 2026-10-02 -- see REGISTRY_RETIREMENTS. A root .mcp.json is\n // live MCP config that clients execute, and the `full` profile's \"*\" put\n // its example server in every full-profile repo (#328).\n {\n name: \"records-conventions\",\n description: \"Supersession, tombstone, and retention conventions for records repositories\",\n files: [\".arcane/governance/records-conventions.md\"],\n },\n {\n name: \"external-verification-standards\",\n description: \"Rules for confirming a write took effect in a system you don't control — consoles, pipelines, DNS, deployed endpoints\",\n files: [\".arcane/governance/external-verification-standards.md\"],\n },\n {\n name: \"web-discoverability-standards\",\n description: \"Search-engine and AI-assistant discoverability rules for web properties\",\n files: [\".arcane/governance/web-discoverability-standards.md\"],\n },\n {\n name: \"mobile-release-standards\",\n description: \"Durable App Store / Google Play console and release-track facts consumed by spell-eas-store-deploy\",\n files: [\".arcane/governance/mobile-release-standards.md\"],\n },\n {\n name: \"compliance-standards\",\n description: \"GDPR/CCPA/SOC 2/HIPAA obligations and a tiered applicability guide, consumed by spell-compliance\",\n files: [\".arcane/governance/compliance-standards.md\"],\n },\n // ─── Spells ────────────────────────────────────────────────────────────────\n // Split from the former monolithic `spell-prompts` + `claude-commands` pair\n // so a profile can select spells by capability (see ARC-0NN). Grouping lives\n // here and only here -- every spell file stays flat on disk; the one\n // sanctioned relocation is ARC-045's canonical move (CS-03).\n //\n // Each component carries, per spell, the canonical source and ALL THREE\n // generated client shims (ARC-045 / CS-03): the client-neutral source\n // (.arcane/spells/*.md, always listed first), the Copilot prompt shim\n // (.github/prompts/*.prompt.md), the Claude Code command stub\n // (.claude/commands/*.md), and the Codex skill (.agents/skills/*/SKILL.md,\n // CS-01). They are never independently selectable -- every profile that\n // wanted one always wanted the others -- and pairing them here makes it\n // structurally impossible for a spell's source and its shims to drift\n // apart across profiles.\n {\n name: \"spells-session\",\n description: \"Session lifecycle — open, close, commit, status, version\",\n files: [\n \".arcane/spells/spell-open-session.md\",\n \".github/prompts/spell-open-session.prompt.md\",\n \".claude/commands/spell-open-session.md\",\n \".agents/skills/spell-open-session/SKILL.md\",\n \".arcane/spells/spell-close-session.md\",\n \".github/prompts/spell-close-session.prompt.md\",\n \".claude/commands/spell-close-session.md\",\n \".agents/skills/spell-close-session/SKILL.md\",\n \".arcane/spells/spell-commit-work.md\",\n \".github/prompts/spell-commit-work.prompt.md\",\n \".claude/commands/spell-commit-work.md\",\n \".agents/skills/spell-commit-work/SKILL.md\",\n \".arcane/spells/spell-status.md\",\n \".github/prompts/spell-status.prompt.md\",\n \".claude/commands/spell-status.md\",\n \".agents/skills/spell-status/SKILL.md\",\n \".arcane/spells/spell-arcane-version.md\",\n \".github/prompts/spell-arcane-version.prompt.md\",\n \".claude/commands/spell-arcane-version.md\",\n \".agents/skills/spell-arcane-version/SKILL.md\",\n ],\n },\n {\n name: \"spells-capture\",\n description: \"Idea, todo, feedback and explanation capture\",\n files: [\n \".arcane/spells/spell-save-idea.md\",\n \".github/prompts/spell-save-idea.prompt.md\",\n \".claude/commands/spell-save-idea.md\",\n \".agents/skills/spell-save-idea/SKILL.md\",\n \".arcane/spells/spell-todo.md\",\n \".github/prompts/spell-todo.prompt.md\",\n \".claude/commands/spell-todo.md\",\n \".agents/skills/spell-todo/SKILL.md\",\n \".arcane/spells/spell-feedback.md\",\n \".github/prompts/spell-feedback.prompt.md\",\n \".claude/commands/spell-feedback.md\",\n \".agents/skills/spell-feedback/SKILL.md\",\n \".arcane/spells/spell-suggest-feature.md\",\n \".github/prompts/spell-suggest-feature.prompt.md\",\n \".claude/commands/spell-suggest-feature.md\",\n \".agents/skills/spell-suggest-feature/SKILL.md\",\n \".arcane/spells/spell-document.md\",\n \".github/prompts/spell-document.prompt.md\",\n \".claude/commands/spell-document.md\",\n \".agents/skills/spell-document/SKILL.md\",\n \".arcane/spells/spell-verification-ledger.md\",\n \".github/prompts/spell-verification-ledger.prompt.md\",\n \".claude/commands/spell-verification-ledger.md\",\n \".agents/skills/spell-verification-ledger/SKILL.md\",\n \".arcane/spells/spell-brainstorm.md\",\n \".github/prompts/spell-brainstorm.prompt.md\",\n \".claude/commands/spell-brainstorm.md\",\n \".agents/skills/spell-brainstorm/SKILL.md\",\n \".arcane/spells/spell-explain-concept.md\",\n \".github/prompts/spell-explain-concept.prompt.md\",\n \".claude/commands/spell-explain-concept.md\",\n \".agents/skills/spell-explain-concept/SKILL.md\",\n ],\n },\n {\n name: \"spells-delivery\",\n description: \"Pull-request lifecycle — open a PR, sync a stale one, respond to review feedback\",\n files: [\n \".arcane/spells/spell-create-pull-request.md\",\n \".github/prompts/spell-create-pull-request.prompt.md\",\n \".claude/commands/spell-create-pull-request.md\",\n \".agents/skills/spell-create-pull-request/SKILL.md\",\n \".arcane/spells/spell-sync-pull-request.md\",\n \".github/prompts/spell-sync-pull-request.prompt.md\",\n \".claude/commands/spell-sync-pull-request.md\",\n \".agents/skills/spell-sync-pull-request/SKILL.md\",\n \".arcane/spells/spell-address-review.md\",\n \".github/prompts/spell-address-review.prompt.md\",\n \".claude/commands/spell-address-review.md\",\n \".agents/skills/spell-address-review/SKILL.md\",\n ],\n },\n {\n name: \"spells-review\",\n description: \"Adversarial code review — requires source and tests, so excluded from docs-only profiles\",\n files: [\n \".arcane/spells/spell-review.md\",\n \".github/prompts/spell-review.prompt.md\",\n \".claude/commands/spell-review.md\",\n \".agents/skills/spell-review/SKILL.md\",\n \".arcane/spells/spell-review-batch.md\",\n \".github/prompts/spell-review-batch.prompt.md\",\n \".claude/commands/spell-review-batch.md\",\n \".agents/skills/spell-review-batch/SKILL.md\",\n ],\n },\n {\n name: \"spells-planning\",\n description: \"PRD, architecture, scoping and product review\",\n files: [\n \".arcane/spells/spell-plan.md\",\n \".github/prompts/spell-plan.prompt.md\",\n \".claude/commands/spell-plan.md\",\n \".agents/skills/spell-plan/SKILL.md\",\n \".arcane/spells/spell-architect.md\",\n \".github/prompts/spell-architect.prompt.md\",\n \".claude/commands/spell-architect.md\",\n \".agents/skills/spell-architect/SKILL.md\",\n \".arcane/spells/spell-scope.md\",\n \".github/prompts/spell-scope.prompt.md\",\n \".claude/commands/spell-scope.md\",\n \".agents/skills/spell-scope/SKILL.md\",\n \".arcane/spells/spell-product-review.md\",\n \".github/prompts/spell-product-review.prompt.md\",\n \".claude/commands/spell-product-review.md\",\n \".agents/skills/spell-product-review/SKILL.md\",\n ],\n },\n {\n name: \"spells-build\",\n description: \"Code and product delivery — implementation, tests, stack experts, release, deployment, asset tooling\",\n files: [\n \".arcane/spells/spell-implement.md\",\n \".github/prompts/spell-implement.prompt.md\",\n \".claude/commands/spell-implement.md\",\n \".agents/skills/spell-implement/SKILL.md\",\n \".arcane/spells/spell-test.md\",\n \".github/prompts/spell-test.prompt.md\",\n \".claude/commands/spell-test.md\",\n \".agents/skills/spell-test/SKILL.md\",\n \".arcane/spells/spell-full-cycle.md\",\n \".github/prompts/spell-full-cycle.prompt.md\",\n \".claude/commands/spell-full-cycle.md\",\n \".agents/skills/spell-full-cycle/SKILL.md\",\n \".arcane/spells/spell-bug.md\",\n \".github/prompts/spell-bug.prompt.md\",\n \".claude/commands/spell-bug.md\",\n \".agents/skills/spell-bug/SKILL.md\",\n \".arcane/spells/spell-bump.md\",\n \".github/prompts/spell-bump.prompt.md\",\n \".claude/commands/spell-bump.md\",\n \".agents/skills/spell-bump/SKILL.md\",\n \".arcane/spells/spell-dotnet-expert.md\",\n \".github/prompts/spell-dotnet-expert.prompt.md\",\n \".claude/commands/spell-dotnet-expert.md\",\n \".agents/skills/spell-dotnet-expert/SKILL.md\",\n \".arcane/spells/spell-security-review.md\",\n \".github/prompts/spell-security-review.prompt.md\",\n \".claude/commands/spell-security-review.md\",\n \".agents/skills/spell-security-review/SKILL.md\",\n \".arcane/spells/spell-ship.md\",\n \".github/prompts/spell-ship.prompt.md\",\n \".claude/commands/spell-ship.md\",\n \".agents/skills/spell-ship/SKILL.md\",\n \".arcane/spells/spell-enchant.md\",\n \".github/prompts/spell-enchant.prompt.md\",\n \".claude/commands/spell-enchant.md\",\n \".agents/skills/spell-enchant/SKILL.md\",\n \".arcane/spells/spell-generate-bot-icons.md\",\n \".github/prompts/spell-generate-bot-icons.prompt.md\",\n \".claude/commands/spell-generate-bot-icons.md\",\n \".agents/skills/spell-generate-bot-icons/SKILL.md\",\n \".arcane/spells/spell-make-discoverable.md\",\n \".github/prompts/spell-make-discoverable.prompt.md\",\n \".claude/commands/spell-make-discoverable.md\",\n \".agents/skills/spell-make-discoverable/SKILL.md\",\n \".arcane/spells/spell-scry.md\",\n \".github/prompts/spell-scry.prompt.md\",\n \".claude/commands/spell-scry.md\",\n \".agents/skills/spell-scry/SKILL.md\",\n \".arcane/spells/spell-eas-store-deploy.md\",\n \".github/prompts/spell-eas-store-deploy.prompt.md\",\n \".claude/commands/spell-eas-store-deploy.md\",\n \".agents/skills/spell-eas-store-deploy/SKILL.md\",\n \".arcane/spells/spell-compliance.md\",\n \".github/prompts/spell-compliance.prompt.md\",\n \".claude/commands/spell-compliance.md\",\n \".agents/skills/spell-compliance/SKILL.md\",\n ],\n // spell-make-discoverable cites the first two for every EV-/WD- rationale;\n // spell-eas-store-deploy and spell-compliance consume the last two.\n requires: [\n \"external-verification-standards\",\n \"web-discoverability-standards\",\n \"mobile-release-standards\",\n \"compliance-standards\",\n ],\n },\n {\n name: \"spells-docs\",\n description: \"Documentation and records workflows — adopt an existing document tree\",\n files: [\n \".arcane/spells/spell-adopt-docs.md\",\n \".github/prompts/spell-adopt-docs.prompt.md\",\n \".claude/commands/spell-adopt-docs.md\",\n \".agents/skills/spell-adopt-docs/SKILL.md\",\n ],\n },\n {\n name: \"spells-venture\",\n description: \"Hub-only venture management\",\n files: [\n \".arcane/spells/spell-summon-venture.md\",\n \".github/prompts/spell-summon-venture.prompt.md\",\n \".claude/commands/spell-summon-venture.md\",\n \".agents/skills/spell-summon-venture/SKILL.md\",\n \".arcane/spells/spell-manifest.md\",\n \".github/prompts/spell-manifest.prompt.md\",\n \".claude/commands/spell-manifest.md\",\n \".agents/skills/spell-manifest/SKILL.md\",\n ],\n },\n {\n name: \"spells-meta\",\n description: \"Arcane-about-Arcane — presentation and documentation drift detection\",\n files: [\n \".arcane/spells/spell-present-arcane.md\",\n \".github/prompts/spell-present-arcane.prompt.md\",\n \".claude/commands/spell-present-arcane.md\",\n \".agents/skills/spell-present-arcane/SKILL.md\",\n \".arcane/spells/spell-check-drift.md\",\n \".github/prompts/spell-check-drift.prompt.md\",\n \".claude/commands/spell-check-drift.md\",\n \".agents/skills/spell-check-drift/SKILL.md\",\n ],\n },\n // Templates\n {\n name: \"venture-template\",\n description: \"Starter template for a new business venture folder — overview plus idea/todo books\",\n files: [\n \".arcane/templates/venture-template/overview.md\",\n \".arcane/templates/venture-template/IDEAS.md\",\n \".arcane/templates/venture-template/TODO.md\",\n ],\n },\n // Session continuity — files required for spell-close-session / spell-open-session\n {\n name: \"session-continuity\",\n description: \"Install-once project orientation and session lifecycle files\",\n skipExisting: true,\n files: [\n \"README.md\",\n \"project.md\",\n \"TODO.md\",\n \"DECISIONS.md\",\n \"ai-context/system-prompt-context.md\",\n \"journal/.gitkeep\",\n ],\n },\n // Agent definitions\n // NOTE: the full portable agent system is managed by `spell agents init` / sync / list.\n // This component only installs the policy reference template for governance-only setups.\n {\n name: \"agent-definitions\",\n description: \"Agent policy reference template (for governance-only setups — use `spell agents init` for the full agent system)\",\n files: [\".arcane/agents/agent-policies-template.md\"],\n },\n // NOTE: the static `agent-files` component was retired — `.github/agents/*.agent.md`\n // files are generated per-roster by `spell agents init` / `spell agents sync`, so\n // installing a fixed-name set via `spell init` shipped stale names and collided\n // with the generated output.\n];\n\n// ─── Profile map (derived from config) ───────────────────────────────────────\n\nfunction buildProfileMap(): Record<Profile, string[]> {\n const allNames = COMPONENTS.map((c) => c.name);\n const map = {} as Record<Profile, string[]>;\n for (const config of PROFILE_CONFIGS) {\n map[config.id] = config.components === \"*\" ? allNames : config.components;\n }\n return map;\n}\n\nconst PROFILE_MAP = buildProfileMap();\n\n// ─── API ──────────────────────────────────────────────────────────────────────\n\nconst componentIndex = new Map(COMPONENTS.map((c) => [c.name, c]));\n\n/**\n * Returns a component by name.\n * Throws ComponentNotFoundError if not found.\n */\n/** Names of the capability-scoped spell components, derived not hand-listed. */\nexport const SPELL_COMPONENT_NAMES: string[] = COMPONENTS.map((c) => c.name).filter((n) =>\n n.startsWith(\"spells-\"),\n);\n\n/**\n * The exact set of components that reconstitutes what the retired monolithic\n * `spell-prompts` / `claude-commands` pair used to install.\n *\n * **Deliberately a frozen literal, not derived from `SPELL_COMPONENT_NAMES`.**\n * Deriving it would mean every future `spells-*` group silently joins this\n * list, so a legacy install would be handed brand-new spells it never had —\n * and a test asserting `migration === SPELL_COMPONENT_NAMES` would be\n * self-referential, passing while the behaviour changed underneath it. This\n * list describes history: what the old monolith contained, as of the 0.18.0\n * split. It must not grow.\n */\nconst LEGACY_MONOLITH_REPLACEMENTS = [\n \"spells-session\",\n \"spells-capture\",\n \"spells-delivery\",\n \"spells-review\",\n \"spells-planning\",\n \"spells-build\",\n \"spells-venture\",\n \"spells-meta\",\n] as const;\n\n/**\n * Components that no longer exist under their old name, mapped to the set that\n * replaces them.\n *\n * `spell-prompts` and `claude-commands` were one monolithic pair holding all 34\n * spells in each client format. Both map to the same replacements, because\n * those components now carry every format of every spell (the canonical source and its three client shims) — so a manifest\n * listing either legacy name (or both, which every profile that had one did)\n * converges on the same result. Callers must dedupe.\n *\n * Without this, `spell update` would hit `ComponentNotFoundError` for the\n * legacy names, print \"not in registry — skipping\", preserve the dead entry,\n * and silently never update that repo's spells again.\n *\n * Null-prototype so a manifest component named `constructor` or `toString`\n * can't resolve to an inherited function and blow up the lookup.\n */\nexport const LEGACY_COMPONENT_MIGRATIONS: Readonly<Record<string, readonly string[]>> =\n Object.assign(Object.create(null) as Record<string, readonly string[]>, {\n \"spell-prompts\": LEGACY_MONOLITH_REPLACEMENTS,\n \"claude-commands\": LEGACY_MONOLITH_REPLACEMENTS,\n });\n\n/**\n * Spells and components renamed or retired since they first shipped (#279,\n * PRD D-15). Report-only: `spell update` explains an entry a manifest still\n * tracks, and deletes nothing beyond what its orphan handling already does.\n * Describes history, so entries are only ever added.\n */\nexport const REGISTRY_RETIREMENTS: readonly RegistryRetirement[] = [\n {\n name: \"spell-bootstrap-business\",\n kind: \"spell\",\n successors: [\"spell-summon-venture\"],\n reason:\n \"renamed in 0.16.0 with no alias (ARC-008 precedent) — it became a hub-gated venture creator with per-venture books and a registry entry\",\n },\n {\n name: \"spell-assess\",\n kind: \"spell\",\n successors: [\"spell-scope\"],\n reason: \"renamed to name its outcome, epic scoping and sequencing (ARC-007); no alias (ARC-008)\",\n },\n {\n name: \"agent-files\",\n kind: \"component\",\n successors: [],\n reason:\n \"`.github/agents/*.agent.md` files are rendered per roster by `spell agents init` / `spell agents sync` (ARC-047), which a fixed-path component cannot do\",\n },\n {\n name: \"mcp-config-template\",\n kind: \"component\",\n successors: [],\n userOwned: true,\n reason:\n \"retired 2026-10-02 (#328): a root .mcp.json is live MCP config that clients execute, and the `full` profile installed its example server everywhere; set per-server timeouts in your own .mcp.json, which `spell doctor` checks\",\n },\n {\n name: \"spell-prompts\",\n kind: \"component\",\n successors: LEGACY_MONOLITH_REPLACEMENTS,\n reason: \"split in 0.18.0 into capability-scoped spells-* components; `spell update` migrates the entry itself\",\n },\n {\n name: \"claude-commands\",\n kind: \"component\",\n successors: LEGACY_MONOLITH_REPLACEMENTS,\n reason: \"split in 0.18.0 into capability-scoped spells-* components; `spell update` migrates the entry itself\",\n },\n];\n\n/**\n * Files that moved from one live component to another. A manifest written\n * before the move still lists them under `from`, where the registry no longer\n * ships them -- so `spell update` would call them orphans. `update` re-homes\n * them under `to` before doing anything else, keeping their recorded hashes,\n * so a file is neither orphaned nor copied a second time.\n */\nexport const COMPONENT_FILE_MOVES: readonly { from: string; to: string; files: readonly string[] }[] = [\n { from: \"docs-baseline\", to: \"line-ending-baseline\", files: [\".gitattributes\"] },\n];\n\nexport function getComponent(name: string): RegistryComponent {\n const component = componentIndex.get(name);\n if (!component) {\n throw new ComponentNotFoundError(name, Array.from(componentIndex.keys()));\n }\n return component;\n}\n\n/**\n * Returns all components for the given profile.\n */\nexport function getProfile(profile: Profile): RegistryComponent[] {\n return PROFILE_MAP[profile].map((name) => getComponent(name));\n}\n\n/**\n * Returns all registered components.\n */\nexport function getAllComponents(): RegistryComponent[] {\n return [...COMPONENTS];\n}\n\n/**\n * Returns all profile definitions with their component lists expanded.\n * Profiles are sourced from src/config/profiles.ts.\n */\nexport function listProfiles(): ProfileDefinition[] {\n const allNames = COMPONENTS.map((c) => c.name);\n return PROFILE_CONFIGS.map((config) => ({\n id: config.id,\n displayName: config.displayName,\n description: config.description,\n components:\n config.components === \"*\" ? [...allNames] : [...config.components],\n }));\n}\n","/**\n * User tier (ARC-045 decision 3 / CS-04): the per-user Arcane store at\n * `~/.arcane/` and the fan-out of generated client shims into each client's\n * home-directory discovery root.\n *\n * The store is a repo-shaped Arcane target -- it has its own `.arcane.json`\n * and is written by the same copier/manifest/hash machinery a repository is\n * -- that holds canonical spells only (`~/.arcane/spells/<id>.md`). No client\n * reads a repository-relative shim from the store, so none is installed\n * there; instead `syncUserTierFanout()` renders the two user-level shims per\n * spell with an ABSOLUTE canonical path and writes them where the clients\n * already look:\n *\n * ~/.agents/skills/<id>/SKILL.md Codex CLI/extension AND VS Code Copilot\n * ~/.claude/commands/<id>.md Claude Code (personal commands)\n *\n * Copilot is served by the Codex file on purpose: VS Code discovers agent\n * skills from `~/.agents/skills` by default (and from `~/.claude/skills`,\n * which is exactly why the Claude shim is a command and not a skill -- a\n * skill there would list every spell twice in Copilot's picker), and it has\n * deprecated the `chat.*FilesLocations` settings a printed snippet would\n * have pointed at (vendor documentation fetched 2026-09-09; see\n * features/codex-support/architecture.md, CS-04 findings). Nothing in this\n * module reads or writes VS Code settings; `VSCODE_USER_TIER_NOTE` is what\n * the commands print instead of a snippet.\n *\n * The fan-out files live outside any Arcane target directory, so they are\n * written with node:fs directly rather than through `copier.copyFile()`,\n * whose traversal guard exists precisely to forbid that for repository\n * installs -- the shape `agent-generator.ts` already uses for OpenClaw\n * workspaces under `~/.openclaw`. What was written is recorded in the store\n * manifest's `fanout` map (home-relative POSIX path -> SHA-256), and every\n * later run reconciles against that record under ARC-038's rule: a file whose\n * content no longer matches what Arcane wrote is the operator's and is never\n * overwritten or deleted; a file Arcane never recorded is not Arcane's at all\n * and is never claimed.\n */\n\nimport { lstat, mkdir, readdir, readFile, rm, rmdir, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileMatchesHash, hashContent, hashFile, validateTargetPath } from \"./copier.js\";\nimport { SPELL_COMPONENT_NAMES } from \"./registry.js\";\nimport {\n canonicalSpellPath,\n parsePromptFrontmatter,\n renderClaudeCommandStub,\n renderCodexSkill,\n} from \"./spell-compiler.js\";\nimport type { InstallScope, InstalledComponent, RegistryComponent } from \"../types.js\";\n\n// ─── Locations ────────────────────────────────────────────────────────────────\n\n/** Name of the store directory under the home directory. */\nexport const USER_TIER_DIR_NAME = \".arcane\";\n\n/** Directory inside the store that holds the canonical spells. */\nexport const USER_TIER_SPELLS_DIR = \"spells\";\n\n/**\n * The home directory, resolved on every call -- never at module load. Node's\n * `os.homedir()` follows `USERPROFILE` (Windows) / `HOME` (POSIX) at call\n * time, which is what lets tests point the whole tier at a temp directory by\n * setting the environment instead of mocking this function.\n */\nexport function resolveHomeDir(): string {\n return homedir();\n}\n\n/** The store root: `~/.arcane`. Always a direct child of the home directory. */\nexport function userTierRoot(homeDir: string = resolveHomeDir()): string {\n return join(homeDir, USER_TIER_DIR_NAME);\n}\n\n/**\n * The components the user tier installs: every capability-scoped spell\n * component, un-profiled. The tier carries spell delivery content only\n * (ARC-045 decision 5) -- governance, continuity files and repository\n * configuration stay with the repository.\n */\nexport const USER_TIER_COMPONENTS: readonly string[] = SPELL_COMPONENT_NAMES;\n\nconst CANONICAL_SPELL_PATH_PATTERN = /^\\.arcane\\/spells\\/(spell-[a-z0-9-]+)\\.md$/;\nconst STORE_SPELL_PATH_PATTERN = /^spells\\/(spell-[a-z0-9-]+)\\.md$/;\n\n/** Store-relative path of a canonical spell: `spells/<id>.md`. */\nexport function storeSpellPath(id: string): string {\n return `${USER_TIER_SPELLS_DIR}/${id}.md`;\n}\n\n/** The spell id of a registry-relative canonical path (`.arcane/spells/<id>.md`), or undefined. */\nexport function spellIdFromCanonicalPath(path: string): string | undefined {\n return path.replace(/\\\\/g, \"/\").match(CANONICAL_SPELL_PATH_PATTERN)?.[1];\n}\n\n/** The spell id of a store-relative path (`spells/<id>.md`), or undefined. */\nexport function spellIdFromStorePath(path: string): string | undefined {\n return path.replace(/\\\\/g, \"/\").match(STORE_SPELL_PATH_PATTERN)?.[1];\n}\n\n/**\n * The absolute path of a spell in the store, with forward slashes on every\n * platform -- the form Codex accepted verbatim in the CS-04 probe, valid on\n * Windows, and free of backslash escaping inside Markdown and YAML.\n */\nexport function absoluteStoreSpellPath(storeRoot: string, id: string): string {\n return resolve(storeRoot, storeSpellPath(id)).replace(/\\\\/g, \"/\");\n}\n\n// ─── The scope-aware view of the registry ─────────────────────────────────────\n\n/**\n * A component as the given scope installs it. For `\"repo\"` that is the\n * component itself. For `\"user\"` it keeps only the canonical spell files,\n * installs each at `spells/<id>.md` and maps it back to its asset through\n * `sourceOverrides` -- the mechanism the registry already uses for dotfiles\n * -- so `init`, `update` (including the ARC-038 merge, which fetches the\n * previously published file by that asset path) and `uninstall` run their\n * ordinary code over the store. The three repository-relative shims are\n * dropped: nothing reads them from the store, and the user-level shims are\n * rendered by the fan-out instead. `directories`, `skipExisting` and\n * `initOnly` are not carried because no spell component has them.\n */\nexport function componentForScope(\n component: RegistryComponent,\n scope: InstallScope,\n): RegistryComponent {\n if (scope === \"repo\") return component;\n\n const files: string[] = [];\n const sourceOverrides: Record<string, string> = {};\n for (const file of component.files) {\n const id = spellIdFromCanonicalPath(file);\n if (id === undefined) continue;\n const installed = storeSpellPath(id);\n files.push(installed);\n sourceOverrides[installed] = component.sourceOverrides?.[file] ?? file;\n }\n return { name: component.name, description: component.description, files, sourceOverrides };\n}\n\n/**\n * A component as a REPOSITORY installs it under its `spell_scope`\n * (ARC-045 decision 4 / CS-05). Identity for `\"repo\"`, the default and the\n * meaning of every manifest that omits the field. For `\"user\"` a\n * spell-delivering component installs nothing -- the repository takes its\n * spells from the machine-wide store instead -- and every other component is\n * returned untouched, because governance, instructions, continuity files,\n * hooks and templates never move tier (ARC-045 decision 5).\n *\n * Which components deliver spells is derived, never hand-listed: filtering\n * by path prefix instead would put the client-surface shapes in a fourth\n * place beside CLIENT_SHIM_PATHS, CLIENT_SHIM_PATH_PATTERNS and\n * CANONICAL_SPELLS_DIR, and would silently miss a fifth client the day one\n * is added. A test pins that the two definitions -- components named\n * `spells-*`, and components carrying a canonical or shim path -- coincide.\n *\n * Orthogonal to componentForScope above: that one answers \"what does the\n * STORE install\", this one \"what does a REPOSITORY install\". Composed only\n * in the repo scope; the store itself has no spell_scope.\n */\nexport function componentForSpellScope(\n component: RegistryComponent,\n spellScope: InstallScope,\n): RegistryComponent {\n if (spellScope === \"repo\") return component;\n if (!SPELL_COMPONENT_NAMES.includes(component.name)) return component;\n return { ...component, files: [] };\n}\n\n/**\n * A repository's effective spell scope: the manifest's field, or `\"repo\"`\n * when it is absent. One place, so no caller has to remember that absent\n * means repo.\n */\nexport function effectiveSpellScope(manifest: { spell_scope?: InstallScope }): InstallScope {\n return manifest.spell_scope ?? \"repo\";\n}\n\n/**\n * The answer `spell init` should pre-select for its spell-scope question on\n * this machine (ARC-048): the store's `default_spell_scope` when one is set,\n * `\"repo\"` otherwise.\n *\n * Deliberately narrow. This changes which answer is HIGHLIGHTED to someone who\n * is being asked and can decline; it is not a default and it never rewrites an\n * existing repository. A repository whose manifest carries no `spell_scope`\n * still means `\"repo\"` permanently, on every machine, forever -- flipping THAT\n * would retroactively change every repository that predates the field, on a\n * release chain with no step where a human looks.\n *\n * Absent store, unreadable store, or an unrecognized value all resolve to\n * `\"repo\"`: a preference that cannot be read is not a reason to change what\n * someone is offered.\n */\nexport function preferredSpellScope(manifest: { default_spell_scope?: unknown } | null): InstallScope {\n return manifest?.default_spell_scope === \"user\" ? \"user\" : \"repo\";\n}\n/** The spell ids a store manifest's components track, deduplicated and sorted. */\nexport function spellIdsInStore(components: InstalledComponent[]): string[] {\n const ids = new Set<string>();\n for (const component of components) {\n for (const file of component.files) {\n const id = spellIdFromStorePath(file);\n if (id !== undefined) ids.add(id);\n }\n }\n return [...ids].sort();\n}\n\n// ─── The fan-out ──────────────────────────────────────────────────────────────\n\n/**\n * Home-relative directory holding the user tier's rendered agent files.\n *\n * `~/.claude/agents` is read by BOTH clients: VS Code resolves it as the\n * `claude-personal` row of its custom-agent table, and Claude Code reads it as\n * its own user-scope subagent directory. One file therefore serves both, which\n * is the only arrangement that adds Claude Code without also adding a second\n * VS Code picker entry -- the four locations sit in one table with no\n * deduplication (ARC-047, amended).\n */\nexport const USER_AGENT_FANOUT_DIR = \".claude/agents\";\n\n/**\n * Where the user tier's agent files lived before ARC-047 was amended. Still\n * recognized as this client's own, so a `spell agents sync --user` after the\n * upgrade prunes the old copies instead of stranding them -- an unrecognized\n * path is treated as another command's and left alone forever.\n */\nexport const LEGACY_USER_AGENT_FANOUT_DIR = \".copilot/agents\";\n\nexport type FanoutClient = \"codex\" | \"claude\" | \"copilot-agents\";\n\n/**\n * The clients whose fan-out file is keyed by a spell id. The agent client is\n * keyed by a roster slug instead, so it has no entry in `USER_FANOUT_PATHS`.\n */\nexport type SpellFanoutClient = Exclude<FanoutClient, \"copilot-agents\">;\n\n/** Every spell-keyed client, in the order a run renders them. */\nexport const SPELL_FANOUT_CLIENTS: readonly SpellFanoutClient[] = [\"codex\", \"claude\"];\n\n/** Where each client's user-level shim goes, relative to the home directory. */\nexport const USER_FANOUT_PATHS: Readonly<Record<SpellFanoutClient, (id: string) => string>> = {\n codex: (id) => `.agents/skills/${id}/SKILL.md`,\n claude: (id) => `.claude/commands/${id}.md`,\n};\n\n/**\n * Where a user-tier agent file goes, relative to the home directory\n * (CS-06 / ARC-047 decision 3). `~/.copilot/agents` is one of the four custom\n * agent locations VS Code resolves by default, and the only one of them read\n * by VS Code alone -- `~/.claude/agents`, the other home location, is also\n * Claude Code's own user-scope subagent directory, and filling it would make\n * every rostered persona delegatable in every Claude Code session on the\n * machine. That is a new client surface rather than a tier move, so it is\n * deliberately not written here.\n *\n * A plain `.md` suffix, not `.agent.md`: inside an `agents` directory under\n * `.claude`, VS Code registers every `.md` file as an agent, and Claude Code\n * expects `.md` for a subagent. One name both accept.\n */\nexport function userAgentFanoutPath(slug: string): string {\n return `${USER_AGENT_FANOUT_DIR}/${slug}.md`;\n}\n\n/** Human-readable label per client, for command output. */\nexport const FANOUT_CLIENT_LABELS: Readonly<Record<FanoutClient, string>> = {\n codex: \"Codex/Copilot skills\",\n claude: \"Claude Code commands\",\n \"copilot-agents\": \"agent personas (VS Code + Claude Code)\",\n};\n\n/** The client a recorded fan-out path belongs to, or undefined for an unknown shape. */\nexport function clientOfFanoutPath(relativePath: string): FanoutClient | undefined {\n const normalized = relativePath.replace(/\\\\/g, \"/\");\n if (normalized.startsWith(\".agents/skills/\")) return \"codex\";\n if (normalized.startsWith(\".claude/commands/\")) return \"claude\";\n if (normalized.startsWith(`${USER_AGENT_FANOUT_DIR}/`)) return \"copilot-agents\";\n if (normalized.startsWith(`${LEGACY_USER_AGENT_FANOUT_DIR}/`)) return \"copilot-agents\";\n return undefined;\n}\n\nexport interface FanoutFile {\n /** Home-relative POSIX path. */\n relativePath: string;\n client: FanoutClient;\n content: string;\n}\n\nexport interface RenderedFanout {\n files: FanoutFile[];\n /** Spell ids whose source could be read from neither the store nor the fallback. */\n missing: string[];\n}\n\nasync function readSpellSource(\n storeRoot: string,\n id: string,\n fallbackDir: string | undefined,\n): Promise<string | undefined> {\n for (const candidate of [\n join(storeRoot, storeSpellPath(id)),\n ...(fallbackDir ? [join(fallbackDir, canonicalSpellPath(id))] : []),\n ]) {\n try {\n return await readFile(candidate, \"utf-8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n }\n return undefined;\n}\n\n/**\n * Renders the desired fan-out for the given spells: the Codex skill and the\n * Claude command per spell, each pointing at the spell's absolute path in\n * the store. The frontmatter comes from the store copy, or -- when that file\n * is missing and `fallbackDir` (the CLI's assets root) is given -- from the\n * vendor asset the next restore would write, which is what lets a dry run\n * preview the fan-out before anything has been restored. A spell with\n * neither source is reported in `missing`, never thrown on.\n */\nexport async function renderUserFanout(\n storeRoot: string,\n spellIds: string[],\n fallbackDir?: string,\n): Promise<RenderedFanout> {\n const files: FanoutFile[] = [];\n const missing: string[] = [];\n for (const id of [...spellIds].sort()) {\n const canonical = await readSpellSource(storeRoot, id, fallbackDir);\n if (canonical === undefined) {\n missing.push(id);\n continue;\n }\n const frontmatter = parsePromptFrontmatter(canonical);\n const absolutePath = absoluteStoreSpellPath(storeRoot, id);\n files.push({\n relativePath: USER_FANOUT_PATHS.codex(id),\n client: \"codex\",\n content: renderCodexSkill(id, frontmatter, absolutePath),\n });\n files.push({\n relativePath: USER_FANOUT_PATHS.claude(id),\n client: \"claude\",\n content: renderClaudeCommandStub(id, frontmatter, absolutePath),\n });\n }\n return { files, missing };\n}\n\ntype TargetState = \"absent\" | \"file\" | \"other\";\n\n/**\n * What is at a fan-out path, without following links: nothing, a regular\n * file, or something else (a directory, a symlink -- dangling or not -- a\n * device). Only a regular file is ever hashed, rewritten or deleted; anything\n * else is left exactly as found. `lstat` rather than `access`/`stat` so a\n * symlink an operator planted at the path is seen as the link, never written\n * through to wherever it points.\n */\nasync function classifyTarget(path: string): Promise<TargetState> {\n try {\n const stats = await lstat(path);\n return stats.isFile() ? \"file\" : \"other\";\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return \"absent\";\n throw err;\n }\n}\n\nexport type FanoutStatus =\n /** Created, or rewritten because the rendered content changed. */\n | \"written\"\n /** On disk, hash-matched, content current. */\n | \"unchanged\"\n /** Recorded, but the operator edited it: kept byte-untouched, recorded hash carried forward. */\n | \"customized\"\n /** A same-named file Arcane never recorded, or something that is not a regular file: kept, warned, not claimed. */\n | \"collision\"\n /** No longer desired, hash-matched: deleted (and its per-spell directory, once empty). */\n | \"pruned\"\n /** No longer desired, but edited since Arcane wrote it (or no longer a regular file): kept, still recorded. */\n | \"kept-edited\"\n /** No longer desired and already gone from disk: record dropped. */\n | \"gone\"\n /** Desired, but the spell's source is missing from the store and the fallback: nothing written or deleted, record carried forward. */\n | \"unrenderable\";\n\nexport interface FanoutOutcome {\n relativePath: string;\n status: FanoutStatus;\n}\n\nexport interface FanoutSyncResult {\n /** The record to persist as the manifest's `fanout` field. */\n record: Record<string, string>;\n outcomes: FanoutOutcome[];\n}\n\nexport interface FanoutSyncOptions {\n /** The home directory the fan-out paths are relative to (the store's parent). */\n homeDir: string;\n storeRoot: string;\n /** Spells that should have fan-out files after this run; empty on uninstall. */\n spellIds: string[];\n /** The manifest's current `fanout` record, if any. */\n previous?: Record<string, string>;\n dryRun?: boolean;\n /** The CLI's assets root: renders a spell whose store copy is missing from the vendor asset instead (see renderUserFanout). */\n fallbackDir?: string;\n /**\n * Already-rendered fan-out files to reconcile alongside the spell files --\n * the user tier's agent files, which come from a roster rather than from a\n * spell id (CS-06).\n */\n extraFiles?: readonly FanoutFile[];\n /**\n * The clients this run owns. Recorded paths belonging to any other client\n * are carried forward untouched instead of being pruned, so two commands\n * can share one `fanout` record without each erasing the other's entries:\n * `spell update --user` owns the spell clients, `spell agents sync --user`\n * owns the agent client. Defaults to every client.\n */\n ownedClients?: readonly FanoutClient[];\n}\n\n/**\n * The per-spell directory a recorded path lives in, when its shape has one\n * (`.agents/skills/<id>/SKILL.md` -> `.agents/skills/<id>`). The Claude shape\n * has none: `.claude/commands/` is the client's own directory and is never\n * removed, even when Arcane's file was the last thing in it.\n */\nfunction perSpellDirectory(homeDir: string, relativePath: string): string | undefined {\n if (clientOfFanoutPath(relativePath) !== \"codex\") return undefined;\n return dirname(join(homeDir, relativePath));\n}\n\n/**\n * Removes `dir` only when it exists and is empty; silent otherwise. `rmdir`\n * on purpose, not `rm`: it removes exactly an empty directory and refuses a\n * non-empty one, so a file that appeared between the check and the removal\n * is never taken with it.\n */\nexport async function removeDirectoryIfEmpty(dir: string): Promise<void> {\n try {\n const entries = await readdir(dir);\n if (entries.length === 0) await rmdir(dir);\n } catch {\n // Already gone, not a directory, or no longer empty -- nothing to remove.\n }\n}\n\n/**\n * Reconciles the desired fan-out against what the manifest recorded, writing\n * only what is safe to write and deleting only what is safe to delete:\n *\n * | on disk | recorded | action |\n * |-----------------------------|----------|----------------------------------------------|\n * | absent | – | write, record |\n * | present, hash == recorded | yes | rewrite only if the rendered content changed |\n * | present, hash != recorded | yes | keep, \"customized\", carry the hash forward |\n * | present | no | keep, \"collision\", never record |\n * | recorded, no longer desired | yes | delete only if hash == recorded; else keep |\n *\n * Dry-run performs no I/O beyond reading and reports the same decisions.\n */\nexport async function syncUserTierFanout(options: FanoutSyncOptions): Promise<FanoutSyncResult> {\n const rendered = await renderUserFanout(options.storeRoot, options.spellIds, options.fallbackDir);\n const desired = [...rendered.files, ...(options.extraFiles ?? [])];\n const owned = options.ownedClients;\n const ownsClient = (client: FanoutClient | undefined): boolean =>\n owned === undefined || (client !== undefined && owned.includes(client));\n const previous = options.previous ?? {};\n const dryRun = Boolean(options.dryRun);\n const record: Record<string, string> = {};\n const outcomes: FanoutOutcome[] = [];\n const desiredPaths = new Set(desired.map((f) => f.relativePath));\n\n // A spell whose source is gone from both the store and this CLI's assets\n // (a component the registry no longer ships, whose store file --prune\n // already removed) cannot be rendered. Its client files are neither\n // rewritten nor pruned -- they still point at a spell the operator may\n // restore -- and stay recorded so a later run recognizes them.\n for (const id of rendered.missing) {\n for (const client of SPELL_FANOUT_CLIENTS) {\n const relativePath = USER_FANOUT_PATHS[client](id);\n desiredPaths.add(relativePath);\n if (previous[relativePath] !== undefined) record[relativePath] = previous[relativePath];\n outcomes.push({ relativePath, status: \"unrenderable\" });\n }\n }\n\n for (const file of desired) {\n // Template-derived, so this cannot fail -- kept as the same invariant the\n // prune loop below enforces on recorded keys.\n validateTargetPath(options.homeDir, file.relativePath);\n const target = join(options.homeDir, file.relativePath);\n const recorded = previous[file.relativePath];\n const newHash = hashContent(file.content);\n const state = await classifyTarget(target);\n\n if (state === \"absent\") {\n if (!dryRun) {\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, file.content, \"utf-8\");\n }\n record[file.relativePath] = newHash;\n outcomes.push({ relativePath: file.relativePath, status: \"written\" });\n continue;\n }\n\n if (state === \"other\" || recorded === undefined) {\n // Not ours: an operator's own skill or command that happens to share\n // the spell's name, or a directory or symlink where a file would go.\n // Recording it would claim it, and `uninstall --user` deletes what the\n // record lists; writing through a symlink would land wherever it points.\n outcomes.push({ relativePath: file.relativePath, status: \"collision\" });\n continue;\n }\n\n if (!(await fileMatchesHash(target, recorded))) {\n record[file.relativePath] = recorded;\n outcomes.push({ relativePath: file.relativePath, status: \"customized\" });\n continue;\n }\n const currentHash = await hashFile(target);\n if (currentHash === newHash) {\n record[file.relativePath] = newHash;\n outcomes.push({ relativePath: file.relativePath, status: \"unchanged\" });\n continue;\n }\n if (!dryRun) await writeFile(target, file.content, \"utf-8\");\n record[file.relativePath] = newHash;\n outcomes.push({ relativePath: file.relativePath, status: \"written\" });\n }\n\n for (const [relativePath, recorded] of Object.entries(previous)) {\n if (desiredPaths.has(relativePath)) continue;\n // Another command's client: not this run's to prune, and dropping it from\n // `record` would untrack a file Arcane wrote, which `uninstall --user`\n // would then leak -- the same hazard the orphan path guards in `update`.\n if (!ownsClient(clientOfFanoutPath(relativePath))) {\n record[relativePath] = recorded;\n continue;\n }\n // Recorded keys are validated when the manifest is read\n // (isValidFanoutPath); this is the module's own guard, so a caller that\n // hands over an unvalidated record still cannot delete outside home.\n validateTargetPath(options.homeDir, relativePath);\n const target = join(options.homeDir, relativePath);\n const state = await classifyTarget(target);\n if (state === \"absent\") {\n outcomes.push({ relativePath, status: \"gone\" });\n continue;\n }\n // Not a regular file any more (a directory or a link now sits there), or\n // edited since Arcane wrote it: never deleted, still recorded.\n if (state === \"other\" || !(await fileMatchesHash(target, recorded))) {\n record[relativePath] = recorded;\n outcomes.push({ relativePath, status: \"kept-edited\" });\n continue;\n }\n if (!dryRun) {\n await rm(target, { force: true });\n const spellDir = perSpellDirectory(options.homeDir, relativePath);\n if (spellDir !== undefined) await removeDirectoryIfEmpty(spellDir);\n }\n outcomes.push({ relativePath, status: \"pruned\" });\n }\n\n return { record, outcomes };\n}\n\n/** Counts outcomes by status, for one-line summaries. */\nexport function summarizeFanout(outcomes: FanoutOutcome[]): Record<FanoutStatus, number> {\n const counts: Record<FanoutStatus, number> = {\n written: 0,\n unchanged: 0,\n customized: 0,\n collision: 0,\n pruned: 0,\n \"kept-edited\": 0,\n gone: 0,\n unrenderable: 0,\n };\n for (const outcome of outcomes) counts[outcome.status]++;\n return counts;\n}\n\n/**\n * Lines describing a sync's outcomes for command output -- one summary line\n * for what was written or pruned, then a warning block per state the\n * operator needs to know about (customized, collision, kept-edited). Files\n * that were unchanged or already gone produce no line. Pure: the commands\n * print these.\n */\nexport function describeFanoutOutcomes(outcomes: FanoutOutcome[], dryRun = false): string[] {\n const counts = summarizeFanout(outcomes);\n const lines: string[] = [];\n const prefix = dryRun ? \"[dry-run] Would \" : \"\";\n const byClient = (status: FanoutStatus): Record<FanoutClient, number> => {\n const n: Record<FanoutClient, number> = { codex: 0, claude: 0, \"copilot-agents\": 0 };\n for (const o of outcomes) {\n if (o.status !== status) continue;\n const client = clientOfFanoutPath(o.relativePath);\n if (client !== undefined) n[client]++;\n }\n return n;\n };\n const paths = (status: FanoutStatus): string =>\n outcomes\n .filter((o) => o.status === status)\n .map((o) => ` ~/${o.relativePath}`)\n .join(\"\\n\");\n\n if (counts.written > 0) {\n const n = byClient(\"written\");\n // Only the clients this run actually wrote for: a spell run would\n // otherwise report \"0 Copilot agent modes\" and an agent run \"0 Codex\n // skills, 0 Claude Code commands\", which is how a correct run came to\n // read as a broken one.\n const breakdown = (Object.keys(n) as FanoutClient[])\n .filter((client) => n[client] > 0)\n .map((client) => `${n[client]} ${FANOUT_CLIENT_LABELS[client]}`)\n .join(\", \");\n lines.push(\n `${prefix}${dryRun ? \"write\" : \"Wrote\"} ${counts.written} client file(s): ${breakdown}.`,\n );\n }\n if (counts.pruned > 0) {\n lines.push(`${prefix}${dryRun ? \"remove\" : \"Removed\"} ${counts.pruned} client file(s) no longer needed.`);\n }\n if (counts.customized > 0) {\n lines.push(\n `! ${dryRun ? \"Would keep\" : \"Kept\"} ${counts.customized} customized client file(s) untouched (edited since Arcane wrote them):\\n${paths(\"customized\")}\\n Delete one to have it regenerated by the next \\`spell update --user\\`.`,\n );\n }\n if (counts.collision > 0) {\n lines.push(\n `! ${dryRun ? \"Would leave\" : \"Left\"} ${counts.collision} existing path(s) alone (not written by Arcane — a skill or command of the same name, a directory or a link already exists there):\\n${paths(\"collision\")}\\n Move or rename it and run \\`spell update --user\\` to install Arcane's.`,\n );\n }\n if (counts.unrenderable > 0) {\n lines.push(\n `! ${dryRun ? \"Would keep\" : \"Kept\"} ${counts.unrenderable} client file(s) whose spell source is missing from the store and from this CLI (left as they are):\\n${paths(\"unrenderable\")}\\n Restore the spell with \\`spell update --user\\`, or delete these files if the spell is no longer shipped.`,\n );\n }\n if (counts[\"kept-edited\"] > 0) {\n lines.push(\n `! ${dryRun ? \"Would keep\" : \"Kept\"} ${counts[\"kept-edited\"]} edited client file(s) that Arcane no longer needs (delete them yourself if unwanted):\\n${paths(\"kept-edited\")}`,\n );\n }\n return lines;\n}\n\nexport interface FanoutHealth {\n /** Recorded files. */\n total: number;\n byClient: Record<FanoutClient, number>;\n /** Recorded files no longer on disk. */\n missing: string[];\n /** Recorded files whose content no longer matches what Arcane wrote. */\n customized: string[];\n}\n\n/**\n * Read-only view of the recorded fan-out for `status` and `doctor`: how many\n * files each client has, and which are missing or edited. Never writes.\n */\nexport async function inspectUserTierFanout(\n homeDir: string,\n record: Record<string, string> | undefined,\n): Promise<FanoutHealth> {\n const health: FanoutHealth = {\n total: 0,\n byClient: { codex: 0, claude: 0, \"copilot-agents\": 0 },\n missing: [],\n customized: [],\n };\n for (const [relativePath, recorded] of Object.entries(record ?? {})) {\n health.total++;\n const client = clientOfFanoutPath(relativePath);\n if (client !== undefined) health.byClient[client]++;\n const target = join(homeDir, relativePath);\n const state = await classifyTarget(target);\n if (state === \"absent\") {\n health.missing.push(relativePath);\n continue;\n }\n // A directory or link where Arcane's file was is \"not what Arcane wrote\",\n // the same as an edit -- and never something to hash or touch.\n if (state === \"other\" || !(await fileMatchesHash(target, recorded))) health.customized.push(relativePath);\n }\n return health;\n}\n\n/**\n * Whether `targetDir` is this machine's store, `~/.arcane`. A manifest with\n * `scope: \"user\"` found anywhere else -- a backup copy, a moved directory --\n * must not be operated on as the user tier without `--user`: the fan-out is\n * anchored at the store's parent, and that parent would be the wrong home.\n */\nexport function isUserTierStore(targetDir: string): boolean {\n return resolve(targetDir) === resolve(userTierRoot());\n}\n\n/**\n * Resolves which tier a command is operating on. `--user` is authoritative\n * (the CLI entry resolved `targetDir` to the store). Without it, a manifest\n * declaring `scope: \"user\"` is honored only when `targetDir` IS the store;\n * anywhere else the caller must refuse rather than fan out relative to the\n * wrong directory.\n */\nexport function resolveInstallScope(\n targetDir: string,\n manifestScope: InstallScope | undefined,\n userFlag: boolean | undefined,\n): { scope: InstallScope; misplacedUserManifest: boolean } {\n if (userFlag) return { scope: \"user\", misplacedUserManifest: false };\n if (manifestScope !== \"user\") return { scope: \"repo\", misplacedUserManifest: false };\n return isUserTierStore(targetDir)\n ? { scope: \"user\", misplacedUserManifest: false }\n : { scope: \"repo\", misplacedUserManifest: true };\n}\n\n/** The message a command prints before exiting when it finds a user-tier manifest outside the store. */\nexport function misplacedUserManifestMessage(targetDir: string): string {\n return (\n `This .arcane.json belongs to the user tier (scope: \"user\"), but ${targetDir} is not the store (${userTierRoot()}). ` +\n \"Run the command with --user to operate on the store, or from the store directory itself.\"\n );\n}\n\n// ─── What the commands print about VS Code ────────────────────────────────────\n\n/**\n * Printed by `init --user` and `status --user`; never applied. ARC-045\n * decision 3 asked for printed, never auto-applied VS Code guidance; the\n * guidance that turned out to be true is that no setting is needed.\n */\nexport const VSCODE_USER_TIER_NOTE: readonly string[] = [\n \"VS Code Copilot: no settings change is needed — Copilot discovers ~/.agents/skills by default\",\n \"(chat.useAgentSkills, on by default) and lists each spell under /. Reload the window after installing.\",\n \"VS Code has deprecated chat.promptFilesLocations; the user tier does not use it.\",\n];\n\n/**\n * Printed with the note: Claude Code's documented precedence rule, which is\n * the one behavior change an operator sees in repositories that still carry\n * their own shims until CS-05's repo opt-out.\n */\nexport const CLAUDE_PRECEDENCE_NOTE =\n \"Claude Code runs a personal command over a project command of the same name, so /spell-* in a repository that still carries its own shims now runs the user tier's copy.\";\n","/**\n * Spell compiler — build-time generation for spell client shims and shared\n * prose fragments (ARC-039 / BC-32, extended by ARC-045 / CS-03).\n *\n * `.arcane/spells/<id>.md` is each spell's sole authored source: client-neutral,\n * carrying every client's hint fields in one frontmatter block. Three\n * renderers produce the client surfaces from it, and none of them carries\n * prose of its own -- `renderCopilotPromptShim()` (`.github/prompts/\n * <id>.prompt.md`), `renderClaudeCommandStub()` (`.claude/commands/<id>.md`)\n * and `renderCodexSkill()` (`.agents/skills/<id>/SKILL.md`) -- the same\n * one-source/multiple-render() shape `agent-generator.ts` established,\n * applied to a second content class. `expandFragment()` inlines a named,\n * shared prose fragment into a marked span of a consuming spell, so genuinely\n * duplicated text (today: the tracking-mode declaration lines) has one edited\n * home instead of N independently-drifting copies.\n */\n\nimport { parse as parseYaml } from \"yaml\";\n\nexport interface PromptFrontmatter {\n name: string;\n description: string;\n claudeDescription?: string;\n}\n\nexport class MissingFrontmatterError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"MissingFrontmatterError\";\n }\n}\n\nexport class MalformedFragmentMarkersError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"MalformedFragmentMarkersError\";\n }\n}\n\nexport class InvalidSkillNameError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidSkillNameError\";\n }\n}\n\n// ─── The path contract (ARC-045 decisions 1-2) ────────────────────────────────\n// Everything that needs to know where a spell's authored content lives, or\n// where its generated client shims go, resolves it here -- the parity script,\n// the registry tests, and `spell update`'s migration logic alike. The shim\n// path GENERATORS (CLIENT_SHIM_PATHS) and the shim path MATCHER\n// (CLIENT_SHIM_PATH_PATTERNS) live side by side and are bound to each other\n// by test, so a renamed shape cannot drift out of the matcher unnoticed.\n\n/** Directory (relative to the assets root / a consumer's repo root) holding the canonical spell sources. */\nexport const CANONICAL_SPELLS_DIR = \".arcane/spells\";\n\n/** The one authored file for a spell: `.arcane/spells/<id>.md`. */\nexport function canonicalSpellPath(id: string): string {\n return `${CANONICAL_SPELLS_DIR}/${id}.md`;\n}\n\n/** Where each client's generated shim for a spell lives, one generator per client surface. */\nexport const CLIENT_SHIM_PATHS = {\n copilot: (id: string): string => `.github/prompts/${id}.prompt.md`,\n claude: (id: string): string => `.claude/commands/${id}.md`,\n codex: (id: string): string => `.agents/skills/${id}/SKILL.md`,\n} as const;\n\n/**\n * The three generated client-shim shapes, one per client surface -- the\n * matcher counterpart of CLIENT_SHIM_PATHS. A path matching any of these\n * carries no authored prose -- it is `render()` output over the canonical\n * file -- which is exactly why `spell update` must never three-way-merge an\n * operator's edit *into* one (see update.ts).\n */\nexport const CLIENT_SHIM_PATH_PATTERNS: readonly RegExp[] = [\n /^\\.github\\/prompts\\/spell-[a-z0-9-]+\\.prompt\\.md$/,\n /^\\.claude\\/commands\\/spell-[a-z0-9-]+\\.md$/,\n /^\\.agents\\/skills\\/spell-[a-z0-9-]+\\/SKILL\\.md$/,\n];\n\n/** True when `path` (registry-relative, either slash style) is a generated client shim. */\nexport function isClientShimPath(path: string): boolean {\n const normalized = path.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\");\n return CLIENT_SHIM_PATH_PATTERNS.some((pattern) => pattern.test(normalized));\n}\n\nconst FRONTMATTER_PATTERN = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/;\n\n/**\n * Parses a spell's YAML frontmatter block. Requires `name` and\n * `description`; `claude_description` (the Claude Code stub's own\n * proactive-invocation hint -- distinct from `description`, see\n * renderClaudeCommandStub) is optional so a spell authored before that\n * field existed still parses, falling back to `description` at render time.\n */\nexport function parsePromptFrontmatter(content: string): PromptFrontmatter {\n const match = content.match(FRONTMATTER_PATTERN);\n if (!match) {\n throw new MissingFrontmatterError(\n \"Prompt file has no YAML frontmatter block (expected a leading --- ... --- block).\",\n );\n }\n\n const raw = parseYaml(match[1]) as Record<string, unknown> | null;\n const name = raw?.[\"name\"];\n const description = raw?.[\"description\"];\n\n if (typeof name !== \"string\" || name.trim() === \"\") {\n throw new MissingFrontmatterError('Prompt frontmatter is missing a \"name\" field.');\n }\n if (typeof description !== \"string\" || description.trim() === \"\") {\n throw new MissingFrontmatterError('Prompt frontmatter is missing a \"description\" field.');\n }\n\n const claudeDescription = raw?.[\"claude_description\"];\n\n return {\n name,\n description,\n claudeDescription: typeof claudeDescription === \"string\" ? claudeDescription : undefined,\n };\n}\n\n/**\n * Returns a spell's complete frontmatter block -- the opening `---`, every\n * field line, the closing `---` and its newline -- normalized to LF. The\n * Copilot shim copies this block verbatim (see renderCopilotPromptShim), so\n * it is extracted as text rather than re-serialized from the parsed object:\n * YAML round-tripping could reorder or requote fields, and the point is that\n * Copilot sees exactly the block it saw before the canonical move.\n */\nexport function extractFrontmatterBlock(content: string): string {\n const match = content.match(FRONTMATTER_PATTERN);\n if (!match) {\n throw new MissingFrontmatterError(\n \"Prompt file has no YAML frontmatter block (expected a leading --- ... --- block).\",\n );\n }\n const block = match[0].replace(/\\r\\n/g, \"\\n\");\n return block.endsWith(\"\\n\") ? block : `${block}\\n`;\n}\n\nconst SPELL_NAME_PREFIX = /^Spell\\s*—\\s*/;\n\n/** Derives a `.claude/commands/` stub title from a prompt's `name` field (\"Spell — X\" -> \"X\"). */\nexport function deriveStubTitle(name: string): string {\n return name.replace(SPELL_NAME_PREFIX, \"\").trim();\n}\n\n/**\n * Renders the `.github/prompts/{id}.prompt.md` shim for VS Code Copilot\n * (ARC-045 decision 2 / CS-03). The frontmatter is the canonical file's own\n * block, byte-for-byte: VS Code reads `description`, `agent`, `argument-hint`\n * (and `name`, `model`, `tools`) from it, and copying the block whole means\n * the picker sees exactly the fields the full prompt carried before the\n * move. The body is one paragraph carrying both reference mechanisms VS\n * Code's prompt-file documentation admits: a relative Markdown link (resolved\n * from the prompt file's own location -- `.github/prompts/` is two levels\n * below the repo root, hence `../../`) for a client that attaches linked\n * files as context, and the explicit read-and-follow sentence proven live\n * against Codex for one that does not. Every spell runs `agent: agent`, so\n * the model always has a file-read tool for the second path.\n */\nexport function renderCopilotPromptShim(id: string, canonicalContent: string): string {\n // Validate before rendering: a canonical file the parser rejects must fail\n // the build here, not ship an unparseable shim.\n parsePromptFrontmatter(canonicalContent);\n const frontmatter = extractFrontmatterBlock(canonicalContent);\n const path = canonicalSpellPath(id);\n\n return `${frontmatter}\nThis prompt is the Arcane \\`${id}\\` spell. Read [\\`${path}\\`](../../${path}) and follow it as the complete workflow.\n`;\n}\n\n/**\n * Renders a `.claude/commands/{id}.md` thin-shim stub from its canonical\n * source's frontmatter. The stub's own `description` is a Claude\n * Code-specific proactive-invocation hint -- prefer `claudeDescription`\n * when the source provides one; only a spell with no such field yet falls\n * back to the plainer `description` (which reads correctly, just without\n * the \"use PROACTIVELY\" framing that encourages unprompted invocation).\n * The `@` include is Claude Code's own mechanism for splicing a repo-relative\n * file into the command, unchanged by CS-03 -- only its target moved.\n * `canonicalRef` defaults to the repository-relative canonical path; the\n * user tier (CS-04) passes the ABSOLUTE path of the spell in `~/.arcane`,\n * since a personal command in `~/.claude/commands/` runs from any working\n * directory and a relative path would resolve against the wrong root.\n */\nexport function renderClaudeCommandStub(\n id: string,\n frontmatter: PromptFrontmatter,\n canonicalRef: string = canonicalSpellPath(id),\n): string {\n const title = deriveStubTitle(frontmatter.name);\n const description = frontmatter.claudeDescription ?? frontmatter.description;\n const promptPath = canonicalRef;\n // Claude Code's documented form for a path with whitespace is\n // `@\"path with spaces.md\"`; the repository-relative canonical path never\n // has any, so the common case stays byte-identical to what shipped in 1.0.0.\n const includeRef = /\\s/.test(promptPath) ? `\"${promptPath}\"` : promptPath;\n\n return `---\ndescription: ${description}\n---\n\n# ${title}\n\nInvoke the Arcane \\`${id}\\` spell workflow.\n\nSee the full prompt at \\`${promptPath}\\` for the complete workflow definition.\n\n---\n\n@${includeRef}\n`;\n}\n\nconst SKILL_NAME_PATTERN = /^[a-z0-9-]+$/;\n\n/**\n * Renders a `.agents/skills/{id}/SKILL.md` for OpenAI Codex (CS-01 / ARC-039\n * extension). Unlike the Claude stub, this is not an `@include` -- Codex has\n * no such directive -- so the body is a plain-language instruction to read\n * and follow the canonical file. Live-tested against installed\n * `codex-cli 0.153.4` (2026-09-09, `docs/research/skill-discovery-smoke-tests.md`):\n * a skill body naming a bare repository-relative path is read via a real\n * shell command and its instructions followed verbatim, so this is a proven\n * mechanism, not an assumption. `canonicalPath` is `canonicalSpellPath(id)`\n * since CS-03 (it was the Copilot prompt's path under CS-01); the parameter\n * stays explicit so the rendered text and the path contract are checked\n * against each other in tests rather than assumed to agree.\n *\n * No description-length guard: Codex already degrades gracefully on an\n * overlong description (observed truncating it under a \"skills context\n * budget\" rather than erroring), so throwing our own error for something the\n * consumer already tolerates would be enforcing a limit nobody has actually\n * hit or documented.\n */\nexport function renderCodexSkill(\n id: string,\n frontmatter: PromptFrontmatter,\n canonicalPath: string,\n): string {\n if (!SKILL_NAME_PATTERN.test(id)) {\n throw new InvalidSkillNameError(\n `Skill id \"${id}\" must match ${SKILL_NAME_PATTERN} (lowercase letters, digits, hyphens only).`,\n );\n }\n const description = frontmatter.claudeDescription ?? frontmatter.description;\n\n return `---\nname: ${id}\ndescription: ${description}\n---\n\nThis skill is the Arcane \\`${id}\\` spell. Read \\`${canonicalPath}\\` and follow it as the complete workflow.\n`;\n}\n\nfunction fragmentMarkers(name: string): { start: string; end: string } {\n return {\n start: `<!-- fragment:${name}:start -->`,\n end: `<!-- fragment:${name}:end -->`,\n };\n}\n\n/**\n * Expands a named fragment into `content`'s marked span, replacing\n * whatever currently sits between `<!-- fragment:{name}:start -->` and\n * `<!-- fragment:{name}:end -->` with `fragmentContent` (the markers\n * themselves are always preserved, so the span stays re-expandable --\n * the same idempotent, always-owned-content model `merger.ts` uses for\n * CLAUDE.md's routing-table section, generalized to a *named* marker so\n * one file can host more than one distinct fragment).\n *\n * The injected lines are re-indented to match the start marker's own\n * leading whitespace, since consuming prompts nest the marker at\n * different list depths (a bare-column-0 splice would dedent a fragment\n * out of its surrounding bullet list). Author both markers at the same\n * indentation; this function only reads the start marker's.\n *\n * A file that does not reference this fragment at all is returned\n * unchanged (most consuming files reference exactly one of several\n * fragments in the library). Mismatched, reversed, or inconsistently\n * indented markers all throw -- fixing a malformed span is a decision\n * only a person should make. (LH-06b: the indentation check was added\n * after finding the START/END pair had actually drifted out of sync\n * in a real consuming file, silently re-indenting the fragment to the\n * START's column while leaving a differently-indented END marker\n * sitting in the output -- exactly the kind of malformed span this\n * function already refuses to silently paper over for the other two\n * cases, just not yet for this one.)\n */\nexport function expandFragment(content: string, name: string, fragmentContent: string): string {\n const { start, end } = fragmentMarkers(name);\n const startIdx = content.indexOf(start);\n const endIdx = content.indexOf(end);\n\n if (startIdx === -1 && endIdx === -1) return content;\n if (startIdx === -1 || endIdx === -1) {\n throw new MalformedFragmentMarkersError(\n `Mismatched fragment markers for \"${name}\": ` +\n `${startIdx === -1 ? \"start missing\" : \"start found\"}, ` +\n `${endIdx === -1 ? \"end missing\" : \"end found\"}.`,\n );\n }\n if (startIdx > endIdx) {\n throw new MalformedFragmentMarkersError(\n `Fragment markers for \"${name}\" are in the wrong order (end before start).`,\n );\n }\n\n const lineStart = content.lastIndexOf(\"\\n\", startIdx) + 1;\n const indent = content.slice(lineStart, startIdx);\n // The end marker's own indentation is part of `after`, not generated here --\n // slicing from endIdx (the marker text) rather than its line start would\n // silently drop it and dedent the closing marker to column 0.\n const endLineStart = content.lastIndexOf(\"\\n\", endIdx) + 1;\n const endIndent = content.slice(endLineStart, endIdx);\n if (endIndent !== indent) {\n throw new MalformedFragmentMarkersError(\n `Fragment markers for \"${name}\" have inconsistent indentation: ` +\n `start is indented ${JSON.stringify(indent)}, end is indented ${JSON.stringify(endIndent)}. ` +\n `Author both markers at the same indentation before re-running.`,\n );\n }\n\n const before = content.slice(0, startIdx + start.length);\n const after = content.slice(endLineStart);\n const indented = fragmentContent\n .trim()\n .split(\"\\n\")\n .map((line) => (line.length > 0 ? `${indent}${line}` : line))\n .join(\"\\n\");\n return `${before}\\n${indented}\\n${after}`;\n}\n\n/** True when `content` references the named fragment's marker span at all. */\nexport function referencesFragment(content: string, name: string): boolean {\n return content.includes(fragmentMarkers(name).start);\n}\n","import chalk from \"chalk\";\nimport gradient from \"gradient-string\";\nimport { readManifest } from \"./manifest.js\";\n\nconst TAGLINE = \"Cast spells to ship software\";\n\n// ─── Brand palette — steel blue (#6F9FD8) ────────────────────────────────────\nconst STEEL = \"#6f9fd8\";\nconst STEEL_DEEP = \"#3a6ea5\";\nconst STEEL_DIM = \"#5d6f93\";\nconst INK_LABEL = \"#c7d2ee\";\nconst FAINT = \"#2c3658\";\n\nconst STOPS = [\"#2a4d7d\", \"#4a7bb5\", \"#6f9fd8\", \"#a9c8ec\"];\nconst arcaneGradient = gradient(STOPS);\n\n// ─── ARCANE wordmark ─────────────────────────────────────────────────────────\nconst WORDMARK = [\n \" █████╗ ██████╗ ██████╗ █████╗ ███╗ ██╗███████╗\",\n \"██╔══██╗██╔══██╗██╔════╝██╔══██╗████╗ ██║██╔════╝\",\n \"███████║██████╔╝██║ ███████║██╔██╗ ██║█████╗\",\n \"██╔══██║██╔══██╗██║ ██╔══██║██║╚██╗██║██╔══╝\",\n \"██║ ██║██║ ██║╚██████╗██║ ██║██║ ╚████║███████╗\",\n \"╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝\",\n];\nconst INDENT = \" \";\nconst LOCKUP_WIDTH = INDENT.length + Math.max(...WORDMARK.map((l) => l.length));\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** True when the terminal can render the fade-in safely. */\nfunction isAnimatable(): boolean {\n return (\n !!process.stdout.isTTY &&\n process.env[\"TERM\"] !== \"dumb\" &&\n process.env[\"CI\"] !== \"true\" &&\n process.env[\"ARCANE_DISABLE_ANIMATION\"] !== \"true\" &&\n (process.stdout.columns ?? 80) >= LOCKUP_WIDTH\n );\n}\n\n/** Scale a hex color toward black by factor f (0..1) — used for the fade-in. */\nfunction scale(hex: string, f: number): string {\n const v = (i: number): number =>\n Math.max(0, Math.min(255, Math.round(parseInt(hex.slice(i, i + 2), 16) * f)));\n return \"#\" + [v(1), v(3), v(5)].map((x) => x.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/** Build the ARCANE wordmark lines at brightness f (1 = full color). */\nfunction lockupLines(f = 1): string[] {\n const g = f >= 1 ? arcaneGradient : gradient(STOPS.map((c) => scale(c, f)));\n return WORDMARK.map((line) => INDENT + g(line));\n}\n\nfunction printFooter(version: string): void {\n const taglineText = ` ${TAGLINE} · agent-native SDLC · v${version}`;\n const width = Math.max(LOCKUP_WIDTH, taglineText.length);\n const border = chalk.hex(STEEL_DEEP)(\"─\".repeat(width));\n console.log(border);\n console.log(\n chalk.hex(STEEL)(` ${TAGLINE}`),\n chalk.hex(STEEL_DIM)(`· agent-native SDLC · v${version}`),\n );\n console.log(border);\n}\n\n/**\n * Prints the ARCANE wordmark static, with tagline and version.\n * Used by subcommands (init, etc.).\n */\nexport function printBanner(version: string): void {\n console.log();\n for (const line of lockupLines()) console.log(line);\n printFooter(version);\n console.log();\n}\n\n/**\n * Headline counts for the welcome — turns the installed component file list into\n * \"N spells · N agents · N standards\" (the manifest stores registry-relative,\n * forward-slash paths). Returns [] when nothing categorizable is installed, so\n * the caller can fall back to a plain component count.\n */\nfunction installSummary(components: { files: string[] }[]): string[] {\n const files = components.flatMap((c) =>\n c.files.map((f) => f.replace(/\\\\/g, \"/\")),\n );\n const spells = new Set(\n files\n .filter((f) => /\\/(prompts|commands)\\/spell-.+\\.md$/.test(f))\n .map((f) => f.split(\"/\").pop()!.replace(/\\.prompt\\.md$|\\.md$/, \"\")),\n ).size;\n const agents = files.filter((f) => /\\/agents\\/[^/]+\\.agent\\.md$/.test(f)).length;\n const standards = files.filter((f) =>\n /\\/governance\\/[^/]+\\.md$/.test(f),\n ).length;\n const parts: string[] = [];\n if (spells) parts.push(`${spells} spell${spells === 1 ? \"\" : \"s\"}`);\n if (agents) parts.push(`${agents} agent${agents === 1 ? \"\" : \"s\"}`);\n if (standards) parts.push(`${standards} standard${standards === 1 ? \"\" : \"s\"}`);\n return parts;\n}\n\n/**\n * The bare-`spell` welcome screen: a soft fade-in of the lockup followed by a\n * single gentle pulse (strobe-safe — monotonic brightness ramps, no color\n * cycling), then real current state: whether Arcane is installed in this repo\n * (profile, components, version) and the Spell Loop. Falls back to a static\n * lockup on non-TTY / CI / narrow terminals.\n */\nexport async function printWelcome(version: string, cwd: string): Promise<void> {\n console.log();\n if (isAnimatable()) {\n const draw = (lines: string[], redraw: boolean): void => {\n if (redraw) process.stdout.write(`\\x1b[${lines.length}A`);\n for (const line of lines) process.stdout.write(`\\r\\x1b[2K${line}\\n`);\n };\n // 1. Fade in from dark — the mark materializes.\n const fade = [0.1, 0.22, 0.36, 0.52, 0.68, 0.84, 1];\n for (let i = 0; i < fade.length; i++) {\n draw(lockupLines(fade[i]!), i > 0);\n await sleep(90);\n }\n // 2. One gentle settling pulse — a single slow breath of brightness.\n const pulse = [1, 0.9, 0.82, 0.9, 1];\n for (let i = 0; i < pulse.length; i++) {\n draw(lockupLines(pulse[i]!), true);\n await sleep(140);\n }\n } else {\n for (const line of lockupLines()) console.log(line);\n }\n printFooter(version);\n console.log();\n\n const label = (s: string): string => chalk.hex(STEEL_DIM)(s.padEnd(11));\n const dot = chalk.hex(FAINT)(\"·\");\n\n let repoLine: string;\n try {\n const manifest = await readManifest(cwd);\n const parts = installSummary(manifest.components);\n const summary = parts.length\n ? parts\n : [`${manifest.components.length} components`];\n repoLine =\n `${chalk.hex(STEEL)(\"✓\")} Arcane ${chalk.hex(INK_LABEL)(`«${manifest.profile}»`)} ${dot} ` +\n summary.map((p) => chalk.hex(INK_LABEL)(p)).join(` ${dot} `) +\n ` ${dot} ${chalk.hex(STEEL_DIM)(`v${manifest.version}`)}`;\n } catch {\n repoLine =\n `${chalk.hex(STEEL_DIM)(\"○\")} not initialized ${chalk.hex(FAINT)(\"—\")} run ` +\n `${chalk.hex(STEEL).inverse(\" spell init \")} ${chalk.hex(STEEL_DIM)(\"to scaffold\")}`;\n }\n console.log(` ${label(\"This repo\")}${repoLine}`);\n\n const phases = [\"plan\", \"architect\", \"implement\", \"test\", \"review\", \"ship\"].join(\n chalk.hex(STEEL_DEEP)(\" → \"),\n );\n console.log(` ${label(\"Spell Loop\")}${chalk.hex(\"#dbe3f7\")(phases)}`);\n\n // loop-back hint under the implement → test → review cycle\n const flat = [\"plan\", \"architect\", \"implement\", \"test\", \"review\", \"ship\"].join(\" → \");\n const loopStart = 13 + flat.indexOf(\"implement\"); // col under \"implement\"\n const loopEnd = 13 + flat.indexOf(\"review\") + 5; // col under last char of \"review\"\n const inner = loopEnd - loopStart - 1;\n const lp = Math.floor((inner - 19) / 2); // 19 = visible width of \" ↺ until it passes \"\n const rp = inner - 19 - lp;\n const dash = (n: number): string => chalk.hex(STEEL_DEEP)(\"─\".repeat(Math.max(0, n)));\n console.log(\n \" \".repeat(loopStart) +\n chalk.hex(STEEL_DEEP)(\"╰\") +\n dash(lp) +\n chalk.hex(STEEL)(\" ↺ \") +\n chalk.hex(STEEL_DIM)(\"until it passes\") +\n \" \" +\n dash(rp) +\n chalk.hex(STEEL_DEEP)(\"╯\"),\n );\n console.log();\n}\n\nexport function printSuccess(message: string): void {\n console.log(`\\n${chalk.hex(STEEL)(\"✦\")} ${chalk.bold(message)}`);\n}\n\nexport function printStep(message: string): void {\n console.log(` ${chalk.hex(STEEL_DEEP)(\"▸\")} ${message}`);\n}\n\nexport function printDryRun(message: string): void {\n console.log(` ${chalk.yellow(\"◌\")} ${chalk.dim(message)}`);\n}\n\nexport function printSection(title: string): void {\n console.log(`\\n${chalk.hex(STEEL)(\"─\")} ${chalk.bold(title)}`);\n}\n\nexport function printInfo(message: string): void {\n console.log(` ${chalk.dim(message)}`);\n}\n\nexport function printNextStep(step: number, message: string): void {\n console.log(` ${chalk.hex(STEEL_DEEP)(`${step}.`)} ${message}`);\n}\n\nexport function printWarning(message: string): void {\n console.log(` ${chalk.yellow(\"⚠\")} ${chalk.yellow(message)}`);\n}\n","/**\n * `spell agents` subcommand group.\n *\n * Subcommands:\n * spell agents init — Interactive roster setup (naming strategy + role selection)\n * spell agents sync — Regenerate all client instruction files from canonical defs\n * spell agents list — Show current roster with assigned names and roles\n */\n\nimport { select, checkbox, Separator } from \"@inquirer/prompts\";\nimport { mkdir, writeFile, copyFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { stringify } from \"yaml\";\nimport type {\n AgentInitOptions,\n AgentRoster,\n AgentRosterEntry,\n AgentSyncOptions,\n NamingStrategy,\n AgentProfileId,\n} from \"../types.js\";\nimport { AGENT_ROLES } from \"../config/agent-roles.js\";\nimport {\n AGENT_PROFILE_CONFIGS,\n getAgentProfileConfig,\n} from \"../config/agent-profiles.js\";\nimport { applyNamingStrategy } from \"./naming.js\";\nimport {\n agentsBaseDir,\n loadRoster,\n projectAgentsDir,\n rosterExists,\n AgentConfigValidationError,\n AgentRosterNotFoundError,\n} from \"./agent-loader.js\";\nimport { syncAgents } from \"./agent-generator.js\";\nimport { printStep, printSuccess } from \"./banner.js\";\nimport { readManifest, writeManifest } from \"./manifest.js\";\nimport {\n effectiveSpellScope,\n describeFanoutOutcomes,\n syncUserTierFanout,\n userTierRoot,\n} from \"./user-tier.js\";\nimport type { ArcaneManifest, InstallScope } from \"../types.js\";\n\n/**\n * Where a run of `spell agents <sub>` operates: this repository, or the user\n * tier's store at `~/.arcane` (CS-06 / ARC-047 decision 2). The store is the\n * same one `spell init --user` creates -- the roster and its definition files\n * sit beside the spells, in the same two paths a repository uses.\n */\nexport function agentsTargetDir(scope: InstallScope | undefined, cwd: string): string {\n return scope === \"user\" ? userTierRoot() : cwd;\n}\n\n/**\n * The user tier's store manifest, or a message saying how to create one.\n * `spell agents --user` writes into the store `spell init --user` owns and\n * records its fan-out in that manifest, so the store has to exist first --\n * there is deliberately no second, agents-only store shape to reason about.\n */\nasync function requireUserStore(storeRoot: string): Promise<ArcaneManifest> {\n try {\n return await readManifest(storeRoot);\n } catch {\n console.error(\n `\\n ✗ No user tier at ${storeRoot}.\\n` +\n \" Run `spell init --user` once to create it, then re-run this command.\\n\",\n );\n process.exit(1);\n throw new Error(\"unreachable\");\n }\n}\n\n/**\n * The roster a run should use, and where it came from.\n *\n * A repository that has opted into the user tier and has no roster of its own\n * takes the tier's -- ARC-045 decision 6, \"the user roster is authoritative\n * for UI-visible agents when `spell_scope: \"user\"`\". Without this a repository\n * that never ran `spell agents init` gets no roster tables at all after opting\n * in, even with a perfectly good roster sitting in the store: the half that\n * removes the repository's files shipped, and the half that falls back for the\n * tables did not. A repository's own roster always wins when it has one.\n */\nexport interface ResolvedRoster {\n roster: AgentRoster;\n /** The directory its per-role definition files live in, preferred over the bundled assets. */\n definitionsDir: string;\n /** True when it came from the user tier rather than this repository. */\n fromUserTier: boolean;\n}\n\nexport async function resolveRoster(\n targetDir: string,\n scope: InstallScope,\n /** The home the store sits under. Defaults to the real one; a test points it elsewhere. */\n homeDir?: string,\n): Promise<ResolvedRoster> {\n try {\n return {\n roster: await loadRoster(targetDir, scope),\n definitionsDir: projectAgentsDir(targetDir, scope),\n fromUserTier: scope === \"user\",\n };\n } catch (err) {\n const missing = err instanceof AgentRosterNotFoundError;\n if (!missing || scope !== \"repo\" || (await repoSpellScope(targetDir)) !== \"user\") throw err;\n // Opted in, and nothing local to read: the store is the authority.\n const storeRoot = homeDir === undefined ? userTierRoot() : userTierRoot(homeDir);\n return {\n roster: await loadRoster(storeRoot, \"user\"),\n definitionsDir: projectAgentsDir(storeRoot, \"user\"),\n fromUserTier: true,\n };\n }\n}\n\n/**\n * This repository's `spell_scope`, for a repo-tier run. A repository with no\n * manifest at all is a plain directory as far as agents are concerned, and\n * takes the default.\n */\nasync function repoSpellScope(targetDir: string): Promise<InstallScope> {\n try {\n return effectiveSpellScope(await readManifest(targetDir));\n } catch {\n return \"repo\";\n }\n}\n\n/**\n * Runs `syncAgents` for either tier and finishes what the tier needs.\n *\n * A repository run is unchanged. A user-tier run gets its rendered agent\n * files reconciled through the same fan-out machinery CS-04 built for spells\n * -- hash-recorded in the store manifest, so an operator's edit to one of\n * them is recognized and never overwritten or deleted -- scoped to the agent\n * client so `spell update --user` and this command can share one record\n * without either erasing the other's entries.\n */\nasync function performAgentSync(\n targetDir: string,\n assetsDir: string,\n roster: AgentRoster,\n options: AgentSyncOptions,\n): Promise<{ synced: string[]; skipped: string[]; hasUnresolvedRoles: boolean }> {\n const scope: InstallScope = options.scope ?? \"repo\";\n const spellScope = scope === \"user\" ? undefined : await repoSpellScope(targetDir);\n const result = await syncAgents(targetDir, assetsDir, roster, {\n ...options,\n scope,\n ...(spellScope === undefined ? {} : { spellScope }),\n });\n\n if (scope === \"user\") {\n const manifest = await requireUserStore(targetDir);\n // The store root IS `<home>/.arcane` (userTierRoot), so the home the\n // fan-out paths are relative to is its parent. Derived rather than read\n // from the environment a second time: one source of truth per run, and a\n // test can point the whole thing at a temporary home by passing a store\n // root inside it.\n const fanout = await syncUserTierFanout({\n homeDir: dirname(targetDir),\n storeRoot: targetDir,\n spellIds: [],\n extraFiles: result.userAgentFiles,\n ownedClients: [\"copilot-agents\"],\n previous: manifest.fanout,\n dryRun: options.dryRun,\n fallbackDir: assetsDir,\n });\n for (const line of describeFanoutOutcomes(fanout.outcomes, options.dryRun)) {\n console.log(` ${line}`);\n }\n if (!options.dryRun) {\n await writeManifest(targetDir, { ...manifest, fanout: fanout.record });\n }\n }\n\n if (result.unmanagedAgentFiles.length > 0) {\n console.log(\n `\\n ! This repository takes its spells from the user tier (spell_scope: \"user\"), so it no\\n` +\n ` longer receives agent files of its own. ${result.unmanagedAgentFiles.length} are still on disk\\n` +\n ` from before the opt-out. Nothing here deletes them -- no component ever tracked\\n` +\n ` them, so there is no recorded hash to prove one untouched. Remove them yourself:\\n`,\n );\n for (const file of result.unmanagedAgentFiles) console.log(` ${file}`);\n console.log(`\\n git rm ${result.unmanagedAgentFiles.join(\" \")}\\n`);\n }\n\n return result;\n}\n\n\n// ─── spell agents init ────────────────────────────────────────────────────────\n\n/**\n * Interactive agent roster setup.\n *\n * 1. Prompts for agent profile (base | full | custom)\n * 2. Prompts for naming strategy (generic | random | custom)\n * 3. Applies naming and writes .arcane/agents.yaml + copies definition files\n */\nexport async function runAgentsInit(\n options: AgentInitOptions,\n targetDir: string,\n assetsDir: string,\n): Promise<void> {\n // Check for existing roster\n const scope: InstallScope = options.scope ?? \"repo\";\n // Before anything is written: the user tier's roster lives in the store\n // `spell init --user` owns, and its fan-out is recorded in that store's\n // manifest. Checking after the roster is written would leave a half-built\n // tier behind on the way to the same error.\n if (scope === \"user\") await requireUserStore(targetDir);\n const baseDir = agentsBaseDir(targetDir, scope);\n const label = scope === \"user\" ? \"~/.arcane\" : \".arcane\";\n if (!options.force && (await rosterExists(targetDir, scope))) {\n console.log(\n 'Agent roster already exists at .arcane/agents.yaml. ' +\n 'Run \"spell agents sync\" to regenerate outputs, or use --force to reinitialize.',\n );\n return;\n }\n\n console.log();\n\n // ── Step 1: Agent profile ────────────────────────────────────────────────\n let profileId: AgentProfileId = options.profile ?? \"full\";\n if (!options.profile) {\n const baseRoles = getAgentProfileConfig(\"base\").roles;\n const baseRoleNames = baseRoles.map((id) => {\n const role = AGENT_ROLES.find((r) => r.id === id);\n return role ? role.role : id;\n });\n const allCategories = [...new Set(AGENT_ROLES.map((r) => r.category))];\n\n profileId = (await select({\n message: \"Select an agent profile:\",\n choices: AGENT_PROFILE_CONFIGS.map((p) => ({\n value: p.id,\n name: `${p.id === \"base\" ? \"🎯\" : p.id === \"full\" ? \"🧙\" : \"🎨\"} ${p.displayName}`,\n description:\n p.id === \"base\"\n ? `\\n4 core roles: ${baseRoleNames.join(\", \")}`\n : p.id === \"full\"\n ? `\\nAll 12 roles across ${allCategories.join(\", \")}`\n : \"\\nPick individual roles from the full roster\",\n })),\n })) as AgentProfileId;\n }\n\n // Resolve role IDs for selected profile\n let selectedRoleIds: string[];\n if (profileId === \"custom\") {\n // Group roles by category with separators\n const categories = [...new Set(AGENT_ROLES.map((r) => r.category))];\n const choicesWithSeparators: Array<\n | { value: string; name: string; checked: boolean }\n | Separator\n > = [];\n for (const cat of categories) {\n choicesWithSeparators.push(new Separator(`── ${cat.charAt(0).toUpperCase() + cat.slice(1)} ──`));\n for (const r of AGENT_ROLES.filter((r) => r.category === cat)) {\n choicesWithSeparators.push({\n value: r.id,\n name: `${r.id} — ${r.role}`,\n checked: true,\n });\n }\n }\n\n console.log();\n selectedRoleIds = await checkbox({\n message: \"Select roles to install:\",\n choices: choicesWithSeparators,\n validate: (choices) =>\n choices.length > 0 ? true : \"Select at least one role\",\n });\n } else {\n selectedRoleIds = getAgentProfileConfig(profileId).roles;\n }\n\n // ── Step 2: Naming strategy ──────────────────────────────────────────────\n let namingStrategy: NamingStrategy = options.naming ?? \"arcanos\";\n if (!options.naming) {\n console.log();\n namingStrategy = (await select({\n message: \"Choose a naming strategy for your agents:\",\n choices: [\n {\n value: \"arcanos\" as NamingStrategy,\n name: \"🃏 Arcanos\",\n description: \"\\nPersona names: Kellar, Merlin, Lafayette, Lince, Prospero...\",\n },\n {\n value: \"generic\" as NamingStrategy,\n name: \"🏷️ Generic\",\n description: \"\\nRole-derived labels: Orchestrator, Architect, Developer, QA Lead...\",\n },\n {\n value: \"random\" as NamingStrategy,\n name: \"🎲 Random\",\n description: \"\\nCommon first names shuffled randomly: Blake, Morgan, Casey...\",\n },\n {\n value: \"custom\" as NamingStrategy,\n name: \"✏️ Custom\",\n description: \"\\nType a unique name for each role interactively\",\n },\n ],\n })) as NamingStrategy;\n }\n\n // ── Step 3: Apply naming ─────────────────────────────────────────────────\n printStep(\"Assigning names...\");\n const named = await applyNamingStrategy(namingStrategy, selectedRoleIds);\n\n // ── Step 4: Build roster ─────────────────────────────────────────────────\n const rosterEntries: AgentRosterEntry[] = named.map(({ definition, name, epithet }) => {\n // Orchestrator gets the \"main\" OpenClaw ID by convention\n const id = definition === \"orchestrator\" ? \"main\" : name.toLowerCase().replace(/\\s+/g, \"-\");\n return epithet ? { definition, name, id, epithet } : { definition, name, id };\n });\n\n const roster: AgentRoster = {\n schema_version: 2,\n naming_strategy: namingStrategy,\n agent_profile: profileId,\n openclaw: {\n enabled: true,\n workspace_root: \"~/.openclaw\",\n },\n roster: rosterEntries,\n };\n\n // ── Step 5: Write .arcane/agents.yaml ────────────────────────────────────\n const arcaneDir = join(baseDir, \"agents\");\n if (!options.dryRun) {\n await mkdir(arcaneDir, { recursive: true });\n await writeFile(join(baseDir, \"agents.yaml\"), stringify(roster), \"utf8\");\n } else {\n console.log(` [dry-run] Would write: ${label}/agents.yaml`);\n }\n\n // ── Step 6: Copy bundled definition files ─────────────────────────────────\n for (const roleId of selectedRoleIds) {\n const src = join(assetsDir, \"agents\", `${roleId}.yaml`);\n const dest = join(arcaneDir, `${roleId}.yaml`);\n if (!options.dryRun) {\n try {\n await copyFile(src, dest);\n } catch {\n console.warn(` ! Could not copy definition for \"${roleId}\" — skipping`);\n }\n } else {\n console.log(` [dry-run] Would copy: ${label}/agents/${roleId}.yaml`);\n }\n }\n\n if (options.dryRun) {\n console.log(\n `\\n [dry-run] Would initialize roster with ${selectedRoleIds.length} agents`,\n );\n return;\n }\n\n // ── Step 7: Auto-sync all clients ────────────────────────────────────────\n printStep(\"Syncing agent definitions to all clients...\");\n const { synced, skipped, hasUnresolvedRoles } = await performAgentSync(\n targetDir,\n assetsDir,\n roster,\n { dryRun: false, scope },\n );\n\n printSuccess(\"Agent roster initialized\");\n console.log(` 📋 Profile: ${profileId} · Naming: ${namingStrategy} · Agents: ${rosterEntries.length}`);\n console.log(\n ` 🔄 Synced: ${synced.length} outputs (${scope === \"user\" ? \"agent personas in ~/.claude/agents\" : \"Copilot, Claude, Codex, OpenClaw\"})`,\n );\n if (skipped.length > 0) {\n console.log(` ⚠️ Skipped: ${skipped.length} (${skipped.join(\", \")})`);\n }\n console.log();\n console.log(\n ` 💡 Customize: edit ${label}/agents.yaml → spell agents sync${scope === \"user\" ? \" --user\" : \"\"}`,\n );\n console.log();\n if (hasUnresolvedRoles) {\n console.error(\n \" ✗ One or more rostered roles could not be resolved (see \\\"Skipped\\\" above) — \" +\n \"a malformed or missing agent template must not pass silently.\",\n );\n process.exitCode = 1;\n }\n}\n\n// ─── spell agents sync ────────────────────────────────────────────────────────\n\n/** Regenerates all client instruction files from the current roster + definitions. */\nexport async function runAgentsSync(\n options: AgentSyncOptions,\n targetDir: string,\n assetsDir: string,\n): Promise<void> {\n const scope: InstallScope = options.scope ?? \"repo\";\n let roster: AgentRoster;\n let definitionsDir: string | undefined;\n let fromUserTier = false;\n try {\n ({ roster, definitionsDir, fromUserTier } = await resolveRoster(targetDir, scope));\n } catch (err) {\n if (\n err instanceof AgentRosterNotFoundError\n || err instanceof AgentConfigValidationError\n ) {\n console.error(err.message);\n process.exit(1);\n return;\n }\n throw err;\n }\n\n if (options.dryRun) {\n console.log(\"\\n [dry-run] Would sync agents from roster:\\n\");\n } else {\n console.log(\"\\n Syncing agents...\\n\");\n }\n\n if (fromUserTier && scope === \"repo\") {\n console.log(\n \" This repository has no roster of its own and takes its spells from the user tier,\" +\n \"\\n so its roster tables are built from ~/.arcane/agents.yaml.\\n\",\n );\n }\n\n const { synced, skipped, hasUnresolvedRoles } = await performAgentSync(\n targetDir,\n assetsDir,\n roster,\n { ...options, scope, ...(definitionsDir === undefined ? {} : { definitionsDir }) },\n );\n\n if (options.dryRun) {\n console.log(`\\n [dry-run] ${synced.length} outputs would be generated`);\n return;\n }\n\n console.log(`\\n ✓ Synced ${synced.length} outputs`);\n for (const item of synced) {\n console.log(` + ${item}`);\n }\n if (skipped.length > 0) {\n console.log(`\\n ! Skipped ${skipped.length}:`);\n for (const item of skipped) {\n console.log(` - ${item}`);\n }\n }\n console.log();\n if (hasUnresolvedRoles) {\n console.error(\n \" ✗ One or more rostered roles could not be resolved (see \\\"Skipped\\\" above) — \" +\n \"a malformed or missing agent template must not pass silently.\",\n );\n process.exitCode = 1;\n }\n}\n\n// ─── spell agents list ────────────────────────────────────────────────────────\n\n/** Displays the current agent roster in a table. */\nexport async function runAgentsList(\n targetDir: string,\n scope: InstallScope = \"repo\",\n): Promise<void> {\n let roster: AgentRoster;\n try {\n roster = await loadRoster(targetDir, scope);\n } catch (err) {\n if (\n err instanceof AgentRosterNotFoundError\n || err instanceof AgentConfigValidationError\n ) {\n console.error(err.message);\n process.exit(1);\n return;\n }\n throw err;\n }\n\n const nameWidth = Math.max(\n \"Name\".length,\n ...roster.roster.map((e) => (e.name ?? e.definition).length),\n );\n const defWidth = Math.max(\n \"Role ID\".length,\n ...roster.roster.map((e) => e.definition.length),\n );\n const idWidth = Math.max(\n \"OpenClaw ID\".length,\n ...roster.roster.map((e) => e.id.length),\n );\n\n const pad = (s: string, n: number) => s.padEnd(n);\n\n console.log(\n `\\n Profile: ${roster.agent_profile} | Naming: ${roster.naming_strategy} | Agents: ${roster.roster.length}\\n`,\n );\n console.log(\n ` ${pad(\"Name\", nameWidth)} ${pad(\"Role ID\", defWidth)} ${pad(\"OpenClaw ID\", idWidth)}`,\n );\n console.log(\n ` ${\"-\".repeat(nameWidth)} ${\"-\".repeat(defWidth)} ${\"-\".repeat(idWidth)}`,\n );\n for (const entry of roster.roster) {\n console.log(\n ` ${pad(entry.name ?? \"(unnamed)\", nameWidth)} ${pad(entry.definition, defWidth)} ${pad(entry.id, idWidth)}`,\n );\n }\n console.log();\n}\n","/**\n * Canonical agent role definitions.\n * Each entry maps to a YAML template file at src/assets/agents/{id}.yaml.\n * These are the 12 standard roles that ship with Arcane.\n */\n\nimport type { AgentCategory } from \"../types.js\";\n\nexport interface AgentRoleConfig {\n id: string;\n role: string;\n category: AgentCategory;\n}\n\nexport const AGENT_ROLES: AgentRoleConfig[] = [\n {\n id: \"orchestrator\",\n role: \"Product Operations Manager\",\n category: \"operations\",\n },\n {\n id: \"architecture-lead\",\n role: \"CTO / Architecture Lead\",\n category: \"engineering\",\n },\n {\n id: \"fullstack-dev\",\n role: \"Full-Stack Developer\",\n category: \"engineering\",\n },\n {\n id: \"qa-lead\",\n role: \"QA Lead\",\n category: \"quality\",\n },\n {\n id: \"devops\",\n role: \"DevOps / CI/CD Engineer\",\n category: \"engineering\",\n },\n {\n id: \"frontend-dev\",\n role: \"Frontend Developer\",\n category: \"engineering\",\n },\n {\n id: \"mobile-dev\",\n role: \"Mobile Developer\",\n category: \"engineering\",\n },\n {\n id: \"research-analyst\",\n role: \"Research & Backlog Analyst\",\n category: \"research\",\n },\n {\n id: \"marketing-strategist\",\n role: \"Marketing Strategist\",\n category: \"operations\",\n },\n {\n id: \"operations-comms\",\n role: \"Operations Communications\",\n category: \"operations\",\n },\n {\n id: \"collaborator\",\n role: \"External Collaboration Lead\",\n category: \"operations\",\n },\n {\n id: \"security-ops\",\n role: \"Security Operations\",\n category: \"quality\",\n },\n];\n\n/** Lookup by role ID. */\nexport function getAgentRole(id: string): AgentRoleConfig | undefined {\n return AGENT_ROLES.find((r) => r.id === id);\n}\n","/**\n * Agent profile configurations.\n * Controls which roles are included when running `spell agents init`.\n * Separate from src/config/profiles.ts (which governs governance/prompts components).\n */\n\nimport type { AgentProfileId } from \"../types.js\";\n\nexport interface AgentProfileConfig {\n id: AgentProfileId;\n displayName: string;\n description: string;\n /** Role IDs included in this profile. Empty means user selects interactively. */\n roles: string[];\n}\n\nexport const AGENT_PROFILE_CONFIGS: AgentProfileConfig[] = [\n {\n id: \"base\",\n displayName: \"Base\",\n description: \"Core team: orchestrator, architecture-lead, fullstack-dev, qa-lead\",\n roles: [\"orchestrator\", \"architecture-lead\", \"fullstack-dev\", \"qa-lead\"],\n },\n {\n id: \"full\",\n displayName: \"Full\",\n description: \"Complete roster — all 12 roles\",\n roles: [\n \"orchestrator\",\n \"architecture-lead\",\n \"fullstack-dev\",\n \"qa-lead\",\n \"devops\",\n \"frontend-dev\",\n \"mobile-dev\",\n \"research-analyst\",\n \"marketing-strategist\",\n \"operations-comms\",\n \"collaborator\",\n \"security-ops\",\n ],\n },\n {\n id: \"custom\",\n displayName: \"Custom\",\n description: \"Select roles interactively\",\n roles: [], // populated at runtime via checkbox prompt\n },\n];\n\n/** Lookup by profile ID. */\nexport function getAgentProfileConfig(id: AgentProfileId): AgentProfileConfig {\n const config = AGENT_PROFILE_CONFIGS.find((p) => p.id === id);\n if (!config) throw new Error(`Unknown agent profile: \"${id}\"`);\n return config;\n}\n","/**\n * Naming strategy logic for agent roster setup.\n *\n * Four strategies:\n * arcanos — the built-in persona roster (default): twelve legendary agents\n * generic — maps each role to a short, memorable generic label\n * random — assigns random common names from a curated pool\n * custom — prompts the user to type a name for each role\n */\n\nimport { input } from \"@inquirer/prompts\";\nimport type { NamingStrategy } from \"../types.js\";\nimport { getAgentRole } from \"../config/agent-roles.js\";\n\n// ─── Generic names ────────────────────────────────────────────────────────────\n\n/** Short, role-derived display names for the generic strategy. */\nconst GENERIC_NAME_MAP: Record<string, string> = {\n \"orchestrator\": \"Orchestrator\",\n \"architecture-lead\": \"Architect\",\n \"fullstack-dev\": \"Developer\",\n \"qa-lead\": \"QA Lead\",\n \"devops\": \"DevOps\",\n \"frontend-dev\": \"Frontend\",\n \"mobile-dev\": \"Mobile\",\n \"research-analyst\": \"Researcher\",\n \"marketing-strategist\": \"Marketer\",\n \"operations-comms\": \"Comms\",\n \"collaborator\": \"Collaborator\",\n \"security-ops\": \"Security\",\n};\n\n// ─── Arcanos preset names ─────────────────────────────────────────────────────\n\n/** Persona names from the Arcanos roster — twelve legendary agents. */\nconst ARCANOS_NAME_MAP: Record<string, string> = {\n \"orchestrator\": \"Kellar\",\n \"architecture-lead\": \"Merlin\",\n \"fullstack-dev\": \"Lafayette\",\n \"qa-lead\": \"Lince\",\n \"devops\": \"Prospero\",\n \"frontend-dev\": \"Adelaide\",\n \"mobile-dev\": \"Mercurio\",\n \"research-analyst\": \"Alexander\",\n \"marketing-strategist\": \"Circe\",\n \"operations-comms\": \"Bess\",\n \"collaborator\": \"Iris\",\n \"security-ops\": \"Custodio\",\n};\n\n/**\n * Arcanos epithets (naming-conventions.md's roster table) — bound to the\n * PERSONA NAME, not the role archetype: \"the Archmage\" belongs to Merlin,\n * not to \"architecture-lead\" itself. Deliberately not a field on the role\n * YAML (src/assets/agents/*.yaml), which is naming-strategy-agnostic and\n * reused by generic/random/custom naming — putting it there would force\n * Arcanos-flavored text onto strategies designed to be epithet-less (the\n * \"generic role labels\" option naming-conventions.md documents).\n */\nconst ARCANOS_EPITHET_MAP: Record<string, string> = {\n \"orchestrator\": \"the Maestro\",\n \"architecture-lead\": \"the Archmage\",\n \"fullstack-dev\": \"the Conjuror\",\n \"qa-lead\": \"the Unmasker\",\n \"devops\": \"the Stormcaller\",\n \"frontend-dev\": \"the Illusionist\",\n \"mobile-dev\": \"the Swift\",\n \"research-analyst\": \"the Man Who Knows\",\n \"marketing-strategist\": \"the Charmweaver\",\n \"operations-comms\": \"the Herald\",\n \"collaborator\": \"the Emissary\",\n \"security-ops\": \"the Warden\",\n};\n\n// ─── Random name pool ─────────────────────────────────────────────────────────\n\n/**\n * Curated pool of common single-word names.\n * Gender-neutral where possible to avoid unintentional character typecasting.\n */\nconst RANDOM_NAME_POOL: string[] = [\n \"Aiden\", \"Blake\", \"Casey\", \"Dana\", \"Ellis\", \"Finley\", \"Gray\", \"Harper\",\n \"Indigo\", \"Jordan\", \"Kai\", \"Lane\", \"Morgan\", \"Nova\", \"Owen\", \"Parker\",\n \"Quinn\", \"River\", \"Sage\", \"Taylor\", \"Uma\", \"Vale\", \"Wren\", \"Xander\",\n \"Yael\", \"Zara\", \"Avery\", \"Brett\", \"Charlie\", \"Devon\",\n];\n\n// ─── Strategy implementations ────────────────────────────────────────────────\n\nexport interface NamingResult {\n definition: string;\n name: string;\n /** Present only for the arcanos strategy — other strategies are deliberately epithet-less. */\n epithet?: string;\n}\n\n/** Assigns short generic labels derived from each role. */\nexport function applyGenericNames(roleIds: string[]): NamingResult[] {\n return roleIds.map((id) => ({\n definition: id,\n name: GENERIC_NAME_MAP[id] ?? id,\n }));\n}\n\n/** Assigns Arcanos persona names (Kellar, Merlin, Lafayette, etc.). */\nexport function applyArcanosNames(roleIds: string[]): NamingResult[] {\n return roleIds.map((id) => {\n const name = ARCANOS_NAME_MAP[id] ?? GENERIC_NAME_MAP[id] ?? id;\n const epithet = ARCANOS_NAME_MAP[id] ? ARCANOS_EPITHET_MAP[id] : undefined;\n return epithet ? { definition: id, name, epithet } : { definition: id, name };\n });\n}\n\n/**\n * Assigns random names from the pool, shuffled per call.\n * If more roles than pool entries, wraps around.\n */\nexport function applyRandomNames(roleIds: string[]): NamingResult[] {\n const shuffled = [...RANDOM_NAME_POOL].sort(() => Math.random() - 0.5);\n return roleIds.map((id, i) => ({\n definition: id,\n name: shuffled[i % shuffled.length]!,\n }));\n}\n\n/**\n * Prompts the user to type a name for each role interactively.\n * Defaults to the generic name to speed up entry.\n */\nexport async function promptCustomNames(roleIds: string[]): Promise<NamingResult[]> {\n const results: NamingResult[] = [];\n for (const id of roleIds) {\n const role = getAgentRole(id);\n const defaultName = GENERIC_NAME_MAP[id] ?? id;\n const name = await input({\n message: `Name for ${role?.role ?? id}:`,\n default: defaultName,\n validate: (v) =>\n v.trim().length > 0 ? true : \"Name cannot be empty\",\n });\n results.push({ definition: id, name: name.trim() });\n }\n return results;\n}\n\n/**\n * Applies the selected naming strategy to a list of role IDs.\n * Returns an array of definition→name pairs.\n */\nexport async function applyNamingStrategy(\n strategy: NamingStrategy,\n roleIds: string[],\n): Promise<NamingResult[]> {\n switch (strategy) {\n case \"arcanos\":\n return applyArcanosNames(roleIds);\n case \"generic\":\n return applyGenericNames(roleIds);\n case \"random\":\n return applyRandomNames(roleIds);\n case \"custom\":\n return promptCustomNames(roleIds);\n }\n}\n","/**\n * Agent definition and roster loader.\n *\n * Reads canonical agent YAML files from two locations:\n * - assetsDir/agents/ — bundled templates (used during init/add)\n * - targetDir/.arcane/agents/ — project's installed/customized definitions (used during sync)\n */\n\nimport { readFile, readdir, access } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { parse } from \"yaml\";\nimport type { AgentDefinition, AgentRoster, InstallScope } from \"../types.js\";\nimport {\n AgentConfigValidationError,\n validateAgentDefinition,\n validateAgentRoster,\n} from \"./agent-schema.js\";\n\nexport { AgentConfigValidationError } from \"./agent-schema.js\";\n\n// ─── Error types ──────────────────────────────────────────────────────────────\n\nexport class AgentDefinitionNotFoundError extends Error {\n constructor(id: string, searchDir: string) {\n super(\n `Agent definition \"${id}\" not found in \"${searchDir}\". ` +\n `Check that ${id}.yaml exists or run \"spell agents add ${id}\".`,\n );\n this.name = \"AgentDefinitionNotFoundError\";\n }\n}\n\nexport class AgentRosterNotFoundError extends Error {\n /**\n * Names the tier the caller actually asked for. A `--user` run that reports\n * the repository's path sends the operator to the wrong file and the wrong\n * command, which is worse than no message.\n */\n constructor(scope: InstallScope = \"repo\") {\n const user = scope === \"user\";\n super(\n `No agent roster found at ${user ? \"~/.arcane/agents.yaml\" : \".arcane/agents.yaml\"}. ` +\n `Run \"spell agents init${user ? \" --user\" : \"\"}\" to create one.`,\n );\n this.name = \"AgentRosterNotFoundError\";\n }\n}\n\nfunction parseYaml(content: string, source: string): unknown {\n try {\n return parse(content);\n } catch (err) {\n const detail = err instanceof Error ? err.message : \"unknown YAML parse error\";\n throw new AgentConfigValidationError(source, [`malformed YAML: ${detail}`]);\n }\n}\n\n// ─── Definition loaders ───────────────────────────────────────────────────────\n\n/**\n * Loads a single agent definition from a directory.\n * Used by `spell agents sync` (reads from .arcane/agents/).\n */\nexport async function loadAgentDefinition(\n agentsDir: string,\n id: string,\n): Promise<AgentDefinition> {\n const filePath = join(agentsDir, `${id}.yaml`);\n let content: string;\n try {\n content = await readFile(filePath, \"utf8\");\n } catch {\n throw new AgentDefinitionNotFoundError(id, agentsDir);\n }\n return validateAgentDefinition(parseYaml(content, filePath), filePath);\n}\n\n/**\n * Loads all agent definition files from a directory.\n * Skips non-YAML files and the agents.yaml roster file.\n */\nexport async function loadAllAgentDefinitions(\n agentsDir: string,\n): Promise<AgentDefinition[]> {\n let entries: string[];\n try {\n entries = await readdir(agentsDir);\n } catch {\n return [];\n }\n\n const definitions: AgentDefinition[] = [];\n for (const file of entries) {\n if (!file.endsWith(\".yaml\") || file === \"agents.yaml\") continue;\n const filePath = join(agentsDir, file);\n const content = await readFile(filePath, \"utf8\");\n definitions.push(validateAgentDefinition(parseYaml(content, filePath), filePath));\n }\n return definitions;\n}\n\n// ─── Roster loaders ───────────────────────────────────────────────────────────\n\n/**\n * Loads the agent roster from .arcane/agents.yaml in the target directory.\n * Throws AgentRosterNotFoundError if not initialized.\n */\n/**\n * The directory holding `agents.yaml` and the per-role definition files.\n *\n * A repository keeps them under its framework layer, `<repo>/.arcane`. The\n * user tier's store IS that layer -- `~/.arcane` already plays the role\n * `<repo>/.arcane` plays in a repository, which is why its spells live at\n * `~/.arcane/spells/<id>.md` and not `~/.arcane/.arcane/spells/` (CS-04's\n * store view, `componentForScope`). Agents follow the same rule.\n */\nexport function agentsBaseDir(targetDir: string, scope: InstallScope = \"repo\"): string {\n return scope === \"user\" ? targetDir : join(targetDir, \".arcane\");\n}\n\nexport async function loadRoster(\n targetDir: string,\n scope: InstallScope = \"repo\",\n): Promise<AgentRoster> {\n const rosterPath = join(agentsBaseDir(targetDir, scope), \"agents.yaml\");\n let content: string;\n try {\n content = await readFile(rosterPath, \"utf8\");\n } catch {\n throw new AgentRosterNotFoundError(scope);\n }\n return validateAgentRoster(parseYaml(content, rosterPath), rosterPath);\n}\n\n/**\n * Returns true if a roster file exists in the target directory.\n */\nexport async function rosterExists(\n targetDir: string,\n scope: InstallScope = \"repo\",\n): Promise<boolean> {\n try {\n await access(join(agentsBaseDir(targetDir, scope), \"agents.yaml\"));\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Returns the path to the project's agent definitions directory.\n */\nexport function projectAgentsDir(\n targetDir: string,\n scope: InstallScope = \"repo\",\n): string {\n return join(agentsBaseDir(targetDir, scope), \"agents\");\n}\n","import type { AgentDefinition, AgentRoster } from \"../types.js\";\n\nconst CURRENT_ROSTER_SCHEMA_VERSION = 2;\nconst POWER_LEVELS = new Set([\n \"Spectator\",\n \"Apprentice\",\n \"Wizard\",\n \"Sorcerer\",\n \"Magus\",\n \"Archmage\",\n]);\nconst AGENT_CATEGORIES = new Set([\n \"engineering\",\n \"operations\",\n \"quality\",\n \"research\",\n]);\nconst NAMING_STRATEGIES = new Set([\"arcanos\", \"generic\", \"random\", \"custom\"]);\nconst AGENT_PROFILES = new Set([\"base\", \"full\", \"custom\"]);\n\ntype RecordValue = Record<string, unknown>;\n\nexport class AgentConfigValidationError extends Error {\n constructor(source: string, issues: string[]) {\n super(`Invalid agent configuration in ${source}:\\n- ${issues.join(\"\\n- \")}`);\n this.name = \"AgentConfigValidationError\";\n }\n}\n\nfunction isRecord(value: unknown): value is RecordValue {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction requireRecord(\n value: unknown,\n path: string,\n issues: string[],\n): RecordValue | undefined {\n if (!isRecord(value)) {\n issues.push(`${path} must be an object`);\n return undefined;\n }\n return value;\n}\n\nfunction requireString(value: unknown, path: string, issues: string[]) {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n issues.push(`${path} must be a non-empty string`);\n }\n}\n\nfunction requireBoolean(value: unknown, path: string, issues: string[]) {\n if (typeof value !== \"boolean\") issues.push(`${path} must be a boolean`);\n}\n\nfunction requireStringArray(value: unknown, path: string, issues: string[]) {\n if (!Array.isArray(value) || value.some((entry) => typeof entry !== \"string\")) {\n issues.push(`${path} must be an array of strings`);\n }\n}\n\nfunction requireEnum(\n value: unknown,\n allowed: Set<string>,\n path: string,\n issues: string[],\n) {\n if (typeof value !== \"string\" || !allowed.has(value)) {\n issues.push(`${path} must be one of: ${[...allowed].join(\", \")}`);\n }\n}\n\nfunction validateOptionalClient(\n clients: RecordValue,\n key: string,\n property: string,\n expected: \"string\" | \"boolean\",\n issues: string[],\n) {\n if (clients[key] === undefined) return;\n const client = requireRecord(clients[key], `clients.${key}`, issues);\n if (!client) return;\n if (expected === \"string\") {\n requireString(client[property], `clients.${key}.${property}`, issues);\n } else {\n requireBoolean(client[property], `clients.${key}.${property}`, issues);\n }\n}\n\nexport function validateAgentDefinition(\n value: unknown,\n source: string,\n): AgentDefinition {\n const issues: string[] = [];\n const definition = requireRecord(value, \"definition\", issues);\n if (!definition) throw new AgentConfigValidationError(source, issues);\n\n requireString(definition.id, \"id\", issues);\n requireString(definition.role, \"role\", issues);\n requireEnum(definition.category, AGENT_CATEGORIES, \"category\", issues);\n\n const persona = requireRecord(definition.persona, \"persona\", issues);\n if (persona) {\n requireString(persona.description, \"persona.description\", issues);\n requireStringArray(persona.behavioral_rules, \"persona.behavioral_rules\", issues);\n for (const key of [\"personality\", \"voice\", \"visual_description\"] as const) {\n if (persona[key] !== undefined) requireString(persona[key], `persona.${key}`, issues);\n }\n if (persona.catchphrases !== undefined) {\n requireStringArray(persona.catchphrases, \"persona.catchphrases\", issues);\n }\n }\n\n const model = requireRecord(definition.model, \"model\", issues);\n if (model) {\n requireString(model.primary, \"model.primary\", issues);\n requireStringArray(model.fallback, \"model.fallback\", issues);\n }\n\n const tools = requireRecord(definition.tools, \"tools\", issues);\n if (tools) {\n requireStringArray(tools.allowed, \"tools.allowed\", issues);\n requireStringArray(tools.denied, \"tools.denied\", issues);\n }\n\n const autonomy = requireRecord(definition.autonomy, \"autonomy\", issues);\n if (autonomy) {\n requireEnum(\n autonomy.default_power_level,\n POWER_LEVELS,\n \"autonomy.default_power_level\",\n issues,\n );\n requireBoolean(autonomy.exec_allowed, \"autonomy.exec_allowed\", issues);\n }\n\n const spawn = requireRecord(definition.spawn, \"spawn\", issues);\n if (spawn) {\n requireBoolean(spawn.can_spawn, \"spawn.can_spawn\", issues);\n requireStringArray(spawn.spawnable_by, \"spawn.spawnable_by\", issues);\n }\n\n const clients = requireRecord(definition.clients, \"clients\", issues);\n if (clients) {\n validateOptionalClient(clients, \"openclaw\", \"workspace_prefix\", \"string\", issues);\n validateOptionalClient(clients, \"copilot\", \"agent_file\", \"boolean\", issues);\n validateOptionalClient(clients, \"claude\", \"include_in_instructions\", \"boolean\", issues);\n validateOptionalClient(clients, \"codex\", \"include_in_agents_md\", \"boolean\", issues);\n }\n\n if (issues.length > 0) throw new AgentConfigValidationError(source, issues);\n return definition as unknown as AgentDefinition;\n}\n\nexport function validateAgentRoster(value: unknown, source: string): AgentRoster {\n const issues: string[] = [];\n const parsed = requireRecord(value, \"roster\", issues);\n if (!parsed) throw new AgentConfigValidationError(source, issues);\n\n const version = parsed.schema_version;\n if (!Number.isInteger(version)) {\n throw new AgentConfigValidationError(source, [\n \"schema_version must be an integer; run \\\"spell agents init\\\" with a compatible Arcane CLI\",\n ]);\n }\n if ((version as number) > CURRENT_ROSTER_SCHEMA_VERSION) {\n throw new AgentConfigValidationError(source, [\n `schema_version ${version} is newer than supported version ${CURRENT_ROSTER_SCHEMA_VERSION}; upgrade Arcane before reading this roster`,\n ]);\n }\n if ((version as number) < 1) {\n throw new AgentConfigValidationError(source, [\n `schema_version ${version} is unsupported; supported versions are 1 and ${CURRENT_ROSTER_SCHEMA_VERSION}`,\n ]);\n }\n\n const roster = version === 1\n ? { ...parsed, schema_version: CURRENT_ROSTER_SCHEMA_VERSION }\n : parsed;\n requireEnum(roster.naming_strategy, NAMING_STRATEGIES, \"naming_strategy\", issues);\n requireEnum(roster.agent_profile, AGENT_PROFILES, \"agent_profile\", issues);\n\n const openclaw = requireRecord(roster.openclaw, \"openclaw\", issues);\n if (openclaw) {\n requireBoolean(openclaw.enabled, \"openclaw.enabled\", issues);\n requireString(openclaw.workspace_root, \"openclaw.workspace_root\", issues);\n }\n\n if (!Array.isArray(roster.roster)) {\n issues.push(\"roster must be an array\");\n } else {\n roster.roster.forEach((entry, index) => {\n const item = requireRecord(entry, `roster[${index}]`, issues);\n if (!item) return;\n requireString(item.definition, `roster[${index}].definition`, issues);\n if (item.name !== null) requireString(item.name, `roster[${index}].name`, issues);\n requireString(item.id, `roster[${index}].id`, issues);\n if (item.epithet !== undefined) requireString(item.epithet, `roster[${index}].epithet`, issues);\n });\n }\n\n if (issues.length > 0) throw new AgentConfigValidationError(source, issues);\n return roster as unknown as AgentRoster;\n}","/**\n * Agent generator — multi-client fan-out engine.\n *\n * Reads canonical agent definitions and an agent roster, then generates\n * client-specific instruction files for each enabled AI client:\n * - OpenClaw: IDENTITY.md, SOUL.md, TOOLS.md per agent workspace\n * - Copilot: .github/agents/{name}.agent.md per agent\n * - Claude: CLAUDE.md ← arcane marker merge\n * - Codex: AGENTS.md ← arcane marker merge\n *\n * Each L1 merge (CLAUDE.md, AGENTS.md, .github/copilot-instructions.md) also\n * injects a static intent→spell routing table ahead of the roster table,\n * independent of whether any roster entries resolved — the routing guidance\n * (\"if a spell exists for this workflow, invoke it\") applies with or without\n * agent personas configured.\n *\n * NOTE: openclaw.json is NEVER modified directly — a patch file is generated\n * at .arcane/generated/openclaw-roster.json for the user to apply manually.\n */\n\nimport { writeFile, mkdir, readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type {\n AgentDefinition,\n AgentRoster,\n AgentRosterEntry,\n AgentSyncOptions,\n} from \"../types.js\";\nimport { mergeIntoFile } from \"./merger.js\";\nimport { loadAgentDefinition, projectAgentsDir } from \"./agent-loader.js\";\nimport { userAgentFanoutPath, type FanoutFile } from \"./user-tier.js\";\n\n// ─── Resolved entry (definition + roster entry joined) ───────────────────────\n\ninterface ResolvedEntry {\n entry: AgentRosterEntry;\n def: AgentDefinition;\n displayName: string;\n}\n\n// ─── Content renderers ───────────────────────────────────────────────────────\n\nfunction renderIdentity(def: AgentDefinition, name: string): string {\n const mottosLine =\n def.persona.catchphrases && def.persona.catchphrases.length > 0\n ? `\\n## Mottos\\n\\n${def.persona.catchphrases.map((c) => `- \"${c}\"`).join(\"\\n\")}\\n`\n : \"\";\n return `# ${name} — ${def.role}\n\n## Role\n\n${def.persona.description.trim()}\n\n## Category\n\n${def.category}\n${mottosLine}`;\n}\n\nfunction renderSoul(def: AgentDefinition, name: string): string {\n const rules = def.persona.behavioral_rules.map((r) => `- ${r}`).join(\"\\n\");\n return `# ${name} — Behavioral Directives\n\n## Rules\n\n${rules}\n\n## Autonomy\n\n- **Default Power Level:** ${def.autonomy.default_power_level}\n- **Exec Allowed:** ${def.autonomy.exec_allowed ? \"Yes\" : \"No\"}\n`;\n}\n\nfunction renderTools(def: AgentDefinition, name: string): string {\n const allowed = def.tools.allowed.map((t) => `- \\`${t}\\``).join(\"\\n\");\n const denied = def.tools.denied.map((t) => `- \\`${t}\\``).join(\"\\n\");\n return `# ${name} — Tool Capabilities\n\n## Allowed\n\n${allowed}\n\n## Denied\n\n${denied}\n`;\n}\n\n/**\n * The user tier's agent file. Same body as the repository tier's, one field\n * different: `description`.\n *\n * That field does two jobs at once here. VS Code shows it as the picker's\n * subtitle; Claude Code READS it to decide whether to hand work to this\n * subagent on its own. Twelve personas installed once per machine become\n * visible in every project on that machine, including ones with nothing to do\n * with Arcane, so the wording says plainly that the persona is something you\n * ask for by name rather than something to route work to (ARC-047, amended --\n * an operator who wants automatic delegation changes this one line).\n */\nexport function renderUserTierAgent(def: AgentDefinition, name: string): string {\n // The persona description is hard-wrapped YAML prose. Taking its first\n // LINE cuts mid-sentence (\"...and system design. Reviews\"); collapse the\n // wrapping first, then take the first whole sentence.\n const collapsed = def.persona.description.replace(/\\s+/g, \" \").trim();\n const firstSentence = /^.*?[.!?](?=\\s|$)/.exec(collapsed)?.[0] ?? def.role;\n // Everything after the rendered frontmatter block, kept verbatim: only\n // the description differs between the two tiers.\n const repoFile = renderCopilotAgent(def, name);\n const body = repoFile.slice(repoFile.indexOf(\"---\", 3) + 3);\n return `---\nname: ${name}\ndescription: ${def.role}. ${firstSentence} Use only when explicitly asked for ${name} by name; do not delegate to this persona automatically.\n---${body}`;\n}\n\nexport function renderCopilotAgent(def: AgentDefinition, name: string): string {\n const firstLine = def.persona.description.split(\"\\n\")[0]?.trim() ?? def.role;\n const rules = def.persona.behavioral_rules.map((r) => `- ${r}`).join(\"\\n\");\n const personalityBlock = def.persona.personality\n ? `\\n## Personality\\n\\n${def.persona.personality.trim()}\\n`\n : \"\";\n const voiceBlock = def.persona.voice\n ? `\\n## Voice\\n\\n${def.persona.voice.trim()}\\n`\n : \"\";\n const mottosBlock =\n def.persona.catchphrases && def.persona.catchphrases.length > 0\n ? `\\n## Mottos\\n\\n${def.persona.catchphrases.map((c) => `- \"${c}\"`).join(\"\\n\")}\\n`\n : \"\";\n return `---\nname: ${name}\ndescription: ${def.role} — ${firstLine}\n---\n${mottosBlock}\n## Role\n\n${def.persona.description.trim()}\n${personalityBlock}${voiceBlock}\n## Behavioral Rules\n\n${rules}\n\n## Tools\n\n**Allowed:** ${def.tools.allowed.join(\", \")}\n**Denied:** ${def.tools.denied.join(\", \")}\n`;\n}\n\nfunction renderSpellRoutingSection(): string {\n return `## Spell Routing\n\n| When you're about to... | Invoke |\n|---|---|\n| Commit work | \\`spell-commit-work\\` |\n| Open a session | \\`spell-open-session\\` |\n| Close a session | \\`spell-close-session\\` |\n| Open a pull request | \\`spell-create-pull-request\\` |\n| Ship a feature end-to-end | \\`spell-full-cycle\\` |\n| Fix a bug | \\`spell-bug\\` |\n| Review code or a PR | \\`spell-review\\` |\n\nIf a spell exists for the workflow you are about to perform, invoke it — do not improvise the workflow from general knowledge, even when the user doesn't name the spell.\n`;\n}\n\nfunction renderAgentRosterSection(entries: ResolvedEntry[]): string {\n const rows = entries\n .map(({ def, displayName }) =>\n `| **${displayName}** | ${def.role} | ${def.category} |`,\n )\n .join(\"\\n\");\n\n return `## Agent Roster\n\n| Agent | Role | Category |\n|-------|------|----------|\n${rows}\n`;\n}\n\n// ─── Sync result ─────────────────────────────────────────────────────────────\n\nexport interface SyncResult {\n synced: string[];\n skipped: string[];\n /**\n * True when a rostered role's definition could not be loaded from either\n * the project or bundled source (the mobile-dev bug class: a malformed\n * template silently dropped from the roster with a zero exit code).\n * Distinct from an openclaw-workspace write failure, which is an I/O\n * problem, not a role-resolution failure, and does not set this.\n */\n hasUnresolvedRoles: boolean;\n /**\n * User-tier runs only: the agent files rendered for the home fan-out,\n * unwritten. The caller reconciles them against the store manifest's\n * `fanout` record so an operator's edit survives (CS-06 / ARC-047).\n */\n userAgentFiles: FanoutFile[];\n /**\n * Opted-out repositories only: `.github/agents` files still on disk that\n * this run did not write and will not delete. They are not manifest-tracked\n * by any component, so there is no recorded hash to prove one untouched --\n * they are named for the operator to remove, never removed here.\n */\n unmanagedAgentFiles: string[];\n}\n\n// ─── Main sync function ───────────────────────────────────────────────────────\n\n/**\n * Generates all client-specific output files from the agent roster and definitions.\n *\n * @param targetDir Root of the consuming repo (where .arcane/ lives)\n * @param assetsDir Path to bundled assets (used as fallback for definitions not yet in project)\n * @param roster Parsed agent roster (from .arcane/agents.yaml)\n * @param options Sync options (dry-run, client filters)\n */\n/**\n * The `.agent.md` files already sitting in a repository's agent directory.\n * Used only to report what an opted-out repository still carries; a missing\n * or unreadable directory is simply \"none\".\n */\nasync function existingAgentFiles(agentsDir: string): Promise<string[]> {\n try {\n const entries = await readdir(agentsDir);\n return entries\n .filter((name) => name.endsWith(\".agent.md\"))\n .sort()\n .map((name) => `.github/agents/${name}`);\n } catch {\n return [];\n }\n}\n\nexport async function syncAgents(\n targetDir: string,\n assetsDir: string,\n roster: AgentRoster,\n options: AgentSyncOptions = {},\n): Promise<SyncResult> {\n const synced: string[] = [];\n const skipped: string[] = [];\n let hasUnresolvedRoles = false;\n\n // CS-06 / ARC-047. Two independent axes, deliberately not one flag:\n // `scope` -- is THIS run writing the user tier's own agent files?\n // `spellScope` -- does this REPOSITORY take its client files from the\n // user tier, and therefore write no `.github/agents`?\n const userTier = options.scope === \"user\";\n const repoOptedOut = !userTier && options.spellScope === \"user\";\n const userAgentFiles: FanoutFile[] = [];\n const unmanagedAgentFiles: string[] = [];\n\n // ── Load all definitions ──────────────────────────────────────────────────\n // When the roster came from the user tier, its definition files come with\n // it: an operator who customized a role in the store should see that\n // customization, not the vendor default (CS-06 follow-up / ARC-045 d6).\n const projectDefs = options.definitionsDir ?? projectAgentsDir(targetDir, userTier ? \"user\" : \"repo\");\n const bundledDefs = join(assetsDir, \"agents\");\n\n const resolved: ResolvedEntry[] = [];\n for (const entry of roster.roster) {\n let def: AgentDefinition;\n try {\n // Prefer the project's customized version\n def = await loadAgentDefinition(projectDefs, entry.definition);\n } catch {\n try {\n // Fall back to bundled template\n def = await loadAgentDefinition(bundledDefs, entry.definition);\n } catch {\n skipped.push(`${entry.definition} (definition not found)`);\n hasUnresolvedRoles = true;\n continue;\n }\n }\n resolved.push({\n entry,\n def,\n displayName: entry.name ?? entry.definition,\n });\n }\n\n // ── OpenClaw output ──────────────────────────────────────────────────────\n // Never at the user tier: OpenClaw workspaces already live under\n // `~/.openclaw` and are home-scoped by construction, so there is no\n // per-folder duplication for a tier to solve and nothing to move.\n if (!userTier && roster.openclaw.enabled && options.openclaw !== false) {\n const openclawRoot = roster.openclaw.workspace_root.replace(\n /^~/,\n homedir(),\n );\n\n for (const { entry, def, displayName } of resolved) {\n if (!def.clients.openclaw) continue;\n\n const workspaceName =\n entry.id === \"main\" ? \"workspace\" : `workspace-${entry.id}`;\n const workspaceDir = join(openclawRoot, workspaceName);\n\n try {\n if (!options.dryRun) {\n await mkdir(workspaceDir, { recursive: true });\n await writeFile(\n join(workspaceDir, \"IDENTITY.md\"),\n renderIdentity(def, displayName),\n \"utf8\",\n );\n await writeFile(\n join(workspaceDir, \"SOUL.md\"),\n renderSoul(def, displayName),\n \"utf8\",\n );\n await writeFile(\n join(workspaceDir, \"TOOLS.md\"),\n renderTools(def, displayName),\n \"utf8\",\n );\n } else {\n console.log(` [dry-run] Would write: ${workspaceDir}/IDENTITY.md`);\n console.log(` [dry-run] Would write: ${workspaceDir}/SOUL.md`);\n console.log(` [dry-run] Would write: ${workspaceDir}/TOOLS.md`);\n }\n synced.push(`openclaw:${entry.id}:IDENTITY+SOUL+TOOLS`);\n } catch {\n skipped.push(`openclaw:${entry.id} (workspace not accessible)`);\n }\n }\n\n // Generate openclaw-roster.json patch — never modifies openclaw.json directly\n const patch = {\n $comment:\n \"Apply this to your openclaw.json agents section. \" +\n \"Do not edit manually — regenerated by `spell agents sync`.\",\n agents: resolved.map(({ entry, def }) => ({\n id: entry.id,\n identity: {\n name: entry.name ?? entry.definition,\n ...(entry.epithet ? { epithet: entry.epithet } : {}),\n role: def.role,\n },\n })),\n };\n\n const generatedDir = join(targetDir, \".arcane\", \"generated\");\n if (!options.dryRun) {\n await mkdir(generatedDir, { recursive: true });\n await writeFile(\n join(generatedDir, \"openclaw-roster.json\"),\n JSON.stringify(patch, null, 2) + \"\\n\",\n \"utf8\",\n );\n } else {\n console.log(\n \" [dry-run] Would write: .arcane/generated/openclaw-roster.json\",\n );\n }\n synced.push(\"openclaw-roster.json\");\n }\n\n // ── Copilot output ───────────────────────────────────────────────────────\n if (options.copilot !== false) {\n const agentsDir = join(targetDir, \".github\", \"agents\");\n\n // An opted-out repository still needs `.github` to exist, because the\n // copilot-instructions merge below writes into it and used to rely on\n // this mkdir creating the parent as a side effect.\n if (!options.dryRun && !userTier) {\n await mkdir(repoOptedOut ? join(targetDir, \".github\") : agentsDir, { recursive: true });\n }\n\n for (const { def, displayName } of resolved) {\n if (!def.clients.copilot?.agent_file) continue;\n // Slugify: lowercase, spaces → hyphens (e.g. \"QA Lead\" → \"qa-lead.agent.md\")\n const slug = displayName.toLowerCase().replace(/\\s+/g, \"-\");\n const fileName = `${slug}.agent.md`;\n const content = userTier\n ? renderUserTierAgent(def, displayName)\n : renderCopilotAgent(def, displayName);\n\n // The user tier renders the same bytes to a home-relative path and\n // hands them back rather than writing them here: the write goes through\n // the fan-out reconcile, so an operator's edit to one of these files is\n // recognized and left alone (ARC-038, as applied by CS-04).\n if (userTier) {\n const relativePath = userAgentFanoutPath(slug);\n userAgentFiles.push({ relativePath, client: \"copilot-agents\", content });\n synced.push(`~/${relativePath}`);\n continue;\n }\n\n // This repository takes its client files from the user tier, so it\n // gains no agent file of its own -- writing one would add a second\n // picker entry beside the tier's, not replace it.\n if (repoOptedOut) continue;\n\n if (!options.dryRun) {\n await writeFile(join(agentsDir, fileName), content, \"utf8\");\n } else {\n console.log(` [dry-run] Would write: .github/agents/${fileName}`);\n }\n synced.push(`.github/agents/${fileName}`);\n }\n\n if (repoOptedOut) {\n unmanagedAgentFiles.push(...(await existingAgentFiles(agentsDir)));\n }\n\n // Merge spell routing + roster into copilot-instructions.md. Repository\n // continuity content, not a client discovery surface: it stays in an\n // opted-out repository (ARC-045 decision 5) and is never written at the\n // user tier, which has no repository to describe.\n if (!userTier) {\n const copilotSection = `${renderSpellRoutingSection()}\\n${renderAgentRosterSection(resolved)}`;\n const copilotMerged = await mergeIntoFile(\n targetDir,\n \".github/copilot-instructions.md\",\n copilotSection,\n { force: true, dryRun: options.dryRun },\n );\n if (copilotMerged)\n synced.push(\".github/copilot-instructions.md (agents section)\");\n }\n }\n\n if (userTier) {\n return { synced, skipped, hasUnresolvedRoles, userAgentFiles, unmanagedAgentFiles };\n }\n\n // ── Claude output ────────────────────────────────────────────────────────\n if (options.claude !== false) {\n const claudeSection = `${renderSpellRoutingSection()}\\n${renderAgentRosterSection(resolved)}`;\n const claudeMerged = await mergeIntoFile(\n targetDir,\n \"CLAUDE.md\",\n claudeSection,\n { force: true, dryRun: options.dryRun },\n );\n if (claudeMerged) synced.push(\"CLAUDE.md (agents section)\");\n }\n\n // ── Codex output ─────────────────────────────────────────────────────────\n if (options.codex !== false) {\n const codexSection = `${renderSpellRoutingSection()}\\n${renderAgentRosterSection(resolved)}`;\n const codexMerged = await mergeIntoFile(\n targetDir,\n \"AGENTS.md\",\n codexSection,\n { force: true, dryRun: options.dryRun },\n );\n if (codexMerged) synced.push(\"AGENTS.md (agents section)\");\n }\n\n return { synced, skipped, hasUnresolvedRoles, userAgentFiles, unmanagedAgentFiles };\n}\n","import { readFile, writeFile, rm } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { validateTargetPath } from \"./copier.js\";\n\nexport const MARKER_START = \"<!-- arcane:start -->\";\nexport const MARKER_END = \"<!-- arcane:end -->\";\n\nexport class MalformedMarkersError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"MalformedMarkersError\";\n }\n}\n\n/**\n * Merges `content` into `targetDir/relativePath` using arcane marker comments.\n *\n * Behaviour:\n * - If both markers exist: replaces everything between them with `content`.\n * - If neither marker exists and `force` is true: appends markers + content.\n * - If neither marker exists and `force` is false: no-op, returns false.\n * - If only one marker is present: throws MalformedMarkersError.\n * - If file doesn't exist and `force` is true: creates the file with markers.\n * - If file doesn't exist and `force` is false: no-op, returns false.\n *\n * @returns true if the file was modified (or would be on dry-run), false if untouched.\n */\nexport async function mergeIntoFile(\n targetDir: string,\n relativePath: string,\n content: string,\n opts: { force?: boolean; dryRun?: boolean } = {},\n): Promise<boolean> {\n validateTargetPath(targetDir, relativePath);\n\n const destPath = path.resolve(targetDir, relativePath);\n\n let existing: string | null;\n try {\n existing = await readFile(destPath, \"utf8\");\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\") {\n existing = null;\n } else {\n throw err;\n }\n }\n\n if (existing === null) {\n // File doesn't exist — create with markers only if --force\n if (!opts.force) return false;\n const newContent = `${MARKER_START}\\n${content}\\n${MARKER_END}\\n`;\n if (!opts.dryRun) {\n await writeFile(destPath, newContent, \"utf8\");\n }\n return true;\n }\n\n const startIdx = existing.indexOf(MARKER_START);\n const endIdx = existing.indexOf(MARKER_END);\n\n const hasStart = startIdx !== -1;\n const hasEnd = endIdx !== -1;\n\n if (hasStart !== hasEnd) {\n throw new MalformedMarkersError(\n `File \"${relativePath}\" has mismatched arcane markers: ` +\n `${hasStart ? \"start found\" : \"start missing\"}, ` +\n `${hasEnd ? \"end found\" : \"end missing\"}.`,\n );\n }\n\n if (hasStart && hasEnd) {\n if (startIdx > endIdx) {\n throw new MalformedMarkersError(\n `File \"${relativePath}\" has arcane markers in wrong order (end before start).`,\n );\n }\n // Replace content between the markers (keeps marker lines themselves intact)\n const beforeMarker = existing.slice(0, startIdx + MARKER_START.length);\n const afterMarker = existing.slice(endIdx);\n const updated = `${beforeMarker}\\n${content}\\n${afterMarker}`;\n if (!opts.dryRun) {\n await writeFile(destPath, updated, \"utf8\");\n }\n return true;\n }\n\n // No markers — append only if --force\n if (!opts.force) return false;\n\n const appended =\n `${existing}${existing.endsWith(\"\\n\") ? \"\" : \"\\n\"}` +\n `${MARKER_START}\\n${content}\\n${MARKER_END}\\n`;\n if (!opts.dryRun) {\n await writeFile(destPath, appended, \"utf8\");\n }\n return true;\n}\n\n/**\n * Removes the `<!-- arcane:start -->…<!-- arcane:end -->` block from a file.\n *\n * - If the file has no markers, returns false (no-op).\n * - If the file is empty (or whitespace-only) after stripping, deletes the file.\n * - Otherwise rewrites the file without the marker block.\n *\n * @returns true if the file was modified or deleted, false if untouched.\n */\nexport async function stripMarkerSection(\n targetDir: string,\n relativePath: string,\n opts: { dryRun?: boolean } = {},\n): Promise<boolean> {\n const destPath = path.resolve(targetDir, relativePath);\n\n let existing: string;\n try {\n existing = await readFile(destPath, \"utf8\");\n } catch {\n return false; // file doesn't exist\n }\n\n const startIdx = existing.indexOf(MARKER_START);\n const endIdx = existing.indexOf(MARKER_END);\n if (startIdx === -1 || endIdx === -1) return false;\n\n const before = existing.slice(0, startIdx);\n const after = existing.slice(endIdx + MARKER_END.length);\n const stripped = (before + after).replace(/\\n{3,}/g, \"\\n\\n\").trim();\n\n if (!opts.dryRun) {\n if (stripped.length === 0) {\n await rm(destPath, { force: true });\n } else {\n await writeFile(destPath, stripped + \"\\n\", \"utf8\");\n }\n }\n return true;\n}\n","import { confirm, select, input } from \"@inquirer/prompts\";\nimport { readdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { fileExists } from \"./copier.js\";\nimport { detectAdoContext } from \"./ado-context.js\";\nimport {\n isValidSubjectRoot,\n validateManifestFields,\n ManifestInvalidFieldError,\n MANIFEST_ENUM_VALUES,\n} from \"./manifest.js\";\nimport type {\n ArcaneManifest,\n ContentSensitivity,\n ExternalProvider,\n HubRole,\n ManifestFlags,\n PushPolicy,\n TrackingMode,\n} from \"../types.js\";\n\n/**\n * A manifest-field retrofit: a schema question `spell update` asks exactly\n * once, for an installed manifest that predates the field. This is a general\n * migration mechanism, not a one-off for `role` -- future manifest fields\n * register a retrofit here instead of leaving older installs permanently\n * unset.\n */\nexport interface ManifestRetrofit {\n /** Field this retrofit introduces on ArcaneManifest, for logging only. */\n field: string;\n /** True if the installed manifest predates this field and should be asked. */\n needsRetrofit(manifest: ArcaneManifest): boolean;\n /**\n * Ask the operator (or apply a deterministic default); return the partial\n * patch to merge into the manifest. Receives the manifest so a retrofit\n * can branch on already-known fields (e.g. profile) the way the\n * tracking_mode retrofit below does -- entries that don't need it (role)\n * simply ignore the parameter.\n */\n ask(manifest: ArcaneManifest, targetDir?: string): Promise<Partial<ArcaneManifest>>;\n /** The flag that answers this question without a prompt (PRD D-02). */\n flag: string;\n}\n\n/**\n * The tracking-mode and provider questions, shared by init's Step 5b and the\n * tracking_mode retrofit. With an Azure DevOps remote (PRD D-03), external\n * and then ado are pre-selected; otherwise nothing is. Each option names its\n * downstream effect in one line.\n */\nexport async function askTrackingQuestions(\n targetDir: string | undefined,\n): Promise<{ tracking_mode: TrackingMode; external_provider: ExternalProvider | null }> {\n const ado = targetDir !== undefined && (await detectAdoContext(targetDir));\n const tracking_mode = (await select({\n message: \"How will work be tracked in this repo?\",\n choices: [\n {\n value: \"internal\",\n name: \"Track work in this repo (TODO.md / PRDs)\",\n description: \"Spells keep TODOs, PRDs and stories in this repository's own files.\",\n },\n {\n value: \"external\",\n name: \"Track work in an external tracker (Azure DevOps / GitHub / Jira / other)\",\n description: \"Spells tie PRDs, commits and PRs to items in the tracker you pick next.\",\n },\n ],\n ...(ado ? { default: \"external\" } : {}),\n })) as TrackingMode;\n if (tracking_mode !== \"external\") return { tracking_mode, external_provider: null };\n const external_provider = (await select({\n message: \"Which external tracker?\",\n choices: [\n {\n value: \"ado\",\n name: \"Azure DevOps\",\n description: \"Each PRD needs an ADO work item ID; plans follow your process template's item types.\",\n },\n {\n value: \"github\",\n name: \"GitHub Issues\",\n description: \"Uses the gh CLI; an issue number is optional and linked from commits and PRs when known.\",\n },\n {\n value: \"jira\",\n name: \"Jira\",\n description: \"No Jira automation yet: spells note linking as a TODO and keep planning in this repo.\",\n },\n {\n value: \"other\",\n name: \"Other\",\n description: \"No automation: spells note linking as a TODO and keep planning in this repo.\",\n },\n ],\n ...(ado ? { default: \"ado\" } : {}),\n })) as ExternalProvider;\n return { tracking_mode, external_provider };\n}\n\nexport const MANIFEST_RETROFITS: ManifestRetrofit[] = [\n {\n field: \"role\",\n flag: \"--role <hub|consumer>\",\n needsRetrofit: (m) => m.role === undefined,\n ask: async () => {\n const isHub = await confirm({\n message:\n \"Will this repo manage other ventures as a hub? (idea books, spell-manifest promotion, venture registry)\",\n default: false,\n });\n return { role: (isHub ? \"hub\" : \"consumer\") as HubRole };\n },\n },\n {\n field: \"tracking_mode\",\n flag: \"--tracking-mode <internal|external>\",\n needsRetrofit: (m) => m.tracking_mode === undefined,\n // Mirrors init.ts's Step 5b branching exactly (EF-14 D5): docs-only\n // profiles get a silent default, full/lite get asked -- through the same\n // askTrackingQuestions init uses, so the two cannot drift.\n ask: async (manifest, targetDir) => {\n if (\n manifest.profile === \"governance-only\" ||\n manifest.profile === \"methodology\" ||\n manifest.profile === \"docs\"\n ) {\n return { tracking_mode: \"internal\" as TrackingMode, external_provider: null };\n }\n return askTrackingQuestions(targetDir);\n },\n },\n {\n field: \"content_sensitivity\",\n flag: \"--content-sensitivity <standard|sensitive>\",\n needsRetrofit: (m) => m.content_sensitivity === undefined,\n // EF-12. Asked for every profile -- a code repo can hold sensitive records\n // too -- and defaults to \"standard\", so an operator who just presses enter\n // keeps today's behaviour exactly.\n ask: async () => {\n const content_sensitivity = (await select({\n message: \"How should agents treat this repository's contents?\",\n choices: [\n {\n value: \"standard\",\n name: \"Standard — agents may quote contents in journals and decisions\",\n },\n {\n value: \"sensitive\",\n name: \"Sensitive — agents reference documents by path, never transcribe them\",\n },\n ],\n default: \"standard\",\n })) as ContentSensitivity;\n return { content_sensitivity };\n },\n },\n {\n field: \"subject_root\",\n flag: \"--subject-root <path>\",\n // EF-07. Only the docs profile is asked: other profiles describe code or a\n // venture portfolio, where \"what is this repo about\" is already answered.\n // A docs install that legitimately holds several subjects answers\n // \"portfolio\" and stays unset -- so this retrofit deliberately does NOT\n // re-ask on every update. It is gated on the field being absent AND the\n // profile being docs, and once answered (either way) it never fires again,\n // because \"portfolio\" writes an explicit empty marker.\n needsRetrofit: (m) => m.profile === \"docs\" && m.subject_root === undefined,\n ask: async () => {\n const shape = await select({\n message: \"What does this repository hold?\",\n choices: [\n {\n value: \"root\",\n name: \"One subject, at the repository root (documents sit alongside Arcane's files)\",\n },\n { value: \"subdir\", name: \"One subject, in its own directory\" },\n { value: \"portfolio\", name: \"Several subjects or ventures\" },\n ],\n });\n if (shape === \"root\") return { subject_root: \".\" };\n if (shape === \"subdir\") {\n const answer = await input({\n message: \"Directory holding the subject's documents:\",\n default: \"docs\",\n validate: (v) =>\n isValidSubjectRoot(v.trim() || \"docs\") ||\n \"Must be a relative path inside the repository (no leading /, drive letter, or ..).\",\n });\n return { subject_root: answer.trim() || \"docs\" };\n }\n // Portfolio: business_root covers this shape. Recorded as an explicit\n // null rather than left unset, so the question isn't re-asked on every\n // future update.\n return { subject_root: null };\n },\n },\n {\n field: \"push_policy\",\n flag: \"--push-policy <open|guarded|blocked>\",\n needsRetrofit: (m) => m.push_policy === undefined,\n // EF-09. Defaults to \"open\", so an operator who just presses enter keeps\n // today's behaviour exactly. NOTE: this only records the choice -- unlike\n // init, the retrofit does not install the hook or disable the push URL,\n // because an update should not silently start blocking pushes in a repo\n // someone is mid-workflow in (ARC-034 decision 7). ARC-049 decision 2:\n // update prints pushPolicyNotice() instead, naming `spell block-push`.\n ask: async () => {\n const push_policy = (await select({\n message: \"Should this repository be allowed to push to a remote?\",\n choices: [\n { value: \"open\", name: \"Yes — normal repository\" },\n { value: \"guarded\", name: \"Sensitive, but keep push working — remind me instead\" },\n { value: \"blocked\", name: \"No — block pushes\" },\n ],\n default: \"open\",\n })) as PushPolicy;\n return { push_policy };\n },\n },\n];\n\n/**\n * What `spell update` prints after it records a push_policy (ARC-049\n * decision 2). Update never installs controls (ARC-034 decision 7), so a\n * recorded \"blocked\" is not yet in force and must say so.\n */\nexport function pushPolicyNotice(policy: PushPolicy | undefined): string[] {\n if (policy === \"blocked\") {\n return [\n \"\",\n ' ! Required action: push_policy is now recorded as \"blocked\", but it is NOT enforced yet.',\n \" `spell update` never installs push controls. Pushes from this repository still work\",\n \" until you run:\",\n \"\",\n \" spell block-push\",\n \"\",\n ];\n }\n if (policy === \"guarded\") {\n return [\n \"\",\n ' push_policy is now recorded as \"guarded\". Nothing is installed for it: `spell doctor`',\n \" reports it, and push-performing spells state the policy and ask before pushing.\",\n \"\",\n ];\n }\n return [];\n}\n\n/**\n * Runs every retrofit question the installed manifest predates, in order.\n * Returns the partial patch to merge into the manifest being written.\n * Returns {} immediately (no questions, no console output) if nothing\n * applies -- callers should only invoke this outside dry-run and\n * non-interactive modes.\n */\nexport async function runManifestRetrofits(\n manifest: ArcaneManifest,\n targetDir?: string,\n): Promise<Partial<ArcaneManifest>> {\n const applicable = MANIFEST_RETROFITS.filter((r) => r.needsRetrofit(manifest));\n if (applicable.length === 0) return {};\n\n console.log();\n console.log(\n `This install predates ${applicable.length} manifest field${applicable.length === 1 ? \"\" : \"s\"} -- a couple of quick questions:`,\n );\n\n let patch: Partial<ArcaneManifest> = {};\n for (const retrofit of applicable) {\n const answer = await retrofit.ask({ ...manifest, ...patch }, targetDir);\n patch = { ...patch, ...answer };\n }\n return patch;\n}\n\n/**\n * The command line that answers every still-pending question by flags, for a\n * run that could not ask them (PRD D-02). Undefined when nothing is pending.\n */\nexport function manifestFlagLine(manifest: ArcaneManifest): string[] | undefined {\n const pending = MANIFEST_RETROFITS.filter((r) => r.needsRetrofit(manifest));\n if (pending.length === 0) return undefined;\n const lines = [`spell update ${pending.map((r) => r.flag).join(\" \")}`];\n if (pending.some((r) => r.field === \"tracking_mode\")) {\n lines.push(\"(--tracking-mode external also needs --external-provider <ado|github|jira|other>)\");\n }\n return lines;\n}\n\n/** An operator-facing refusal of a manifest flag; the CLI prints it and exits 1. */\nexport class ManifestFlagError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ManifestFlagError\";\n }\n}\n\nconst FLAG_NAMES: Record<string, string> = {\n role: \"--role\",\n tracking_mode: \"--tracking-mode\",\n external_provider: \"--external-provider\",\n content_sensitivity: \"--content-sensitivity\",\n subject_root: \"--subject-root\",\n push_policy: \"--push-policy\",\n};\n\nconst PUSH_POLICY_STRICTNESS: Record<PushPolicy, number> = { open: 0, guarded: 1, blocked: 2 };\n\nexport function hasManifestFlags(flags: ManifestFlags | undefined): flags is ManifestFlags {\n return flags !== undefined && Object.values(flags).some((v) => v !== undefined);\n}\n\n/**\n * Validates the manifest-question flags and returns the patch they make to\n * `current` (an empty object for a fresh init). Throws ManifestFlagError.\n *\n * A flag may answer an unset question or TIGHTEN push_policy. It never\n * changes a recorded answer, and it never loosens push_policy: ARC-034\n * decision 6 keeps that interactive-only, in `spell unblock-push`.\n */\nexport function resolveManifestFlags(\n flags: ManifestFlags | undefined,\n current: Partial<ArcaneManifest>,\n manifestPath: string,\n): Partial<ArcaneManifest> {\n if (!hasManifestFlags(flags)) return {};\n\n const requested: Partial<Record<keyof ArcaneManifest, unknown>> = {};\n if (flags.role !== undefined) requested.role = flags.role;\n if (flags.trackingMode !== undefined) requested.tracking_mode = flags.trackingMode;\n if (flags.externalProvider !== undefined) requested.external_provider = flags.externalProvider;\n if (flags.contentSensitivity !== undefined) requested.content_sensitivity = flags.contentSensitivity;\n if (flags.subjectRoot !== undefined) requested.subject_root = flags.subjectRoot;\n if (flags.pushPolicy !== undefined) requested.push_policy = flags.pushPolicy;\n\n const invalid = (error: ManifestInvalidFieldError): ManifestFlagError => {\n const allowed = error.field === \"role\" ? [\"hub\", \"consumer\"] : MANIFEST_ENUM_VALUES[error.field];\n const hint = allowed ? ` Valid values: ${allowed.join(\", \")}.` : \"\";\n return new ManifestFlagError(`Invalid ${FLAG_NAMES[error.field] ?? error.field}: ${error.message}${hint}`);\n };\n // role has no manifest validator of its own; same error class, same wording.\n if (requested.role !== undefined && requested.role !== \"hub\" && requested.role !== \"consumer\") {\n throw invalid(new ManifestInvalidFieldError(manifestPath, \"role\", requested.role));\n }\n try {\n validateManifestFields(requested as Partial<ArcaneManifest>, manifestPath);\n } catch (error) {\n if (error instanceof ManifestInvalidFieldError) throw invalid(error);\n throw error;\n }\n\n if (requested.external_provider !== undefined && requested.tracking_mode !== \"external\") {\n throw new ManifestFlagError(\"--external-provider applies only together with --tracking-mode external.\");\n }\n if (requested.tracking_mode === \"external\" && requested.external_provider === undefined) {\n throw new ManifestFlagError(\n \"--tracking-mode external needs --external-provider <ado|github|jira|other> as well.\",\n );\n }\n if (requested.tracking_mode === \"internal\") requested.external_provider = null;\n\n const patch: Partial<Record<keyof ArcaneManifest, unknown>> = {};\n for (const field of Object.keys(FLAG_NAMES) as (keyof ArcaneManifest)[]) {\n if (!(field in requested)) continue;\n const value = requested[field];\n const recorded = current[field];\n if (recorded === undefined) {\n patch[field] = value;\n continue;\n }\n if (field === \"push_policy\") {\n const from = PUSH_POLICY_STRICTNESS[recorded as PushPolicy];\n const to = PUSH_POLICY_STRICTNESS[value as PushPolicy];\n if (to < from) {\n throw new ManifestFlagError(\n `--push-policy ${String(value)} would loosen this repository's recorded push_policy ` +\n `\"${String(recorded)}\". No flag can loosen it: run \\`spell unblock-push\\` from an ` +\n \"interactive terminal.\",\n );\n }\n if (to > from) patch[field] = value;\n continue;\n }\n if (recorded === value) continue;\n // external_provider is implied by --tracking-mode internal; name the flag the operator typed.\n const flag =\n field === \"external_provider\" && flags.externalProvider === undefined ? \"--tracking-mode\" : FLAG_NAMES[field];\n throw new ManifestFlagError(\n `${flag} conflicts with the recorded ${field}: ${JSON.stringify(recorded)}. A flag only answers ` +\n \"a question that has no recorded answer; change .arcane.json deliberately if the recorded \" +\n \"value is wrong.\",\n );\n }\n return patch as Partial<ArcaneManifest>;\n}\n\n/** One line per field a flag recorded, for the run's output. */\nexport function describeFlagPatch(patch: Partial<ArcaneManifest>): string[] {\n return Object.entries(patch)\n .filter(([field]) => field in FLAG_NAMES)\n .map(([field, value]) => `${field}: ${JSON.stringify(value)}`);\n}\n\n/**\n * Offers to scaffold `{business_root}/registry.json` from existing venture\n * folders, once `role` has just become \"hub\" (via init or retrofit). Never\n * overwrites an existing registry. No-ops silently (no prompt, no output) if\n * the business root doesn't exist or holds no venture folders -- a brand new\n * hub with nothing under `ventures/` yet has nothing to scaffold.\n */\nexport async function offerRegistryScaffold(\n targetDir: string,\n businessRoot: string,\n): Promise<void> {\n const registryPath = join(targetDir, businessRoot, \"registry.json\");\n if (await fileExists(registryPath)) return;\n\n const businessRootPath = join(targetDir, businessRoot);\n let ventureDirs: string[];\n try {\n const entries = await readdir(businessRootPath, { withFileTypes: true });\n ventureDirs = entries\n .filter((e) => e.isDirectory() && e.name !== \"_template\")\n .map((e) => e.name);\n } catch {\n return;\n }\n if (ventureDirs.length === 0) return;\n\n console.log();\n const scaffold = await confirm({\n message: `Found ${ventureDirs.length} folder${ventureDirs.length === 1 ? \"\" : \"s\"} under ${businessRoot}/ -- scaffold ${businessRoot}/registry.json from them?`,\n default: true,\n });\n if (!scaffold) return;\n\n const ventures: Record<string, unknown> = {};\n for (const slug of ventureDirs) {\n const include = await confirm({ message: ` Include \"${slug}\"?`, default: true });\n if (!include) continue;\n ventures[slug] = {\n name: slug,\n aliases: [],\n status: \"active\",\n visibility: \"private\",\n ownership: \"llc\",\n tracking: \"none\",\n repos: [],\n };\n }\n if (Object.keys(ventures).length === 0) {\n console.log(\" No ventures selected -- registry not created.\");\n return;\n }\n\n const registry = {\n _comment:\n \"Hub-owned venture registry. Private. Never installed or modified by spell update. \" +\n \"Seeded by the retrofit wizard -- review and enrich (aliases, ownership, tracking, \" +\n \"clones) before relying on it.\",\n updated: new Date().toISOString().slice(0, 10),\n ventures,\n };\n await writeFile(registryPath, JSON.stringify(registry, null, 2) + \"\\n\", \"utf-8\");\n console.log(\n ` ✓ Scaffolded ${businessRoot}/registry.json with ${Object.keys(ventures).length} venture(s).`,\n );\n}\n","import { execFile } from \"node:child_process\";\n\nexport type GitRepositoryState =\n | { status: \"ready\"; uncommittedChanges: number }\n | { status: \"not-repository\" }\n | { status: \"no-commits\" };\n\nexport type GitCommandClass = \"read\" | \"write\" | \"network\";\n\nexport interface RunGitOptions {\n /** Override auto-classification from the subcommand. */\n commandClass?: GitCommandClass;\n /** Override the command class's default timeout, in milliseconds. */\n timeoutMs?: number;\n}\n\nexport interface GitResult {\n stdout: string;\n stderr: string;\n}\n\n/**\n * EF-20: thrown when a `git` subprocess exceeds its runGit-assigned timeout\n * and Node reports the resulting kill via `error.killed`. Note this reflects\n * \"the child was killed by a signal Node sent it,\" which today only ever\n * happens through runGit's own timeout, since no caller retains the\n * underlying ChildProcess to kill it independently -- if a future caller\n * gains that ability, `error.killed` alone would no longer distinguish\n * runGit's timeout from an external kill of the same handle.\n */\nexport class GitTimeoutError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"GitTimeoutError\";\n }\n}\n\nconst NETWORK_SUBCOMMANDS = new Set([\"fetch\", \"pull\", \"push\", \"clone\", \"ls-remote\", \"send-email\"]);\n\nconst WRITE_SUBCOMMANDS = new Set([\n \"init\",\n \"commit\",\n \"add\",\n \"checkout\",\n \"switch\",\n \"merge\",\n \"rebase\",\n \"reset\",\n \"tag\",\n \"mv\",\n \"rm\",\n \"cherry-pick\",\n \"revert\",\n \"stash\",\n \"apply\",\n \"am\",\n \"restore\",\n \"submodule\",\n \"sparse-checkout\",\n \"maintenance\",\n \"notes\",\n]);\n\n// These subcommands can be read-only (`branch --list`, `config --get`) or\n// mutating (`branch -m`, `config user.name x`) depending on flags. Classified\n// as \"write\" -- the safer over-estimate: a read given write's longer timeout\n// just waits a little longer, while a write given read's shorter timeout\n// risks being killed mid-operation.\nconst AMBIGUOUS_AS_WRITE = new Set([\"branch\", \"config\", \"remote\", \"worktree\"]);\n\n// Known read-only subcommands get the fast 15s budget explicitly. Anything\n// NOT in this list, NOT in WRITE_SUBCOMMANDS/AMBIGUOUS_AS_WRITE, and NOT in\n// NETWORK_SUBCOMMANDS is a subcommand this module doesn't recognize at all --\n// that case defaults to \"write\" (see classifyGitCommand), the safer\n// over-estimate for a genuinely unknown operation.\nconst READ_SUBCOMMANDS = new Set([\n \"status\",\n \"rev-parse\",\n \"log\",\n \"diff\",\n \"show\",\n \"describe\",\n \"cat-file\",\n \"ls-files\",\n \"ls-tree\",\n \"symbolic-ref\",\n \"show-ref\",\n \"for-each-ref\",\n \"merge-base\",\n \"blame\",\n \"shortlog\",\n \"grep\",\n \"reflog\",\n \"diff-tree\",\n \"rev-list\",\n \"name-rev\",\n \"count-objects\",\n \"verify-commit\",\n \"verify-tag\",\n \"help\",\n \"version\",\n]);\n\n// Global options that take their value as a SEPARATE following token (not\n// `--opt=value`). Without skipping the value too, `args.find(a =>\n// !a.startsWith(\"-\"))` would grab the value itself (e.g. \"user.name=x\" from\n// `-c user.name=x commit`) and misclassify the real subcommand that follows.\nconst GLOBAL_FLAGS_WITH_SEPARATE_VALUE = new Set([\"-c\", \"-C\"]);\n\nconst DEFAULT_TIMEOUTS_MS: Record<GitCommandClass, number> = {\n read: 15_000,\n write: 30_000,\n network: 120_000,\n};\n\n/**\n * Classify a git invocation by its first real subcommand token, skipping any\n * leading global flags (and, for `-c`/`-C`, their separate-token values) to\n * find it. Exported for tests.\n */\nexport function classifyGitCommand(args: string[]): GitCommandClass {\n let subcommand: string | undefined;\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (GLOBAL_FLAGS_WITH_SEPARATE_VALUE.has(arg)) {\n i++; // skip this flag's value token too\n continue;\n }\n if (arg.startsWith(\"-\")) continue;\n subcommand = arg;\n break;\n }\n if (!subcommand) return \"read\";\n if (NETWORK_SUBCOMMANDS.has(subcommand)) return \"network\";\n if (WRITE_SUBCOMMANDS.has(subcommand) || AMBIGUOUS_AS_WRITE.has(subcommand)) return \"write\";\n if (READ_SUBCOMMANDS.has(subcommand)) return \"read\";\n // A subcommand this module doesn't recognize at all: default to \"write\"\n // (the safer over-estimate) rather than \"read\" -- see READ_SUBCOMMANDS.\n return \"write\";\n}\n\n/**\n * EF-20/EF-13: the non-interactive execution environment applied to every\n * git invocation. GIT_TERMINAL_PROMPT and GCM_INTERACTIVE suppress credential\n * and terminal prompts; GIT_OPTIONAL_LOCKS disables git's speculative index\n * lock during `status` (git-config(1): \"core.optionalLocks\" affects only\n * status/update-index, so this is safe to apply blanket rather than per\n * command class) -- the mitigation for EF-13's stranded-lock report on\n * filesystems that reject unlink. Exported for tests.\n */\nexport function buildGitEnv(): NodeJS.ProcessEnv {\n return {\n ...process.env,\n GIT_TERMINAL_PROMPT: \"0\",\n GCM_INTERACTIVE: \"Never\",\n GIT_OPTIONAL_LOCKS: \"0\",\n };\n}\n\n/**\n * Single chokepoint for every production `git` invocation. Closes stdin\n * immediately (EF-20: an interactive prompt reading from stdin gets instant\n * EOF instead of hanging on an open, unwritten pipe) and enforces a\n * command-class-scoped timeout (EF-20: no git operation can block an\n * autonomous session indefinitely with no completion signal).\n */\nexport function runGit(cwd: string, args: string[], options: RunGitOptions = {}): Promise<GitResult> {\n const commandClass = options.commandClass ?? classifyGitCommand(args);\n // Falls back to the \"write\" budget if commandClass ever reaches here as\n // something outside the GitCommandClass union (only reachable from an\n // untyped caller) -- an undefined timeoutMs would mean NO timeout at all,\n // silently defeating the one guarantee this module exists to provide.\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUTS_MS[commandClass] ?? DEFAULT_TIMEOUTS_MS.write;\n\n return new Promise((resolve, reject) => {\n const child = execFile(\n \"git\",\n args,\n {\n cwd,\n env: buildGitEnv(),\n timeout: timeoutMs,\n // SIGTERM (Node's default) rather than SIGKILL: gives git a chance\n // to run its own cleanup on POSIX, relevant to EF-20's own concern\n // about a killed operation leaving refs/locks in an uncertain state.\n // Moot on Windows -- Node docs: both map to an abrupt TerminateProcess\n // there -- but this module isn't Windows-gated.\n killSignal: \"SIGTERM\",\n windowsHide: true,\n encoding: \"utf8\",\n },\n (error, stdout, stderr) => {\n if (error) {\n if (error.killed) {\n reject(\n new GitTimeoutError(\n // NOTE: embeds the raw args, including any secrets a future\n // network-class caller might pass (e.g. a credentialed\n // clone URL). No current caller does; redact before one does.\n `git ${args.join(\" \")} timed out after ${timeoutMs}ms (class: ${commandClass})`,\n { cause: error },\n ),\n );\n return;\n }\n reject(error);\n return;\n }\n resolve({ stdout, stderr });\n },\n );\n // EF-20: never leave stdin as an open, unread pipe -- see module doc.\n child.stdin?.end();\n });\n}\n\n/**\n * Returns the repository state required before a destructive managed update.\n */\nexport async function inspectGitRepository(cwd: string): Promise<GitRepositoryState> {\n try {\n const { stdout } = await runGit(cwd, [\"rev-parse\", \"--is-inside-work-tree\"]);\n if (stdout.trim() !== \"true\") {\n return { status: \"not-repository\" };\n }\n } catch {\n return { status: \"not-repository\" };\n }\n\n try {\n await runGit(cwd, [\"rev-parse\", \"--verify\", \"HEAD\"]);\n } catch {\n return { status: \"no-commits\" };\n }\n\n const { stdout } = await runGit(cwd, [\"status\", \"--porcelain\", \"-uall\"]);\n const lines = stdout.trim();\n return {\n status: \"ready\",\n uncommittedChanges: lines.length === 0 ? 0 : lines.split(\"\\n\").length,\n };\n}\n\n/**\n * Returns the number of uncommitted changes in the working tree, or 0\n * if the directory is not a git repo or git is not installed.\n */\nexport async function countUncommittedChanges(cwd: string): Promise<number> {\n try {\n const { stdout } = await runGit(cwd, [\"status\", \"--porcelain\", \"-uall\"]);\n const lines = stdout.trim();\n return lines.length === 0 ? 0 : lines.split(\"\\n\").length;\n } catch {\n return 0;\n }\n}\n\nexport interface UnbornBranchCorrection {\n corrected: boolean;\n from?: string;\n to: \"main\";\n /**\n * Set when the correction was declined because the target ref could not be\n * read (a broken/unreadable `refs/heads/main`) rather than because it was\n * absent or already healthy. Callers should surface this: the repo has a\n * real integrity problem the operator will want to know about, but it is\n * not severe enough to abort an install over a cosmetic branch rename.\n */\n blockedReason?: \"target-unreadable\";\n}\n\n/**\n * EF-05: on an unborn repository (git init has run, no commit yet) whose\n * HEAD currently targets exactly \"master\" -- the reported Git for Windows\n * system-default leak (init.defaultBranch=master at the system config\n * level) -- repoint it to \"main\" via `symbolic-ref`. Safe because no commit\n * objects exist yet to conflict with; equivalent to what `git init -b main`\n * does internally. Any OTHER branch name, including one an operator\n * deliberately chose, is left untouched -- this corrects the specific\n * reported default-leak, not a general \"Arcane requires main\" policy.\n * No-op (not corrected) if `cwd` isn't an unborn repo at all.\n */\nexport async function correctUnbornMasterDefault(cwd: string): Promise<UnbornBranchCorrection> {\n let current: string;\n try {\n const { stdout } = await runGit(cwd, [\"symbolic-ref\", \"--short\", \"HEAD\"]);\n current = stdout.trim();\n } catch {\n return { corrected: false, to: \"main\" };\n }\n\n if (current !== \"master\") {\n return { corrected: false, to: \"main\" };\n }\n\n // Verify unborn-ness internally rather than trusting the caller: on a\n // BORN repo already on \"master\", `symbolic-ref HEAD refs/heads/main`\n // would detach HEAD's branch pointer from its commit history without\n // moving any commits -- `main` would exist as an empty ref while every\n // real commit stays reachable only via the abandoned `master` ref. This\n // check is the same one `inspectGitRepository` uses to distinguish\n // \"no-commits\" from \"ready\".\n try {\n await runGit(cwd, [\"rev-parse\", \"--verify\", \"HEAD\"]);\n return { corrected: false, to: \"main\" }; // HEAD resolves -- repo is born, don't touch it\n } catch {\n // rev-parse --verify HEAD failing IS the unborn signal -- proceed.\n }\n\n // Also verify the TARGET doesn't already exist with real history.\n // Confirming the source is unborn is not sufficient: if `refs/heads/main`\n // already resolves (e.g. created earlier in this repo, or shared across\n // git worktrees -- refs/heads/* is shared while HEAD is per-worktree, so\n // an unborn \"master\" HEAD in one worktree can coexist with a fully born\n // \"main\" from another), repointing HEAD onto it would silently attach\n // whatever's currently staged/uncommitted on the unborn HEAD to main's\n // real commit history -- a history splice, not a safe unborn-HEAD\n // repoint, and the next `git commit` an operator runs would commit it.\n // Distinguishing \"absent\" from \"unreadable\" needs stdout/stderr, not an\n // exit code. Verified empirically against git 2.x -- BOTH `rev-parse\n // --verify` and `show-ref --verify --quiet` return the same nonzero status\n // for a corrupt ref as for a missing one (128 and 1 respectively), so\n // either one behind a bare catch reads \"this branch is broken\" as \"this\n // branch does not exist\" and repoints HEAD onto real history anyway.\n //\n // `for-each-ref` usually exits 0 and reports the state through its streams:\n // healthy -> stdout = the object id, stderr empty\n // corrupt -> stdout empty, stderr \"warning: ignoring broken ref ...\"\n // absent -> stdout empty, stderr empty\n // Only the third is safe to treat as absence, and even it needs a second\n // probe (see the symref check below). The stderr test is on emptiness, not\n // on git's English wording, so a translated locale can't defeat it.\n let mainRef: { stdout: string; stderr: string };\n try {\n mainRef = await runGit(cwd, [\"for-each-ref\", \"--format=%(objectname)\", \"refs/heads/main\"]);\n } catch {\n // for-each-ref itself failed -- a corrupt .git/packed-refs exits 128, and\n // a timeout rejects too. Either way we learned nothing about whether main\n // exists, so absence is not a conclusion we're entitled to draw.\n return { corrected: false, to: \"main\", blockedReason: \"target-unreadable\" };\n }\n\n if (mainRef.stdout.trim() !== \"\") {\n return { corrected: false, to: \"main\" }; // main already exists -- don't touch it\n }\n\n if (mainRef.stderr.trim() !== \"\") {\n // Broken ref: it may still hold real history we cannot see. Fail closed\n // -- decline the correction, and tell the caller why so it can warn.\n return { corrected: false, to: \"main\", blockedReason: \"target-unreadable\" };\n }\n\n // Empty/empty is still ambiguous: a DANGLING SYMREF (refs/heads/main is\n // `ref: refs/heads/<missing>`) reports exactly the same shape as an absent\n // ref. Repointing onto it would leave HEAD on the symref's target while\n // reporting \"repointed to main\" -- untrue, even though nothing is spliced.\n // `symbolic-ref -q` is the discriminator: 0 = main exists as a symref,\n // 1 = genuinely absent. Anything else is another unreadable state.\n try {\n await runGit(cwd, [\"symbolic-ref\", \"-q\", \"refs/heads/main\"]);\n return { corrected: false, to: \"main\", blockedReason: \"target-unreadable\" };\n } catch (error) {\n if ((error as { code?: unknown }).code !== 1) {\n return { corrected: false, to: \"main\", blockedReason: \"target-unreadable\" };\n }\n // Exit 1 -- refs/heads/main genuinely does not exist. Safe to target it.\n }\n\n await runGit(cwd, [\"symbolic-ref\", \"HEAD\", \"refs/heads/main\"]);\n return { corrected: true, from: current, to: \"main\" };\n}\n\nexport type PullRebaseResult =\n | { action: \"set\" }\n | { action: \"already-set\" }\n | { action: \"explicit-false-preserved\" };\n\n/**\n * EF-32: ensure `pull.rebase` is set at the repository-LOCAL level so the\n * mandated rebase-and-fast-forward workflow (git-conventions.md) doesn't\n * silently depend on the operator's machine-wide default (Git for Windows\n * ships pull.rebase=false at the system level). Distinguishes \"unset\n * locally\" (safe to set -- it was only ever inheriting a machine default)\n * from \"explicitly set locally, even to false\" (never silently overridden\n * -- the caller is expected to surface this as a warning instead).\n */\nexport async function ensureLocalPullRebase(cwd: string): Promise<PullRebaseResult> {\n let rawLocalValue: string | undefined;\n try {\n const { stdout } = await runGit(cwd, [\"config\", \"--local\", \"--get\", \"pull.rebase\"]);\n rawLocalValue = stdout.trim();\n } catch {\n rawLocalValue = undefined; // nothing set locally at all (may be inherited from global/system)\n }\n\n if (rawLocalValue === undefined) {\n await runGit(cwd, [\"config\", \"--local\", \"pull.rebase\", \"true\"]);\n return { action: \"set\" };\n }\n\n // Something IS set locally. Determine whether it's a falsy boolean via\n // git's own boolean parser (`--type=bool` normalizes any of git's valid\n // spellings -- true/yes/on/1 and false/no/off/0 -- to canonical\n // \"true\"/\"false\"), rather than hand-matching the literal string \"false\"\n // and missing every other valid spelling. A value `--type=bool` can't\n // coerce (e.g. \"merges\" or \"interactive\" -- both valid, deliberate\n // pull.rebase settings) means the operator explicitly chose something\n // else entirely; that's still an explicit local choice and must never be\n // overwritten, so it's treated the same as \"already-set\".\n try {\n const { stdout } = await runGit(cwd, [\"config\", \"--local\", \"--type=bool\", \"--get\", \"pull.rebase\"]);\n if (stdout.trim() === \"false\") {\n return { action: \"explicit-false-preserved\" };\n }\n return { action: \"already-set\" };\n } catch {\n return { action: \"already-set\" };\n }\n}\n","import { runGit } from \"./git.js\";\n\n/**\n * \"ADO context\" for the CLI (PRD D-03, #278): a configured remote hosted on\n * Azure DevOps. A remote host is the only ADO signal a CLI can read\n * deterministically before any PRD exists, so it is the whole definition.\n * It only pre-selects an answer; the operator is still asked.\n */\n\n/** Host of a git remote URL, lower-cased, for both URL and scp-like (`user@host:path`) forms. */\nexport function remoteUrlHost(url: string): string | undefined {\n const trimmed = url.trim();\n if (trimmed.includes(\"://\")) {\n try {\n return new URL(trimmed).hostname.toLowerCase() || undefined;\n } catch {\n return undefined;\n }\n }\n // scp-like syntax; a Windows drive letter (\"C:\\repo\") is a path, not a host.\n const scp = /^(?:[^@/\\s]+@)?([^:/\\s]+):/.exec(trimmed);\n if (!scp || /^[a-zA-Z]$/.test(scp[1]!)) return undefined;\n return scp[1]!.toLowerCase();\n}\n\n/**\n * dev.azure.com, including its `ssh.dev.azure.com` SSH endpoint, or any\n * `*.visualstudio.com` (the legacy `org.visualstudio.com` and\n * `vs-ssh.visualstudio.com` forms). Suffix-matched on a dot boundary so a\n * look-alike such as `dev.azure.com.example.net` does not count.\n */\nexport function isAdoRemoteUrl(url: string): boolean {\n const host = remoteUrlHost(url);\n if (host === undefined) return false;\n return host === \"dev.azure.com\" || host.endsWith(\".dev.azure.com\") || host.endsWith(\".visualstudio.com\");\n}\n\n/** True when any configured remote's URL is on Azure DevOps. */\nexport async function detectAdoContext(targetDir: string): Promise<boolean> {\n let stdout: string;\n try {\n ({ stdout } = await runGit(targetDir, [\"config\", \"--get-regexp\", \"^remote\\\\..*\\\\.url$\"]));\n } catch {\n // Exit 1 (no remote), or not a repository: no ADO context.\n return false;\n }\n return stdout\n .split(\"\\n\")\n .map((line) => line.slice(line.indexOf(\" \") + 1))\n .some((url) => url.trim() !== \"\" && isAdoRemoteUrl(url));\n}\n","/**\n * Push safety (EF-09 / features/push-safety/PRD.md) and shared git-hooks\n * management (ARC-037 decision 2).\n *\n * Two layered controls for a repository whose history must not reach a remote:\n * a `pre-push` hook, and disabled push URLs. Neither is tamper-proof, and the\n * PRD is explicit that they are not meant to be — the threat modelled is an\n * *accidental* push (wrong remote, muscle memory, an unsupervised agent), not a\n * determined operator. Overclaiming here would be its own hazard.\n *\n * The two layers cover different bypasses, which is why both exist:\n * - `git push --no-verify` skips hooks entirely → the disabled URL catches it.\n * - `git push <url> main`, or a second remote, never consults the first\n * remote's URL → the hook catches it.\n * Only combining both bypasses in one command gets through, which is squarely\n * the determined-operator case this does not claim to stop.\n *\n * `installHook`/`isHookInstalled`/`removeHook` below are the generalized form\n * of this module's original pre-push-only machinery: any Arcane hook (pre-push,\n * and now the consumer-facing secrets-scan pre-commit hook) claims the SAME\n * shared `.arcane/hooks/` directory and `core.hooksPath` slot through the SAME\n * R7 collision-guarded path, rather than each hook re-implementing its own\n * claim-and-refuse logic. `installPrePushHook`/`isHookEnforced`/\n * `removePrePushHook` are now thin wrappers over the generic functions, kept\n * for every existing caller (doctor.ts, init.ts, unblock-push.ts, uninstall.ts)\n * and this file's own extensive test suite -- their behavior is unchanged.\n */\nimport { mkdir, readFile, writeFile, chmod, access, rm, readdir } from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport { join, resolve, dirname, basename } from \"node:path\";\nimport { fileExists } from \"./copier.js\";\nimport { runGit } from \"./git.js\";\n\n/**\n * Sentinel push URL. Not a real scheme, so git fails to resolve a helper.\n *\n * The scheme name carries the recovery instruction because it is the ONLY text\n * git shows the operator here. With both layers in place the URL fails first and\n * the hook never runs, so the hook's carefully worded message is never seen --\n * all git prints is `fatal: remote helper '<scheme>' aborted session`. Encoding\n * the fix in the scheme turns a dead end into an instruction.\n */\nexport const DISABLED_PUSH_URL = \"arcane-push-blocked-run-spell-unblock-push://this-repository\";\n\n/**\n * Marks that a remote had NO `pushurl` key before the block. Restoring must then\n * *remove* the key rather than write the fetch URL into it -- otherwise a later\n * `git remote set-url` would change fetch only and pushes would silently keep\n * going to the old location.\n *\n * A separate boolean key rather than a magic value stored in the URL slot: a\n * remote whose genuine push URL happened to equal that magic string would\n * otherwise be recorded as \"there was no key\" and lose its original on restore.\n */\nconst NO_PUSH_URL_MARKER = (remote: string): string => `remote.${remote}.arcaneHadNoPushUrl`;\n\n/** Directory Arcane points `core.hooksPath` at when it owns the hooks. */\nexport const ARCANE_HOOKS_DIR = \".arcane/hooks\";\n\n/**\n * Where a config value is set, as git itself reports it via `--show-scope`.\n * `worktree` matters here: a linked worktree can carry its own `config.worktree`\n * value that `--local` does not see, which an earlier local-vs-effective\n * heuristic misfiled as inherited-from-global.\n */\nexport type ConfigScope = \"local\" | \"worktree\" | \"global\" | \"system\" | \"command\" | \"unknown\";\n\nexport interface HooksPathInfo {\n /** The effective value git would use, respecting scope precedence. */\n value: string;\n /** Where it is set, as reported by git rather than inferred. */\n scope: ConfigScope;\n /** Git could not be asked. Callers must fail closed, never assume \"unset\". */\n unreadable?: boolean;\n}\n\nexport type HookInstallOutcome =\n | { status: \"installed\"; path: string }\n | { status: \"already-ours\" }\n | { status: \"refused-foreign-hooks-path\"; existing: string; scope: ConfigScope }\n | { status: \"refused-default-hooks\"; hooks: string[] }\n | { status: \"refused-unreadable-config\" };\n\n/**\n * Reads the EFFECTIVE `core.hooksPath` — not just the repository-local one.\n *\n * Reading only `--local` was a real defect: a `core.hooksPath` set at *global*\n * scope (the standard way organisations deploy pre-commit and corporate hook\n * managers) was invisible, so installation reported success while silently\n * disabling it, since a local value overrides a global one. That is precisely\n * the harm R7 exists to prevent.\n */\nexport async function readHooksPath(cwd: string): Promise<HooksPathInfo | undefined> {\n let raw: string;\n try {\n // `--show-scope` makes git report the scope rather than us inferring it by\n // comparing --local against the effective value. That inference was wrong\n // for a per-worktree `config.worktree` value, which --local does not see:\n // a genuinely repository-scoped setting was reported as inherited.\n const { stdout } = await runGit(cwd, [\"config\", \"--show-scope\", \"--get\", \"core.hooksPath\"]);\n raw = stdout.trim();\n } catch (error) {\n // ONLY exit 1 means \"not set anywhere\". Anything else -- an unparseable\n // config file, a permission error, an older git that rejects --show-scope --\n // means we could not determine whether someone else owns core.hooksPath.\n // Treating that as \"no claim\" made the R7 collision guard fail OPEN, which\n // is the one direction it must never fail: it would install over an org hook\n // manager it simply failed to see.\n if ((error as { code?: unknown }).code !== 1) {\n return { value: \"\", scope: \"unknown\", unreadable: true };\n }\n return undefined;\n }\n if (raw === \"\") return undefined;\n\n // Format is `<scope>\\t<value>`. The value may itself contain whitespace.\n const separator = raw.indexOf(\"\\t\");\n if (separator === -1) return { value: raw, scope: \"unknown\" };\n\n const scope = raw.slice(0, separator).trim();\n const value = raw.slice(separator + 1).trim();\n if (value === \"\") return undefined;\n\n const known: readonly ConfigScope[] = [\"local\", \"worktree\", \"global\", \"system\", \"command\"];\n const matched = known.find((candidate) => candidate === scope);\n return { value, scope: matched ?? \"unknown\" };\n}\n\n/**\n * True when two `core.hooksPath` values name the same directory.\n *\n * Exact string equality misreported every equivalent spelling git accepts --\n * `.arcane/hooks/`, `./.arcane/hooks`, `.arcane\\hooks`, and an absolute path all\n * point where our hook actually lives and all fire on a real push, yet compared\n * unequal. That direction fails safe (we refuse rather than clobber, and report\n * \"not enforced\" for something that is), but it means doctor contradicts what\n * git does, which is the exact class of confusion this module exists to remove.\n */\nfunction isArcaneHooksPath(root: string, value: string): boolean {\n // Case folding only where the filesystem actually folds case. On POSIX,\n // `.arcane/HOOKS` is a genuinely different directory, and treating it as ours\n // would make the R7 guard fail OPEN -- installing over someone else's hooks.\n // Backslash-to-slash folding is Windows-only for the same reason as case\n // folding: on POSIX a backslash is a legal filename character, so a directory\n // genuinely named `.arcane\\hooks` would normalise onto ours and the R7 guard\n // would fail OPEN -- installing over someone else's hooks.\n const windows = process.platform === \"win32\";\n const normalize = (p: string): string => {\n const separated = windows ? p.replace(/\\\\/g, \"/\") : p;\n // Relative values resolve against the repository ROOT, matching how git\n // itself resolves core.hooksPath -- against the worktree top level, never\n // against the process's working directory.\n const absolute = resolve(root, separated).replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n return windows ? absolute.toLowerCase() : absolute;\n };\n return normalize(value) === normalize(ARCANE_HOOKS_DIR);\n}\n\nconst PRE_PUSH_HOOK_BODY = `#!/bin/sh\n# Installed by Arcane because this repository's .arcane.json sets\n# push_policy: \"blocked\".\n#\n# This is deliberately not tamper-proof. It guards against an accidental push\n# -- the wrong remote, muscle memory, an unsupervised agent -- not against a\n# determined operator, who can always bypass it.\n#\n# To push legitimately, undo the policy first:\n#\n# spell unblock-push\n#\n# That command is interactive-only and asks you to confirm the repository by\n# name. Do not work around this hook by other means: the manifest would then\n# claim a protection this repository no longer has.\necho \"arcane: push blocked (push_policy: blocked). Run 'spell unblock-push' to undo.\" >&2\nexit 1\n`;\n\n/**\n * Human phrasing for a config scope.\n *\n * Worth its own function because \"globally\" was previously printed for anything\n * not local — including a system-scope value and a per-worktree one, neither of\n * which the operator would find where that message sends them.\n */\nexport function describeConfigScope(scope: ConfigScope): string {\n switch (scope) {\n case \"local\":\n return \"set for this repository\";\n case \"worktree\":\n return \"set for this worktree\";\n case \"global\":\n return \"set globally, for your user\";\n case \"system\":\n return \"set system-wide\";\n case \"command\":\n return \"set on the command line\";\n default:\n return \"scope unknown\";\n }\n}\n\n/**\n * The repository's top level — where the hook must live.\n *\n * `targetDir` is whatever directory the CLI was run from, and\n * `--is-inside-work-tree` is true from any subdirectory. Installing the hook\n * relative to `targetDir` while pointing repo-wide `core.hooksPath` at the\n * literal `.arcane/hooks` put the file somewhere git never looks: git resolves a\n * relative hooksPath against the *worktree top-level*, so a `spell init` run\n * inside a monorepo package disabled the repository's hooks entirely and\n * reported the block installed.\n */\nasync function repositoryRoot(cwd: string): Promise<string> {\n // Anchored on the COMMON git dir, so every linked worktree resolves to the\n // same directory as the main one. `--show-toplevel` would give each worktree\n // its own root, which is exactly the mistake being fixed: the hook lives in\n // one place, and `core.hooksPath` is shared config that must point there from\n // everywhere.\n try {\n const { stdout } = await runGit(cwd, [\n \"rev-parse\",\n \"--path-format=absolute\",\n \"--git-common-dir\",\n ]);\n const commonDir = stdout.trim();\n if (commonDir !== \"\") {\n // `<main>/.git` -> `<main>`. A bare repository has no working tree above\n // its git dir, so fall through to the worktree top level there.\n const parent = dirname(commonDir);\n if (basename(commonDir) === \".git\" && parent !== \"\") return parent;\n }\n } catch {\n // Older git without --path-format; fall back below.\n }\n try {\n const { stdout } = await runGit(cwd, [\"rev-parse\", \"--show-toplevel\"]);\n const top = stdout.trim();\n if (top !== \"\") return top;\n } catch {\n // Not a work tree (bare repo).\n }\n return cwd;\n}\n\n/**\n * Absolute path of a named hook file Arcane installs, resolved against the\n * repository top level rather than the working directory. Defaults to\n * \"pre-push\" -- this module's original and, until ARC-037, only hook -- so\n * every existing single-argument call site (this file's own internals and\n * this file's test suite) is unaffected by the generalization to other\n * hook names.\n */\nexport async function hookFilePath(cwd: string, hookName: string = \"pre-push\"): Promise<string> {\n return join(await repositoryRoot(cwd), ARCANE_HOOKS_DIR, hookName);\n}\n\n/**\n * The value written to `core.hooksPath`: an ABSOLUTE path.\n *\n * A relative value is re-resolved per worktree, and `core.hooksPath` lives in\n * shared local config while `.arcane/hooks/` is an untracked directory that\n * exists only in the checkout where it was created. So every linked worktree\n * inherited the config and had no hook file — the hook layer was simply absent\n * there, and `git push <url>` (the bypass only the hook covers) delivered the\n * full history in one ordinary command while `doctor` reported the repository\n * blocked. That is not an edge case here: Arcane's own methodology sends\n * concurrent sessions into linked worktrees.\n */\nasync function hooksPathValue(cwd: string): Promise<string> {\n return join(await repositoryRoot(cwd), ARCANE_HOOKS_DIR).replace(/\\\\/g, \"/\");\n}\n\n/**\n * Installs a named hook (its body written verbatim), refusing rather than\n * clobbering someone else's hook manager (R7).\n *\n * `core.hooksPath` is a single exclusive slot — Git reads one directory, never\n * several — so pointing it at Arcane's directory silently disables Husky,\n * Lefthook, pre-commit, or anything else already using it, at either local or\n * global scope. This repository's own `core.hooksPath` is `.husky/_`, running\n * lint, typecheck and the full test suite; a naive install would have turned\n * that off while appearing to add protection.\n *\n * Safe to call for a SECOND hook name once a first has already claimed the\n * directory: the collision guard above only refuses a FOREIGN path, and\n * Arcane's own already-claimed `.arcane/hooks/` is not foreign to a second\n * Arcane hook — the two hooks share the one slot rather than racing for it.\n */\nexport async function installHook(cwd: string, hookName: string, body: string): Promise<HookInstallOutcome> {\n const root = await repositoryRoot(cwd);\n const existing = await readHooksPath(cwd);\n\n if (existing?.unreadable === true) return { status: \"refused-unreadable-config\" };\n\n if (existing !== undefined && !isArcaneHooksPath(root, existing.value)) {\n return {\n status: \"refused-foreign-hooks-path\",\n existing: existing.value,\n scope: existing.scope,\n };\n }\n\n // The guard above only sees hook managers that announce themselves via\n // core.hooksPath. A repository using git's DEFAULT hooks directory has no\n // such key, so taking the slot silently disabled every hook it had -- the\n // exact harm R7 names, reached by the one route R7 wasn't looking at.\n // Only relevant on the FIRST claim: once existing is defined (ours), a\n // second hook joining the same directory displaces nothing new.\n if (existing === undefined) {\n const displaced = await defaultHooks(cwd);\n if (displaced.length > 0) {\n return { status: \"refused-default-hooks\", hooks: displaced };\n }\n }\n\n const hookPath = join(root, ARCANE_HOOKS_DIR, hookName);\n\n if (existing !== undefined && (await hookFileMatches(cwd, hookName, body))) {\n return { status: \"already-ours\" };\n }\n\n await mkdir(join(root, ARCANE_HOOKS_DIR), { recursive: true });\n await writeFile(hookPath, body, \"utf-8\");\n // Git requires the hook be executable on POSIX. chmod is a no-op for the\n // owner on Windows, which is fine -- Git for Windows does not check the bit.\n await chmod(hookPath, 0o755);\n await runGit(cwd, [\"config\", \"--local\", \"core.hooksPath\", await hooksPathValue(cwd)]);\n\n return { status: \"installed\", path: hookPath };\n}\n\n/** Installs the pre-push hook. Thin wrapper over {@link installHook}. */\nexport async function installPrePushHook(cwd: string): Promise<HookInstallOutcome> {\n return installHook(cwd, \"pre-push\", PRE_PUSH_HOOK_BODY);\n}\n\n/**\n * True only if the named hook is genuinely in force: the config points at\n * Arcane's directory AND that hook's file actually exists there with the\n * expected body.\n *\n * Checking the config alone is a declaration check, not an enforcement check —\n * deleting the hook file leaves the config intact and pushes succeed.\n */\nexport async function isHookInstalled(cwd: string, hookName: string, body: string): Promise<boolean> {\n const hooksPath = await readHooksPath(cwd);\n if (hooksPath === undefined || hooksPath.unreadable === true) return false;\n if (!isArcaneHooksPath(await repositoryRoot(cwd), hooksPath.value)) return false;\n return hookFileMatches(cwd, hookName, body);\n}\n\n/** Whether the pre-push hook is genuinely enforced. Thin wrapper over {@link isHookInstalled}. */\nexport async function isHookEnforced(cwd: string): Promise<boolean> {\n return isHookInstalled(cwd, \"pre-push\", PRE_PUSH_HOOK_BODY);\n}\n\n/**\n * Warns -- never blocks -- when the branch being pushed already has a\n * CLOSED or MERGED pull request (ARC-035 decision 4: the silent\n * stranded-commit mode that shipped as PR #63's incident is made loud by\n * extending this same hook slot, not by adding a second, competing one).\n *\n * Shares the \"pre-push\" hook name with {@link PRE_PUSH_HOOK_BODY}, so the\n * two are mutually exclusive per repository by construction -- a repo with\n * `push_policy: \"blocked\"` already refuses every push outright and has no\n * use for a warning about which PR it would have gone to; every other\n * tier (`guarded`, `open`, unset) gets this one instead. GitHub-only,\n * matching the ADR's own cited mechanism (`gh pr view --json state`); a\n * non-GitHub remote or a missing/unauthenticated `gh` is a silent no-op,\n * never a blocked or errored push.\n */\nconst CLOSED_PR_WARNING_HOOK_BODY = `#!/bin/sh\n# Installed by Arcane (ARC-035 decision 4): warns when the branch being\n# pushed already has a closed or merged PR, instead of accepting the push\n# into a void with no signal at all. Never blocks -- pushing to a branch\n# behind a closed PR is sometimes intentional.\nremote_url=\"$2\"\ncase \"$remote_url\" in\n *github.com*) ;;\n *) exit 0 ;;\nesac\ncommand -v gh >/dev/null 2>&1 || exit 0\nbranch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || exit 0\nstate=$(gh pr view \"$branch\" --json state --jq .state 2>/dev/null) || exit 0\ncase \"$state\" in\n CLOSED|MERGED)\n echo \"arcane: WARNING -- the PR for branch '$branch' is already $state. Pushing anyway (not blocked).\" >&2\n ;;\nesac\nexit 0\n`;\n\n/** Installs the closed-PR warning hook. Thin wrapper over {@link installHook}. */\nexport async function installClosedPrWarningHook(cwd: string): Promise<HookInstallOutcome> {\n return installHook(cwd, \"pre-push\", CLOSED_PR_WARNING_HOOK_BODY);\n}\n\n/** True only if the closed-PR warning hook (not the blocking one) is the one currently installed. */\nexport async function isClosedPrWarningHookInstalled(cwd: string): Promise<boolean> {\n return isHookInstalled(cwd, \"pre-push\", CLOSED_PR_WARNING_HOOK_BODY);\n}\n\n/**\n * Real hooks living in git's default directory, which taking `core.hooksPath`\n * would silently switch off. `.sample` files are git's own inert templates.\n */\nasync function defaultHooks(cwd: string): Promise<string[]> {\n let hooksDir: string;\n try {\n const { stdout } = await runGit(cwd, [\"rev-parse\", \"--git-common-dir\"]);\n hooksDir = join(resolve(cwd, stdout.trim()), \"hooks\");\n } catch {\n return [];\n }\n try {\n return (await readdir(hooksDir)).filter((name) => !name.endsWith(\".sample\")).sort();\n } catch {\n return [];\n }\n}\n\n/**\n * True when a named hook file on disk is the one Arcane wrote, byte for byte.\n *\n * Existence alone is not enforcement: a zero-byte file, or one edited down to\n * `exit 0`, leaves the config pointing at a hook that blocks nothing while\n * `doctor` reports a pass. That is the same declaration-versus-enforcement\n * confusion review already found one level up, so the check compares content --\n * which `installHook` already needed for its `already-ours` decision.\n */\nasync function hookFileMatches(cwd: string, hookName: string, body: string): Promise<boolean> {\n const hookPath = await hookFilePath(cwd, hookName);\n if (!(await fileExists(hookPath))) return false;\n try {\n if ((await readFile(hookPath, \"utf-8\")) !== body) return false;\n } catch {\n return false;\n }\n\n // On POSIX, git silently skips a hook that is not executable, so the right\n // body in a non-executable file is still not enforcement. Git for Windows\n // does not consult the bit, and chmod there is largely advisory, so checking\n // it would produce a false \"not enforced\" for a hook that demonstrably fires.\n if (process.platform === \"win32\") return true;\n try {\n await access(hookPath, constants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Removes a named hook's wiring. Leaves a foreign `core.hooksPath` untouched.\n *\n * Only unclaims `core.hooksPath` itself once NO Arcane-installed hook remains\n * in the shared directory: since ARC-037, `.arcane/hooks/` can hold more than\n * one hook (pre-push and pre-commit), and removing one must not silently\n * disable a sibling that still needs the same claimed path. Verified rather\n * than assumed by re-listing the directory after deleting -- not by counting\n * known hook names, which this module has no reason to enumerate.\n */\nexport async function removeHook(cwd: string, hookName: string): Promise<void> {\n const existing = await readHooksPath(cwd);\n if (existing === undefined || existing.unreadable === true) return;\n const root = await repositoryRoot(cwd);\n if (!isArcaneHooksPath(root, existing.value)) return;\n if (existing.scope !== \"local\" && existing.scope !== \"worktree\") return;\n\n // Remove the hook file first. Leaving it behind means a later `spell init`\n // that re-points core.hooksPath silently re-arms a block the operator\n // lifted.\n try {\n await rm(await hookFilePath(cwd, hookName), { force: true });\n } catch {\n // Non-fatal: if core.hooksPath ends up unset below, git no longer reads\n // this directory anyway.\n }\n\n const hooksDir = join(root, ARCANE_HOOKS_DIR);\n let remaining: string[] = [];\n try {\n remaining = await readdir(hooksDir);\n } catch {\n remaining = [];\n }\n if (remaining.length > 0) return; // a sibling hook still needs core.hooksPath\n\n // Unset at the scope the value actually lives in. `--local` cannot touch a\n // per-worktree `config.worktree` value, so accepting worktree scope above and\n // then unsetting locally left the hook fully in force while `unblock-push`\n // printed \"Push unblocked\".\n const scopeFlag = existing.scope === \"worktree\" ? \"--worktree\" : \"--local\";\n try {\n await runGit(cwd, [\"config\", scopeFlag, \"--unset\", \"core.hooksPath\"]);\n } catch {\n // Already unset; nothing to undo.\n }\n}\n\n/** Removes the pre-push hook's wiring. Thin wrapper over {@link removeHook}. */\nexport async function removePrePushHook(cwd: string): Promise<void> {\n return removeHook(cwd, \"pre-push\");\n}\n\n/** Every configured remote name, in git's own order. */\nexport async function listRemotes(cwd: string): Promise<string[]> {\n try {\n const { stdout } = await runGit(cwd, [\"remote\"]);\n return stdout\n .split(\"\\n\")\n .map((r) => r.trim())\n .filter(Boolean);\n } catch {\n return [];\n }\n}\n\n/**\n * Each remote with the URL(s) a push would actually reach.\n *\n * The `guarded` reminder exists to catch a *wrong remote*, and a bare name like\n * `origin` says nothing about where it points — which is the entire question.\n */\nexport async function pushTargets(cwd: string): Promise<{ remote: string; urls: string[] }[]> {\n const targets: { remote: string; urls: string[] }[] = [];\n for (const remote of await listRemotes(cwd)) {\n try {\n const { stdout } = await runGit(cwd, [\"remote\", \"get-url\", \"--push\", \"--all\", remote]);\n targets.push({\n remote,\n urls: stdout\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean),\n });\n } catch {\n targets.push({ remote, urls: [] });\n }\n }\n return targets;\n}\n\nexport type PushUrlStatus =\n | \"disabled\"\n | \"already-disabled\"\n | \"restored\"\n | \"nothing-recorded\"\n | \"failed\";\n\nexport interface PushUrlResult {\n remote: string;\n status: PushUrlStatus;\n /** Present only on \"failed\". Why this remote could not be covered. */\n reason?: string;\n}\n\n/**\n * Where a remote's original push URL is recorded.\n *\n * Deliberately a key *inside the remote's own section* rather than a flat\n * `arcane.originalPushUrl.<remote>`, which review broke three ways:\n *\n * - `my_remote` is a legal remote name but an illegal trailing config key\n * (that segment must be alphanumeric or `-`), so `git config` errored and\n * aborted the whole loop, leaving every later remote live while the operator\n * had been told the repository was blocked.\n * - Trailing key segments are case-INSENSITIVE, so remotes `origin` and\n * `Origin` collided on one key: one original was lost and restore pointed a\n * remote at the other's URL -- the wrong-remote push this feature exists to\n * prevent.\n * - `git remote rename` left the flat key orphaned, so restore silently found\n * nothing while `unblock-push` still reported success.\n *\n * A `remote.<name>.*` subsection has none of those problems: git accepts any\n * legal remote name there, subsection names ARE case-sensitive, and `git remote\n * rename` moves the whole section including keys it has never heard of. All\n * three verified directly against git 2.53 before this was written.\n */\nfunction originalPushUrlKey(remote: string): string {\n return `remote.${remote}.arcaneOriginalPushUrl`;\n}\n\n/** Reads every value of a possibly-multivalued local config key. */\nasync function readLocalAll(cwd: string, key: string): Promise<string[]> {\n try {\n const { stdout } = await runGit(cwd, [\"config\", \"--local\", \"--get-all\", key]);\n return stdout\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean);\n } catch {\n // Exit 1 == key absent.\n return [];\n }\n}\n\n/**\n * Every value of a multivalued key, WITH the scope each came from.\n *\n * `remote.<name>.pushurl` is multivalued and git collects values across system,\n * global and local scope. Reading only `--local` was a silent hole: a pushurl\n * configured globally stayed invisible, a local `--replace-all` *appended* the\n * sentinel instead of replacing the live URL, and because the global value sorts\n * first git delivered the push to it and only then failed on the sentinel. The\n * exit code was 128 and the history was already gone -- with `doctor` reporting\n * the repository fully covered.\n */\nasync function readScopedAll(cwd: string, key: string): Promise<{ scope: string; value: string }[]> {\n let stdout: string;\n try {\n ({ stdout } = await runGit(cwd, [\"config\", \"--show-scope\", \"--get-all\", key]));\n } catch {\n return [];\n }\n return stdout\n .split(\"\\n\")\n .filter((line) => line !== \"\")\n .map((line) => {\n // `<scope>\\t<value>`; split BEFORE trimming. Trimming first ate the\n // separator tab on an empty-valued entry (`global\\t`), which then parsed\n // as `{scope: \"unknown\", value: \"global\"}` and produced a remedy telling\n // the operator to run `git config --unknown`, which is not a command.\n const separator = line.indexOf(\"\\t\");\n if (separator === -1) return { scope: \"unknown\", value: line.trim() };\n return { scope: line.slice(0, separator).trim(), value: line.slice(separator + 1).trim() };\n });\n}\n\n/** Push URLs git would actually use for a remote, across every config scope. */\nasync function effectivePushUrls(cwd: string, remote: string): Promise<string[]> {\n const scoped = await readScopedAll(cwd, `remote.${remote}.pushurl`);\n return scoped.map((entry) => entry.value);\n}\n\n/**\n * Points every remote's PUSH url at a sentinel, so `git push` fails at the\n * transport layer. Fetch is unaffected: fetch URLs are left alone, so a blocked\n * repository can still pull.\n *\n * Applies to ALL remotes, not just `origin` — a repository commonly has an\n * `upstream` or `backup` too, and protecting only one of them protects nothing\n * in particular.\n *\n * Each original is recorded in `arcane.originalPushUrl.<remote>` so\n * `spell unblock-push` restores exactly what was there rather than guessing\n * from the fetch URL, which is legitimately allowed to differ.\n */\nexport async function disablePushUrls(cwd: string): Promise<PushUrlResult[]> {\n const results: PushUrlResult[] = [];\n for (const remote of await listRemotes(cwd)) {\n // One remote must never take the others down with it. The previous loop let\n // a single `git config` error propagate, so an oddly-named remote left every\n // remote after it in git's ordering completely unprotected -- silently, in a\n // repository the operator had just been told was blocked. Failures are now\n // recorded per remote and reported, and the loop continues.\n try {\n results.push(await disableOneRemote(cwd, remote));\n } catch (error) {\n results.push({ remote, status: \"failed\", reason: describeError(error) });\n }\n }\n return results;\n}\n\nasync function disableOneRemote(cwd: string, remote: string): Promise<PushUrlResult> {\n const scoped = await readScopedAll(cwd, `remote.${remote}.pushurl`);\n\n // A pushurl set outside this repository cannot be neutralised from inside it.\n // Config is additive for multivalued keys: a local write ADDS to git's list,\n // it cannot subtract the outside value, and the outside value sorts first --\n // so the push is delivered and only then fails on the sentinel. Refuse and say\n // so, rather than writing a sentinel that makes the repository look covered\n // while the history walks out the front door.\n // `worktree` scope is writable from inside the repository, so it is ours to\n // manage, not foreign. Only genuinely outer scopes are unreachable.\n const OVERRIDABLE = new Set([\"local\", \"worktree\"]);\n const foreign = scoped.filter(\n (entry) => !OVERRIDABLE.has(entry.scope) && entry.value !== DISABLED_PUSH_URL,\n );\n if (foreign.length > 0) {\n const scopes = [...new Set(foreign.map((entry) => entry.scope))];\n const where = scopes.join(\", \");\n const flag = scopes[0] === \"unknown\" ? \"<scope>\" : `--${scopes[0]}`;\n return {\n remote,\n status: \"failed\",\n reason:\n `a push URL for this remote is configured at ${where} scope, which this repository cannot ` +\n `override — git would still deliver the push there. Remove it with ` +\n `\\`git config ${flag} --unset-all remote.${remote}.pushurl\\` and re-apply.`,\n };\n }\n\n const currentPushUrls = scoped.map((entry) => entry.value);\n if (currentPushUrls.length === 1 && currentPushUrls[0] === DISABLED_PUSH_URL) {\n return { remote, status: \"already-disabled\" };\n }\n\n const key = originalPushUrlKey(remote);\n // Record what was there BEFORE overwriting, and only if we have not already\n // recorded it -- otherwise a second disable would enshrine the sentinel as the\n // \"original\" and make restore impossible.\n if ((await readLocalAll(cwd, key)).length === 0 && !(await hadNoPushUrl(cwd, remote))) {\n if (currentPushUrls.length > 0) {\n await runGit(cwd, [\"config\", \"--local\", \"--replace-all\", key, currentPushUrls[0]!]);\n for (const extra of currentPushUrls.slice(1)) {\n await runGit(cwd, [\"config\", \"--local\", \"--add\", key, extra]);\n }\n } else {\n // A remote with no pushurl key pushes to its fetch URL. Record that as a\n // separate marker rather than a magic URL value, so restore removes the\n // key instead of pinning one that was never there -- and so a remote whose\n // genuine URL happened to equal the marker cannot be misread as \"none\".\n await runGit(cwd, [\"config\", \"--local\", NO_PUSH_URL_MARKER(remote), \"true\"]);\n }\n }\n\n // `--replace-all` rather than `git remote set-url --push`: a mirror remote\n // with two push URLs makes set-url refuse outright (\"has multiple values\"),\n // which used to abort the run and leave BOTH mirrors pushable.\n await runGit(cwd, [\n \"config\",\n \"--local\",\n \"--replace-all\",\n `remote.${remote}.pushurl`,\n DISABLED_PUSH_URL,\n ]);\n\n // Re-read what git ACTUALLY resolves, rather than trusting that the write did\n // what we intended. The scope-based refusal above catches the cases git labels\n // as an outer scope, but not all of them: a value contributed by a file the\n // repository `include`s is reported as scope `local`, survives\n // `--replace-all`, and — if the include precedes the remote section — sorts\n // first and delivers the push. Verifying the outcome closes that whole class\n // rather than the enumerated members of it, which is the difference between\n // this check and the last three rounds of enumerating members.\n const after = await effectivePushUrls(cwd, remote);\n if (after.length !== 1 || after[0] !== DISABLED_PUSH_URL) {\n const live = after.filter((url) => url !== DISABLED_PUSH_URL);\n return {\n remote,\n status: \"failed\",\n reason:\n `the sentinel was written but git still resolves a live push URL for this remote ` +\n `(${live.join(\", \")}). It most likely comes from a file this repository \\`include\\`s, ` +\n `which \\`--replace-all\\` cannot remove. Run \\`git config --show-origin --get-all ` +\n `remote.${remote}.pushurl\\` to find the file, and clear it there.`,\n };\n }\n return { remote, status: \"disabled\" };\n}\n\n/**\n * Remotes whose push URL is NOT fully disabled. Empty when fully covered.\n *\n * A remote counts as open if ANY of its push URLs is live — a mirror with one\n * blocked and one live URL still delivers the push.\n */\nexport async function undisabledRemotes(cwd: string): Promise<string[]> {\n const open: string[] = [];\n for (const remote of await listRemotes(cwd)) {\n // Effective, not local: a pushurl inherited from global or system scope is\n // a live delivery path, and reading only local scope reported such a remote\n // as covered while `--no-verify` alone still delivered the history.\n const urls = await effectivePushUrls(cwd, remote);\n if (urls.length === 0 || urls.some((url) => url !== DISABLED_PUSH_URL)) open.push(remote);\n }\n return open;\n}\n\n/**\n * Remotes still carrying the sentinel push URL.\n *\n * The inverse of `undisabledRemotes`, and needed after an unblock: if a remote\n * was renamed while blocked, its recorded original moved with it but a different\n * remote can be left holding the sentinel with nothing to restore from. Checked\n * directly rather than inferred from what the restore believes it did.\n */\nexport async function blockedRemotes(cwd: string): Promise<string[]> {\n const blocked: string[] = [];\n for (const remote of await listRemotes(cwd)) {\n const urls = await effectivePushUrls(cwd, remote);\n if (urls.some((url) => url === DISABLED_PUSH_URL)) blocked.push(remote);\n }\n return blocked;\n}\n\n/** Restores every recorded push URL. Used only by `spell unblock-push`. */\nexport async function restorePushUrls(cwd: string): Promise<PushUrlResult[]> {\n const results: PushUrlResult[] = [];\n for (const remote of await listRemotes(cwd)) {\n try {\n results.push(await restoreOneRemote(cwd, remote));\n } catch (error) {\n results.push({ remote, status: \"failed\", reason: describeError(error) });\n }\n }\n return results;\n}\n\nasync function restoreOneRemote(cwd: string, remote: string): Promise<PushUrlResult> {\n const key = originalPushUrlKey(remote);\n let recorded = await readLocalAll(cwd, key);\n let hadNone = await hadNoPushUrl(cwd, remote);\n\n if (recorded.length === 0 && !hadNone) {\n const legacy = await readLegacyRecord(cwd, remote);\n if (legacy.status !== \"usable\") return { remote, status: legacy.status, reason: legacy.reason };\n recorded = legacy.values;\n hadNone = false;\n }\n\n if (hadNone) {\n // `--unset-all` exits 5 when the key is already absent. That is the goal\n // state, not a failure -- letting it throw skipped the stale-key cleanup\n // below, and a surviving `arcaneHadNoPushUrl` marker made the NEXT block\n // record nothing and the next restore delete a genuine push URL.\n await unsetIgnoringMissing(cwd, `remote.${remote}.pushurl`);\n } else {\n await runGit(cwd, [\n \"config\",\n \"--local\",\n \"--replace-all\",\n `remote.${remote}.pushurl`,\n recorded[0]!,\n ]);\n for (const extra of recorded.slice(1)) {\n await runGit(cwd, [\"config\", \"--local\", \"--add\", `remote.${remote}.pushurl`, extra]);\n }\n }\n\n // Clear our bookkeeping only on success. Doing this unconditionally would\n // destroy a record we had *deliberately* declined to apply -- the legacy-key\n // refusal above prints the value for the operator to apply by hand, and\n // wiping it in the same breath makes that instruction unfollowable.\n //\n // The stale-marker hazard this used to guard against is closed at its source:\n // `unsetIgnoringMissing` treats git's exit 5 (\"key already absent\") as\n // success, so the throw that previously skipped this cleanup no longer\n // happens.\n for (const staleKey of [key, NO_PUSH_URL_MARKER(remote), `arcane.originalPushUrl.${remote}`]) {\n try {\n await unsetIgnoringMissing(cwd, staleKey);\n } catch {\n // Non-fatal: the URL is restored, which is what matters.\n }\n }\n return { remote, status: \"restored\" };\n}\n\n/**\n * Unsets a config key, treating \"it was already absent\" as success.\n *\n * `git config --unset-all` exits **5** for a missing key. Letting that throw\n * turned a no-op into a reported failure and, worse, skipped whatever cleanup\n * followed it.\n */\nasync function unsetIgnoringMissing(cwd: string, key: string): Promise<void> {\n try {\n await runGit(cwd, [\"config\", \"--local\", \"--unset-all\", key]);\n } catch (error) {\n if ((error as { code?: unknown }).code !== 5) throw error;\n }\n}\n\nasync function hadNoPushUrl(cwd: string, remote: string): Promise<boolean> {\n return (await readLocalAll(cwd, NO_PUSH_URL_MARKER(remote)))[0] === \"true\";\n}\n\n/**\n * Reads a record left by the released `0.20.0`, which stored originals in a flat\n * `arcane.originalPushUrl.<remote>` key -- and only applies it when it can be\n * trusted.\n *\n * Applying it unconditionally reintroduced, for exactly the population told to\n * upgrade, the wrong-remote push this release fixes. Two ways, both reproduced:\n *\n * - Trailing config-key segments are case-insensitive, so `0.20.0` wrote one\n * key for both `origin` and `Origin` and only the last writer survived.\n * Restoring it points one remote at the OTHER remote's URL, reported as a\n * clean success.\n * - Unlike a `remote.<name>.*` key, the flat key survives `git remote remove`.\n * A remote deleted and re-added to a different URL gets silently restored to\n * the old target.\n *\n * So the record is used only when the remote is still carrying OUR sentinel\n * (which a re-added remote is not) and no two remote names collide\n * case-insensitively. Otherwise the value is reported for the operator to apply\n * by hand: a restore that might pick the wrong remote is worse than none.\n */\nasync function readLegacyRecord(\n cwd: string,\n remote: string,\n): Promise<\n | { status: \"usable\"; values: string[] }\n | { status: \"nothing-recorded\" | \"failed\"; reason?: string }\n> {\n const values = await readLocalAll(cwd, `arcane.originalPushUrl.${remote}`);\n if (values.length === 0) return { status: \"nothing-recorded\" };\n\n const current = await effectivePushUrls(cwd, remote);\n if (!current.includes(DISABLED_PUSH_URL)) {\n return {\n status: \"failed\",\n reason:\n `found a push URL recorded by 0.20.0 (\"${values.join(\", \")}\") but this remote is not ` +\n \"currently blocked by Arcane, so the record may belong to a different remote of the same \" +\n \"name. Not applying it — set the URL yourself if it is correct.\",\n };\n }\n\n const names = await listRemotes(cwd);\n const collides = names.some(\n (other) => other !== remote && other.toLowerCase() === remote.toLowerCase(),\n );\n if (collides) {\n return {\n status: \"failed\",\n reason:\n `this repository has remotes whose names differ only by case, and 0.20.0 stored their ` +\n `original push URLs under one shared key, so the recorded value (\"${values.join(\", \")}\") ` +\n \"cannot be attributed to a specific remote. Not applying it — set the URLs yourself.\",\n };\n }\n\n return { status: \"usable\", values };\n}\n\nfunction describeError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n return message.split(\"\\n\")[0]!.trim();\n}\n\nexport interface PushControlMessage {\n level: \"info\" | \"warning\";\n text: string;\n}\n\nexport interface BlockedPushControlsOutcome {\n hook: HookInstallOutcome;\n /** Empty when no remote exists, or when the hook refusal stopped the run first. */\n urls: PushUrlResult[];\n /** True when `stopOnHookRefusal` returned before touching any push URL. */\n stoppedAtHook: boolean;\n messages: PushControlMessage[];\n}\n\n/** True for every hook outcome that leaves the blocking hook in force. */\nexport function hookInForce(outcome: HookInstallOutcome): boolean {\n return outcome.status === \"installed\" || outcome.status === \"already-ours\";\n}\n\n/**\n * Applies `push_policy: \"blocked\"`'s two controls (ARC-034 decision 2) and\n * describes the result. Shared by `spell init` and `spell block-push`\n * (ARC-049 decision 1) so the two install exactly the same thing with the\n * same refusals. Messages are emitted as they occur, as well as returned, so\n * a throw part-way leaves the caller with everything already reported.\n *\n * `stopOnHookRefusal` returns before any push URL is touched when the hook is\n * refused: `block-push` refuses outright rather than leave half a block in a\n * repository whose manifest will not claim it. `init` keeps applying the URL\n * layer regardless, as it always has.\n */\nexport async function applyBlockedPushControls(\n cwd: string,\n options: { stopOnHookRefusal?: boolean; emit?: (message: PushControlMessage) => void } = {},\n): Promise<BlockedPushControlsOutcome> {\n const messages: PushControlMessage[] = [];\n const say = (level: PushControlMessage[\"level\"], text: string): void => {\n const message = { level, text };\n messages.push(message);\n options.emit?.(message);\n };\n\n const hook = await installPrePushHook(cwd);\n if (hook.status === \"refused-unreadable-config\") {\n // Fail closed: we could not determine whether another hook manager\n // owns core.hooksPath, and installing on a guess could disable it.\n say(\n \"warning\",\n \"Did not install the pre-push hook: git could not report the current core.hooksPath \" +\n \"(unreadable config, or a git too old for `--show-scope`). Installing without knowing \" +\n \"whether another hook manager owns that setting could silently disable it. Fix the \" +\n \"config and run `spell doctor`.\",\n );\n } else if (hook.status === \"refused-default-hooks\") {\n // Same harm as a foreign core.hooksPath, reached by the route the R7\n // guard wasn't watching: this repository's hooks live in git's\n // default directory and have no config key to collide with, so\n // taking the slot would switch every one of them off silently.\n say(\n \"warning\",\n `Did not install the pre-push hook: this repository has hooks in git's default ` +\n `directory (${hook.hooks.join(\", \")}). Setting core.hooksPath would silently stop ` +\n `them running. Move them under \\`${ARCANE_HOOKS_DIR}\\` yourself and re-run, or add ` +\n \"the push guard to your existing pre-push hook.\",\n );\n } else if (hook.status === \"refused-foreign-hooks-path\") {\n // R7: core.hooksPath is one exclusive slot, at local OR global\n // scope, so pointing it at Arcane would silently disable whatever\n // hook manager already owns it.\n say(\n \"warning\",\n `Did not install the pre-push hook: core.hooksPath is already \"${hook.existing}\" ` +\n `(${describeConfigScope(hook.scope)}), so another hook manager owns it. Overwriting ` +\n \"would silently disable those hooks. Chain an Arcane pre-push guard into that \" +\n \"directory yourself, or unset core.hooksPath first.\",\n );\n } else {\n say(\"info\", \"Installed a pre-push hook that blocks pushes from this repository.\");\n }\n\n if (options.stopOnHookRefusal === true && !hookInForce(hook)) {\n return { hook, urls: [], stoppedAtHook: true, messages };\n }\n\n const urls = await disablePushUrls(cwd);\n const unprotected = urls.filter((u) => u.status === \"failed\");\n if (unprotected.length > 0) {\n // Never let a partial application read as a full one.\n say(\n \"warning\",\n `Could not disable the push URL for: ${unprotected\n .map((u) => `${u.remote} (${u.reason ?? \"unknown error\"})`)\n .join(\"; \")}. Those remotes are still pushable with \\`--no-verify\\`. ` +\n \"Run `spell doctor` — it lists exactly which remotes are still live.\",\n );\n }\n if (urls.length === 0) {\n // Be precise: nothing is protecting a remote added later, because\n // this only disables remotes that exist right now. Claiming\n // otherwise would be exactly the false confidence ARC-034 warns of.\n say(\n \"warning\",\n \"No remote is configured, so only the pre-push hook is active. A remote added later \" +\n \"will NOT have its push URL disabled automatically, and a `--no-verify` push to it \" +\n \"would succeed. Run `spell doctor` after adding one — it reports this gap.\",\n );\n } else {\n const covered = urls.filter((u) => u.status !== \"failed\").map((u) => u.remote);\n if (covered.length > 0) {\n say(\"info\", `Disabled the push URL for: ${covered.join(\", \")} (fetch still works).`);\n }\n }\n say(\"info\", \"Run `spell unblock-push` from a terminal to undo this.\");\n return { hook, urls, stoppedAtHook: false, messages };\n}\n","/**\n * Regex-based credential-pattern denylist, sharing denylist-scan.ts's generic\n * file-walking/matching engine (also used by org-token-lint.ts and spell\n * ward). Extracted from scripts/copy-assets.ts (ARC-037) so the same pattern\n * set backs both the pre-existing src/assets/-scoped copy-time scan and the\n * repository-wide CI backstop / pre-commit hook this ADR adds.\n *\n * Patterns are deliberately conservative -- common credential shapes. A false\n * positive blocks a build or a commit, which is the safe direction to fail.\n */\nimport type { DenylistRule } from \"./denylist-scan.js\";\nimport { installHook, isHookInstalled, removeHook, type HookInstallOutcome } from \"./push-safety.js\";\n\nexport const SECRETS_RULES: DenylistRule[] = [\n { label: \"api-key\", pattern: /API[_-]KEY\\s*[:=]\\s*\\S+/i },\n // Case-sensitive and requiring an all-caps/underscore identifier (SECRET,\n // MY_SECRET, API_SECRET_KEY...) -- the real config/env-var convention for a\n // credential-holding key. Widening this repository-wide (ARC-037 decision 3)\n // surfaced that the original case-insensitive form matched ANY lowercase\n // `secret`/`token` code identifier (e.g. `const token = value.trim();`),\n // which is overwhelmingly a parsed/resolved value, never a literal\n // credential. The `(?!\\$?\\{)` guard additionally excludes any\n // brace-delimited placeholder -- GitHub Actions/templating references\n // (`TOKEN: ${{ secrets.X }}`, a reference to a secret store, never the\n // secret itself) and this codebase's own documented `{UPPER_SNAKE}`\n // placeholder convention (denylist-scan.ts's `scanFile` already strips\n // those before matching for the sibling org-token scan; a bare single\n // brace, with no `scanFile`-style stripping in the copy-time call path,\n // needed the same exclusion here rather than only the double-brace case).\n // A minimum length of 8 further excludes short non-secret values like\n // `write` (`id-token: write`).\n { label: \"generic-secret\", pattern: /\\b[A-Z0-9_]*SECRET[A-Z0-9_]*\\s*[:=]\\s*(?!\\$?\\{)\\S{8,}/ },\n { label: \"generic-token\", pattern: /\\b[A-Z0-9_]*TOKEN[A-Z0-9_]*\\s*[:=]\\s*(?!\\$?\\{)\\S{8,}/ },\n { label: \"bearer-token\", pattern: /Bearer\\s+[A-Za-z0-9._~+/-]{20,}/ }, // real tokens are long\n { label: \"openai-key\", pattern: /sk-[A-Za-z0-9]{20,}/ },\n { label: \"pem-key\", pattern: /-----BEGIN [A-Z ]+-----/ },\n { label: \"slack-token\", pattern: /xox[bpars]-[A-Za-z0-9-]+/ },\n { label: \"aws-access-key\", pattern: /AKIA[0-9A-Z]{16}/ },\n { label: \"github-pat\", pattern: /ghp_[A-Za-z0-9]{36}/ },\n];\n\n/**\n * Back-compat plain-pattern list for `scripts/copy-assets.ts`'s existing\n * per-line copy-time scan, which predates `DenylistRule` and iterates raw\n * `RegExp`s directly. Derived from `SECRETS_RULES` so there is exactly one\n * place these patterns are authored.\n */\nexport const SECRETS_PATTERNS: RegExp[] = SECRETS_RULES.map((rule) => rule.pattern);\n\n// ─── Consumer-facing pre-commit hook (ARC-037 decision 2 / decision 4b) ──────\n// This repo's OWN pre-commit scan is a Husky step (.husky/pre-commit calls\n// `npm run doctor:leaks`); a fresh consumer repo has no Husky. This installs\n// the same `spell doctor --leaks` check via push-safety.ts's generalized,\n// push_policy-independent hook-install path (ARC-037 decision 2) instead --\n// every profile gets it regardless of push_policy, since credential leakage\n// is a risk independent of whether a repository's history may reach a\n// remote. Mandatory rather than an opt-in prompt: the check is cheap, local,\n// and `--no-verify`-bypassable by design (decision 6) -- the repository-wide\n// CI backstop this ADR also adds is the real, unavoidable defense, not this.\n\nexport const SECRETS_PRECOMMIT_HOOK_NAME = \"pre-commit\";\n\n/**\n * Pure code, not a shipped asset file -- the same shape as push-safety.ts's\n * own PRE_PUSH_HOOK_BODY. `spell` is expected on PATH per the documented\n * global-install pattern (README.md); a missing binary skips the scan rather\n * than blocking the commit; a misconfigured PATH has nothing to do with\n * whatever the operator is actually committing.\n */\nexport const SECRETS_PRECOMMIT_HOOK_BODY = `#!/bin/sh\n# Installed by Arcane (ARC-037) to scan staged content for accidentally\n# committed credentials before they enter history.\n#\n# This is deliberately not tamper-proof: \\`git commit --no-verify\\` skips it\n# entirely. The real defense is the repository-wide scan Arcane's own build\n# runs in CI on every push -- this hook exists to catch a leak locally, for\n# free, before it ever reaches a commit.\nif ! command -v spell >/dev/null 2>&1; then\n echo \"arcane: 'spell' not found on PATH -- skipping secrets scan (see: npm install -g arcane-cli)\" >&2\n exit 0\nfi\nspell doctor --leaks\n`;\n\nexport async function installSecretsPrecommitHook(cwd: string): Promise<HookInstallOutcome> {\n return installHook(cwd, SECRETS_PRECOMMIT_HOOK_NAME, SECRETS_PRECOMMIT_HOOK_BODY);\n}\n\nexport async function isSecretsPrecommitHookInstalled(cwd: string): Promise<boolean> {\n return isHookInstalled(cwd, SECRETS_PRECOMMIT_HOOK_NAME, SECRETS_PRECOMMIT_HOOK_BODY);\n}\n\nexport async function removeSecretsPrecommitHook(cwd: string): Promise<void> {\n return removeHook(cwd, SECRETS_PRECOMMIT_HOOK_NAME);\n}\n","import { readFile, stat, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { copyFile, fileExists, hashFile, listDirectoryFiles, removeEmptyAncestors, removeWithin } from \"./copier.js\";\nimport type { RegistryComponent } from \"../types.js\";\n\nexport type DestinationState = \"missing\" | \"identical\" | \"differs\" | \"directory\";\n\nexport interface PlannedFile {\n /** Installed path, relative to the repository root. */\n file: string;\n /** Absolute path of the packaged file it is copied from. */\n src: string;\n state: DestinationState;\n}\n\n/**\n * What installing one component would do, decided from the disk before\n * anything is written. `spell add` and `spell update` both consume it, so a\n * dry run and the real run cannot disagree (ARC-052 decision 3).\n */\nexport interface InstallPlan {\n component: RegistryComponent;\n /** Every file the component ships, directories flattened. */\n files: PlannedFile[];\n /** Files that will be written: absent, identical (adopted), or present under `force`. */\n toCopy: PlannedFile[];\n /** Present and user-owned (`skipExisting`): left alone, and not recorded. */\n kept: PlannedFile[];\n /** Present and not user-owned: refuse unless the caller forces. A directory is always refused. */\n conflicts: PlannedFile[];\n}\n\nexport async function planComponentInstall(\n component: RegistryComponent,\n targetDir: string,\n assetsDir: string,\n opts: { force?: boolean } = {},\n): Promise<InstallPlan> {\n const files: PlannedFile[] = [];\n const stateOf = async (file: string, src: string): Promise<PlannedFile> => {\n const dest = join(targetDir, file);\n if (!(await fileExists(dest))) return { file, src, state: \"missing\" };\n if ((await stat(dest)).isDirectory()) return { file, src, state: \"directory\" };\n const same = (await hashFile(dest)) === (await hashFile(src));\n return { file, src, state: same ? \"identical\" : \"differs\" };\n };\n for (const file of component.files) {\n files.push(await stateOf(file, join(assetsDir, component.sourceOverrides?.[file] ?? file)));\n }\n for (const dir of component.directories ?? []) {\n const srcDir = join(assetsDir, dir);\n for (const file of await listDirectoryFiles(srcDir, dir)) {\n files.push(await stateOf(file, join(assetsDir, file)));\n }\n }\n\n const of = (...states: DestinationState[]) => files.filter((f) => states.includes(f.state));\n // --force overwrites a file's content; it never deletes a tree, so a directory stays a conflict.\n if (opts.force) {\n return { component, files, toCopy: of(\"missing\", \"identical\", \"differs\"), kept: [], conflicts: of(\"directory\") };\n }\n if (component.skipExisting) {\n return { component, files, toCopy: of(\"missing\"), kept: of(\"identical\", \"differs\", \"directory\"), conflicts: [] };\n }\n return { component, files, toCopy: of(\"missing\", \"identical\"), kept: [], conflicts: of(\"differs\", \"directory\") };\n}\n\nexport interface InstalledFiles {\n files: string[];\n fileHashes: Record<string, string>;\n}\n\n/**\n * Writes the plan's `toCopy` set and returns what to record in the manifest.\n * A failure part-way undoes the files already written (a created file is\n * removed, an overwritten one gets its previous content back) before the\n * error is rethrown, so nothing is half-installed and unrecorded.\n */\nexport async function executeInstallPlan(plan: InstallPlan, targetDir: string): Promise<InstalledFiles> {\n const files: string[] = [];\n const fileHashes: Record<string, string> = {};\n const written: { file: string; previous: Buffer | null }[] = [];\n try {\n for (const { file, src, state } of plan.toCopy) {\n const previous = state === \"missing\" ? null : await readFile(join(targetDir, file));\n fileHashes[file] = await copyFile(src, targetDir, file, { force: true });\n written.push({ file, previous });\n files.push(file);\n }\n } catch (err) {\n await rollback(written, targetDir);\n throw err;\n }\n return { files, fileHashes };\n}\n\nasync function rollback(written: { file: string; previous: Buffer | null }[], targetDir: string): Promise<void> {\n for (const { file, previous } of [...written].reverse()) {\n try {\n if (previous === null) {\n await removeWithin(targetDir, file);\n await removeEmptyAncestors(targetDir, file);\n } else {\n await writeFile(join(targetDir, file), previous);\n }\n } catch {\n // Best effort: the error that stopped the install is the one to report.\n }\n }\n}\n","import {\n readManifest,\n writeManifest,\n addComponent,\n hasComponent,\n ManifestNotFoundError,\n} from \"../modules/manifest.js\";\nimport { getComponent, ComponentNotFoundError } from \"../modules/registry.js\";\nimport {\n planComponentInstall,\n executeInstallPlan,\n type InstallPlan,\n type PlannedFile,\n} from \"../modules/component-install.js\";\nimport type { InstalledComponent, SpellAddOptions } from \"../types.js\";\n\ntype AddOutcome = \"added\" | \"skipped\" | \"failed\";\n\nfunction describeExisting(f: PlannedFile): string {\n if (f.state === \"directory\") return \"is a directory\";\n return f.state === \"identical\" ? \"identical to the packaged file\" : \"differs from the packaged file\";\n}\n\nfunction directoryConflict(plan: InstallPlan): PlannedFile | undefined {\n return plan.conflicts.find((f) => f.state === \"directory\");\n}\n\nfunction printDryRun(name: string, plan: InstallPlan, options: SpellAddOptions): void {\n for (const f of plan.files) {\n if (f.state === \"missing\") {\n console.log(` [dry-run] Would copy: ${f.file}`);\n } else if (f.state === \"directory\" && !(plan.component.skipExisting && !options.force)) {\n console.log(` [dry-run] Would refuse: ${f.file} (exists, is a directory) — remove it first`);\n } else if (options.force) {\n console.log(` [dry-run] Would overwrite: ${f.file} (exists, ${describeExisting(f)})`);\n } else if (plan.component.skipExisting) {\n console.log(` [dry-run] Would keep: ${f.file} (exists, yours, ${describeExisting(f)})`);\n } else if (f.state === \"identical\") {\n console.log(` [dry-run] Would adopt: ${f.file} (exists, identical to the packaged file)`);\n } else {\n console.log(` [dry-run] Would refuse: ${f.file} (exists, ${describeExisting(f)}) — needs --force`);\n }\n }\n const dir = directoryConflict(plan);\n if (dir) {\n console.log(`\\n[dry-run] Would refuse component \"${name}\": a directory is at \"${dir.file}\", which --force does not replace. Remove it first.`);\n return;\n }\n if (plan.conflicts.length > 0) {\n console.log(\n `\\n[dry-run] Would refuse component \"${name}\": ${plan.conflicts.length} existing file${plan.conflicts.length === 1 ? \"\" : \"s\"}. Re-run with --force to overwrite.`,\n );\n return;\n }\n console.log(`\\n[dry-run] Would add component \"${name}\" \\u2014 ${plan.toCopy.length} files`);\n}\n\nasync function addOne(\n name: string,\n options: SpellAddOptions,\n targetDir: string,\n assetsDir: string,\n packageVersion: string,\n): Promise<AddOutcome> {\n const manifest = await readManifest(targetDir);\n\n let component;\n try {\n component = getComponent(name);\n } catch (err) {\n if (err instanceof ComponentNotFoundError) {\n console.error(err.message);\n return \"failed\";\n }\n throw err;\n }\n\n if (hasComponent(manifest, name)) {\n console.log(\n `Component \"${name}\" is already installed. Use \"spell update\" to update it.`,\n );\n return \"skipped\";\n }\n\n const plan = await planComponentInstall(component, targetDir, assetsDir, { force: options.force });\n\n if (options.dryRun) {\n printDryRun(name, plan, options);\n return plan.conflicts.length > 0 ? \"failed\" : \"added\";\n }\n\n const dir = directoryConflict(plan);\n if (dir) {\n console.error(\n `Cannot add \"${name}\": \"${dir.file}\" is a directory, which --force does not replace. Nothing was written. Remove it first.`,\n );\n return \"failed\";\n }\n if (plan.conflicts.length > 0) {\n const others = plan.conflicts.length - 1;\n console.error(\n `Cannot add \"${name}\": \"${plan.conflicts[0]!.file}\" already exists${others > 0 ? ` (and ${others} more)` : \"\"}. Nothing was written. Use --force to overwrite.`,\n );\n return \"failed\";\n }\n\n const { files, fileHashes } = await executeInstallPlan(plan, targetDir);\n for (const f of plan.kept) console.log(` Kept existing (yours): ${f.file}`);\n\n const newComponent: InstalledComponent = {\n name,\n files,\n installedVersion: packageVersion,\n fileHashes,\n };\n await writeManifest(targetDir, addComponent(manifest, newComponent));\n\n console.log(\n `\\n\\u2713 Added component \"${name}\" \\u2014 ${files.length} files copied${plan.kept.length > 0 ? `, ${plan.kept.length} kept` : \"\"}`,\n );\n return \"added\";\n}\n\n/**\n * Runs the `spell add <component...>` command: each name in order, stopping\n * at the first failure (R-293c); a dry run walks every name so it reports all\n * the refusals at once. A name already installed is skipped, not a\n * failure. A refusal is one error line and exit 1, with nothing written for\n * that component (R-294b).\n *\n * @param names Component names to install\n * @param options CLI flags (force, dryRun)\n * @param targetDir Directory containing the Arcane installation\n * @param assetsDir Path to the bundled assets root\n * @param packageVersion Current package version string\n */\nexport async function runAdd(\n names: string[],\n options: SpellAddOptions,\n targetDir: string,\n assetsDir: string,\n packageVersion: string,\n): Promise<void> {\n try {\n await readManifest(targetDir);\n } catch (err) {\n if (err instanceof ManifestNotFoundError) {\n console.error(\n 'Not initialized. Run \"spell init\" first before adding components.',\n );\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n throw err;\n }\n\n const added: string[] = [];\n const refused: string[] = [];\n for (const [index, name] of names.entries()) {\n const outcome = await addOne(name, options, targetDir, assetsDir, packageVersion);\n if (outcome === \"added\") added.push(name);\n if (outcome !== \"failed\") continue;\n refused.push(name);\n if (options.dryRun) continue;\n if (names.length > 1) {\n const notAttempted = names.slice(index + 1);\n console.error(\n `Added: ${added.length > 0 ? added.join(\", \") : \"none\"}.${notAttempted.length > 0 ? ` Not attempted: ${notAttempted.join(\", \")}.` : \"\"}`,\n );\n }\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n if (refused.length > 0) {\n console.error(`[dry-run] Would refuse: ${refused.join(\", \")}.`);\n process.exit(1);\n }\n}\n","import { dirname, join } from \"node:path\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport {\n copyFile,\n copyDirectory,\n fileExists,\n fileMatchesHash,\n hashFile,\n removeEmptyAncestors,\n removeWithin,\n validateTargetPath,\n} from \"../modules/copier.js\";\nimport {\n readManifest,\n writeManifest,\n ManifestNotFoundError,\n} from \"../modules/manifest.js\";\nimport { inspectGitRepository } from \"../modules/git.js\";\nimport {\n getComponent,\n ComponentNotFoundError,\n SPELL_COMPONENT_NAMES,\n LEGACY_COMPONENT_MIGRATIONS,\n COMPONENT_FILE_MOVES,\n REGISTRY_RETIREMENTS,\n} from \"../modules/registry.js\";\nimport {\n MANIFEST_RETROFITS,\n runManifestRetrofits,\n offerRegistryScaffold,\n pushPolicyNotice,\n describeFlagPatch,\n hasManifestFlags,\n manifestFlagLine,\n resolveManifestFlags,\n ManifestFlagError,\n} from \"../modules/hub.js\";\nimport { merge3 } from \"../modules/merge3.js\";\nimport {\n findMissingRequires,\n findNewlyAvailable,\n findTrackedRetirements,\n formatRegistryChanges,\n type RegistryChanges,\n} from \"../modules/registry-changes.js\";\nimport {\n describeInstallPlan,\n executeUpdateInstalls,\n offerableNewComponents,\n planUpdateInstalls,\n selectNewComponents,\n type UpdateInstallPlan,\n} from \"../modules/update-installs.js\";\nimport { fetchPublishedFile } from \"../modules/npm-registry.js\";\nimport { isClientShimPath } from \"../modules/spell-compiler.js\";\nimport {\n componentForScope,\n componentForSpellScope,\n describeFanoutOutcomes,\n effectiveSpellScope,\n misplacedUserManifestMessage,\n resolveInstallScope,\n spellIdsInStore,\n SPELL_FANOUT_CLIENTS,\n syncUserTierFanout,\n userTierRoot,\n} from \"../modules/user-tier.js\";\nimport type {\n ArcaneManifest,\n InstallScope,\n InstalledComponent,\n RegistryComponent,\n SpellUpdateOptions,\n} from \"../types.js\";\n\n/**\n * The `default_spell_scope` change a run should record, or `undefined` when it\n * asks for nothing new (ARC-048).\n *\n * Store-only and preference-only: it decides which answer `spell init`\n * pre-selects for its scope question in a NEW repository on this machine. It\n * never touches an existing repository's manifest, and it does not change what\n * an absent `spell_scope` means -- that stays `\"repo\"` permanently.\n */\n/**\n * Warns, loudly, when a repository depends on a user tier that is not there\n * (ARC-048's gate). `spell doctor`'s scope check already fails on this, but\n * nothing runs `spell doctor` -- no workflow, no hook and no spell -- so it\n * fires when a human types it, which is not when the failure happens.\n * `spell update` is a command people DO run, and a run that says nothing is\n * how a silent opt-out stays silent.\n *\n * Returns the lines rather than printing them, so a test can read them.\n *\n * The home is resolved on its own, NOT from `runUpdate`'s `homeDir`: that one\n * is `dirname(targetDir)`, which is the home only on a user-tier run. On a\n * repository run it is the repository's PARENT DIRECTORY, and an early version\n * of this warning named it -- pointing the operator at a `.arcane` beside\n * their project that was never going to exist.\n */\nexport async function missingTierWarning(\n scope: InstallScope,\n spellScope: InstallScope,\n homeDir?: string,\n): Promise<string[]> {\n if (scope !== \"repo\" || spellScope !== \"user\") return [];\n const storeRoot = homeDir === undefined ? userTierRoot() : userTierRoot(homeDir);\n try {\n await readManifest(storeRoot);\n return [];\n } catch {\n return [\n \"\",\n ` ! This repository takes its spells from the user tier, and ${storeRoot} is not installed.`,\n \" No client can reach a spell here: the routing block in CLAUDE.md and AGENTS.md still\",\n \" names spells that resolve to nothing, so an agent improvises the workflow instead of\",\n \" failing. Fix it with one of:\",\n \"\",\n \" spell init --user # install the tier on this machine\",\n ' # or set \"spell_scope\": \"repo\" in .arcane.json and re-run `spell update`',\n \"\",\n ];\n }\n}\n\n/**\n * Names what the flags recorded, then the ARC-049 notice if push_policy\n * changed by any route in this run. Update installs no push controls.\n */\nfunction reportFlagPatch(\n flagPatch: Partial<ArcaneManifest>,\n before: ArcaneManifest,\n after: ArcaneManifest,\n dryRun: boolean | undefined,\n): void {\n console.log(`\\n${dryRun ? \"[dry-run] Would record\" : \"Recorded\"} from flags:`);\n for (const line of describeFlagPatch(flagPatch)) console.log(` ${line}`);\n if (!dryRun && after.push_policy !== before.push_policy) {\n for (const line of pushPolicyNotice(after.push_policy)) console.log(line);\n }\n}\n\nexport function defaultScopePatch(\n options: SpellUpdateOptions,\n manifest: ArcaneManifest,\n): { default_spell_scope: InstallScope } | undefined {\n if (options.defaultScope === undefined) return undefined;\n if (manifest.default_spell_scope === options.defaultScope) return undefined;\n return { default_spell_scope: options.defaultScope };\n}\n\n/**\n * Runs the `spell update` command.\n *\n * For each installed component, looks up the current registry definition and\n * re-copies files from the current package assets. User-owned files in a\n * skipExisting component are preserved when present and backfilled when absent.\n * The manifest is updated with the new file paths so future updates remain\n * correct even when source paths change between versions.\n *\n * @param options CLI flags (dryRun)\n * @param targetDir Directory containing the Arcane installation\n * @param assetsDir Path to the bundled assets root\n * @param packageVersion Current package version string\n */\n/**\n * Rewrites manifest entries whose component was split or renamed in a later\n * release, so `update` can refresh their files instead of skipping them.\n *\n * Deterministic and idempotent: an already-migrated manifest contains no\n * legacy names, so this returns it unchanged. Entry order is preserved, with\n * each legacy entry's replacements expanded in place. Replacements inherit the\n * legacy entry's `installedVersion` and an empty file list -- the caller\n * re-derives each replacement's files from the registry, which is the source\n * of truth for what a component contains *now*.\n *\n * What the registry cannot supply is the set of paths the legacy component\n * actually wrote into this repository. A spell retired since that install\n * belongs to no current component, so once its entry is expanded, nothing\n * would ever look at it again: not reported, not prunable, and dropped from\n * the manifest in the same run. Those paths and their recorded hashes are\n * therefore returned separately as `inherited`, for the caller to feed through\n * the same orphan resolution every other tracked-but-unclaimed file gets.\n * Discarding them -- which this function used to do -- is exactly how a\n * consumer upgrading across the split kept a dead spell on disk forever.\n *\n * Both legacy spell components map to the same replacement set, so a manifest\n * listing both (the common case) is deduped rather than producing doubles.\n * `carrier` names the replacement that adopts any inherited file still on\n * disk, so a file Arcane wrote stays tracked instead of being forgotten.\n */\nexport interface LegacyMigration {\n components: InstalledComponent[];\n inherited: {\n files: string[];\n fileHashes: Record<string, string>;\n /** null when the manifest held no legacy entry at all. */\n carrier: string | null;\n };\n}\n\nexport function migrateLegacyComponents(components: InstalledComponent[]): LegacyMigration {\n const out: InstalledComponent[] = [];\n const seen = new Set<string>();\n const inheritedFiles: string[] = [];\n const inheritedHashes: Record<string, string> = {};\n let carrier: string | null = null;\n\n for (const entry of components) {\n const replacements = LEGACY_COMPONENT_MIGRATIONS[entry.name];\n if (!replacements) {\n if (seen.has(entry.name)) continue;\n seen.add(entry.name);\n out.push(entry);\n continue;\n }\n\n for (const file of entry.files) {\n if (!inheritedFiles.includes(file)) inheritedFiles.push(file);\n const hash = entry.fileHashes?.[file];\n // First writer wins -- deterministic given entry order, and both legacy\n // names describe the same physical install anyway.\n if (hash !== undefined && inheritedHashes[file] === undefined) {\n inheritedHashes[file] = hash;\n }\n }\n\n for (const name of replacements) {\n carrier ??= name;\n if (seen.has(name)) continue;\n seen.add(name);\n out.push({ name, files: [], installedVersion: entry.installedVersion });\n }\n }\n\n return {\n components: out,\n inherited: { files: inheritedFiles, fileHashes: inheritedHashes, carrier },\n };\n}\n\n/**\n * Re-homes tracked files that moved between two live components\n * (COMPONENT_FILE_MOVES), keeping each file's recorded hash. Idempotent.\n *\n * The destination entry is created only when the source actually tracked a\n * moved file. One that never did -- the operator's own file, preserved and\n * never recorded -- leaves the destination uninstalled, so the\n * newly-available report can offer it (or skip it, when the file is already\n * there) instead of `update` claiming a component Arcane never wrote.\n */\nexport function moveComponentFiles(\n components: InstalledComponent[],\n present: (file: string) => boolean = () => true,\n): InstalledComponent[] {\n let out = components;\n for (const move of COMPONENT_FILE_MOVES) {\n const from = out.find((c) => c.name === move.from);\n const tracked = from?.files.filter((f) => move.files.includes(f)) ?? [];\n if (!from || tracked.length === 0) continue;\n // A moved file already deleted from disk is dropped, not re-homed: an\n // entry with no files would make `spell add` refuse it forever, while an\n // absent entry lets the newly-available report offer a working `spell add`.\n const moving = tracked.filter(present);\n const dropped = tracked.filter((f) => !present(f));\n\n const movedHashes: Record<string, string> = {};\n const keptHashes: Record<string, string> = {};\n for (const [file, hash] of Object.entries(from.fileHashes ?? {})) {\n if (dropped.includes(file)) continue;\n (moving.includes(file) ? movedHashes : keptHashes)[file] = hash;\n }\n const newFrom: InstalledComponent = {\n ...from,\n files: from.files.filter((f) => !tracked.includes(f)),\n ...(from.fileHashes ? { fileHashes: keptHashes } : {}),\n };\n const existing = out.find((c) => c.name === move.to);\n const newTo: InstalledComponent = existing\n ? {\n ...existing,\n files: [...existing.files, ...moving.filter((f) => !existing.files.includes(f))],\n fileHashes: { ...movedHashes, ...existing.fileHashes },\n }\n : {\n name: move.to,\n files: moving,\n installedVersion: from.installedVersion,\n ...(from.fileHashes ? { fileHashes: movedHashes } : {}),\n };\n\n out = out.flatMap((c) => {\n if (c === from) return existing || moving.length === 0 ? [newFrom] : [newFrom, newTo];\n if (c === existing) return [newTo];\n return [c];\n });\n }\n return out;\n}\n\nexport type OrphanStatus = \"pruned\" | \"reported\" | \"not-found\";\n\n/**\n * Resolves one orphaned file: previously tracked, no longer part of any\n * current component (TODO.md T10). Report-only unless `prune` is set, and\n * even then only deletes when the on-disk content still matches the hash\n * Arcane last recorded -- an operator edit is exactly as worth preserving\n * here as it is in the main update loop (ARC-038 decision 1's \"do not\n * silently discard the edit\" applies to deleting a file, not only\n * overwriting one). A file with no recorded hash (predates ARC-038, or was\n * never hashed) is reported but never auto-pruned -- there is nothing to\n * verify it against, and guessing is not this feature's job.\n */\nexport async function resolveOrphan(\n targetDir: string,\n file: string,\n recordedHash: string | undefined,\n prune: boolean,\n): Promise<OrphanStatus> {\n // A manifest path is repository content: one that resolves outside the\n // target directory is never read, hashed or deleted -- the same guard every\n // write path applies, reported rather than acted on.\n try {\n validateTargetPath(targetDir, file);\n } catch {\n console.log(` ! Refused (path escapes the target directory): ${file}`);\n return \"reported\";\n }\n const filePath = join(targetDir, file);\n if (!(await fileExists(filePath))) return \"not-found\";\n if (!prune) return \"reported\";\n\n if (recordedHash === undefined) {\n console.log(` ! Orphaned, not pruned (no recorded hash to verify it's untouched): ${file}`);\n return \"reported\";\n }\n if (!(await fileMatchesHash(filePath, recordedHash))) {\n console.log(` ! Orphaned but edited since install — not pruning: ${file}`);\n return \"reported\";\n }\n await removeWithin(targetDir, file);\n // A component's files often sit in a directory of their own\n // (.agents/skills/<id>/SKILL.md); leaving 41 empty directories behind is\n // not \"pruned\".\n await removeEmptyAncestors(targetDir, file);\n console.log(` Pruned orphaned file: ${file}`);\n return \"pruned\";\n}\n\n/**\n * Whether a retired user-owned component's file is the operator's to keep:\n * on disk, and either edited since Arcane wrote it or never hashed by Arcane\n * (pre-dates ARC-038, or Arcane kept the operator's own copy at install).\n * Such a file leaves the manifest instead of being reported as an orphan on\n * every run -- the orphan path protects files Arcane owns, and a\n * `skipExisting` file was the operator's from the moment it was written\n * (#328). An untouched copy is not released: it is still the scaffold exactly\n * as shipped, and `--prune` is the right way to remove it. A path that\n * escapes the target directory is never read; resolveOrphan refuses it.\n */\nasync function isReleasableUserOwnedFile(\n targetDir: string,\n file: string,\n recordedHash: string | undefined,\n): Promise<boolean> {\n try {\n validateTargetPath(targetDir, file);\n } catch {\n return false;\n }\n const filePath = join(targetDir, file);\n if (!(await fileExists(filePath))) return false;\n if (recordedHash === undefined) return true;\n return !(await fileMatchesHash(filePath, recordedHash));\n}\n\n/**\n * An orphan that is still ON DISK stays in the manifest, with whatever hash\n * was recorded for it. The manifest records what Arcane put in this\n * repository, and the file is still there: dropping the entry would untrack a\n * file Arcane wrote, which `spell uninstall` then leaks (the hazard the\n * preserveExisting branch names), would overwrite an operator's edit without a\n * word if the component is ever re-adopted, and would make the remedy `update`\n * prints a lie -- a report-only run that forgot its own orphans leaves\n * `spell update --prune` with nothing to find. Reported every run until it is\n * pruned or removed by hand; dropped as soon as it is (\"pruned\"/\"not-found\").\n */\nasync function retainReportedOrphan(\n targetDir: string,\n file: string,\n recordedHash: string | undefined,\n into: InstalledComponent,\n): Promise<void> {\n try {\n validateTargetPath(targetDir, file);\n } catch {\n // Escaping path: refused and reported by resolveOrphan, and deliberately\n // dropped from the manifest -- it is not a file Arcane could have written.\n return;\n }\n if (!(await fileExists(join(targetDir, file)))) return;\n\n if (!into.files.includes(file)) into.files.push(file);\n if (recordedHash !== undefined) {\n into.fileHashes ??= {};\n into.fileHashes[file] = recordedHash;\n }\n}\n\n/**\n * Tracked files that should be on disk but are not. Feeds the same-version\n * restore path in `runUpdate` (ARC-045 / CS-03): an operator who has ported a\n * customized client shim's edits into the canonical spell deletes the old\n * file and runs `spell update` to get the generated shim back -- at the\n * same version, since nothing else changed. Only files the current registry\n * still ships for that component count (anything else is an orphan, handled\n * by `resolveOrphan`). `initOnly` components never do -- `update` must not\n * create those at any version (EF-17) -- and neither do `skipExisting`\n * (user-owned) ones: a continuity file, `.mcp.json` or `journal/.gitkeep`\n * an operator deleted on purpose is backfilled only by a version change, as\n * it always was, not on every same-version run. A component the registry no\n * longer knows has nothing to restore from.\n */\n/**\n * Spell components a repository is RE-ADOPTING: it previously opted out\n * (`spell_scope: \"user\"`, so their manifest entries were emptied and their\n * files pruned) and has now switched back. Their files are shipped by the\n * registry but neither tracked nor on disk, so without naming this case a\n * same-version `spell update` would answer \"Already up to date.\" and the\n * repository would stay empty until the next release -- a rollback that\n * silently does nothing (ARC-045 decision 4 / CS-05).\n *\n * Deliberately narrow. It does NOT relax CS-03's rule that an untracked\n * registry file is neither created nor claimed on a same-version run: only\n * a spell component whose manifest entry lists zero files qualifies, which\n * is a state nothing but the opt-out produces.\n */\nexport function findReadoptedSpellComponents(\n components: InstalledComponent[],\n scope: InstallScope = \"repo\",\n spellScope: InstallScope = \"repo\",\n): Set<string> {\n if (scope !== \"repo\" || spellScope !== \"repo\") return new Set();\n return new Set(\n components\n .filter((c) => SPELL_COMPONENT_NAMES.includes(c.name) && c.files.length === 0)\n .map((c) => c.name),\n );\n}\nexport async function findMissingTrackedFiles(\n targetDir: string,\n components: InstalledComponent[],\n scope: InstallScope = \"repo\",\n spellScope: InstallScope = \"repo\",\n readopted: Set<string> = new Set(),\n): Promise<string[]> {\n const missing: string[] = [];\n for (const installed of components) {\n let component: RegistryComponent;\n try {\n component = componentForSpellScope(\n componentForScope(getComponent(installed.name), scope),\n scope === \"repo\" ? spellScope : \"repo\",\n );\n } catch {\n continue;\n }\n if (component.initOnly || component.skipExisting) continue;\n const candidates = readopted.has(installed.name) ? component.files : installed.files;\n for (const file of candidates) {\n if (!component.files.includes(file)) continue;\n if (!(await fileExists(join(targetDir, file)))) missing.push(file);\n }\n }\n return missing;\n}\n\n/**\n * Tracked files the current registry no longer ships for their component --\n * the orphan candidates. Existence is deliberately not checked here: that is\n * `resolveOrphan`'s job, and this only has to answer whether a run has\n * anything to say at all.\n *\n * It exists because of the opt-out (ARC-045 decision 4 / CS-05). A\n * repository that sets `spell_scope: \"user\"` without a version change would\n * otherwise meet the `Already up to date.` short-circuit below -- nothing\n * missing, nothing to write -- and its now-unmanaged spell files would go\n * unmentioned until the next release. The opt-out has to be visible on the\n * very next `spell update`, which is the command an operator runs after\n * editing the field.\n */\nexport function findUnmanagedTrackedFiles(\n components: InstalledComponent[],\n scope: InstallScope = \"repo\",\n spellScope: InstallScope = \"repo\",\n): string[] {\n const unmanaged: string[] = [];\n for (const installed of components) {\n let component: RegistryComponent;\n try {\n component = componentForSpellScope(\n componentForScope(getComponent(installed.name), scope),\n scope === \"repo\" ? spellScope : \"repo\",\n );\n } catch {\n // Component gone from the registry entirely: every file it tracked is\n // an orphan, and the main loop already reports that case by itself.\n unmanaged.push(...installed.files);\n continue;\n }\n const shipped = new Set(component.files);\n for (const file of installed.files) {\n if (!shipped.has(file)) unmanaged.push(file);\n }\n }\n return unmanaged;\n}\nexport async function runUpdate(\n options: SpellUpdateOptions,\n targetDir: string,\n assetsDir: string,\n packageVersion: string,\n): Promise<void> {\n // Read existing manifest\n let manifest: ArcaneManifest;\n try {\n manifest = await readManifest(targetDir);\n } catch (err) {\n if (err instanceof ManifestNotFoundError) {\n console.error(\n `Not initialized. Run \"spell init${options.user ? \" --user\" : \"\"}\" first before updating.`,\n );\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n throw err;\n }\n // PRD D-09: re-home files that moved component before anything below\n // (up-to-date check, orphan sweep) reads the old entry as their owner.\n const movedFilesPresent = new Set<string>();\n for (const move of COMPONENT_FILE_MOVES) {\n for (const file of move.files) {\n if (await fileExists(join(targetDir, file))) movedFilesPresent.add(file);\n }\n }\n manifest = {\n ...manifest,\n components: moveComponentFiles(manifest.components, (f) => movedFilesPresent.has(f)),\n };\n\n // ARC-045 decision 3 / CS-04: the user tier at ~/.arcane is not a\n // repository -- its merge baseline is the recorded hash plus the published\n // vendor file, so the git checks below do not apply; and its client shims\n // live outside the store and are reconciled after the store itself (see\n // the fan-out calls below). Everything else in this command runs unchanged\n // over the store through componentForScope.\n const resolvedScope = resolveInstallScope(targetDir, manifest.scope, options.user);\n if (resolvedScope.misplacedUserManifest) {\n console.error(misplacedUserManifestMessage(targetDir));\n process.exit(1);\n return;\n }\n const scope: InstallScope = resolvedScope.scope;\n\n // ARC-052 / D-07: newly available components are a repository question.\n if (options.addNew && scope !== \"repo\") {\n console.error(\n \"\\n ✗ --add-new offers the components a repository's profile gained; the user tier has no profile. Run it without --user, inside a repository.\\n\",\n );\n process.exit(1);\n return;\n }\n\n // PRD D-02: validated and conflict-checked before anything is written, so a\n // refused flag (including any attempt to loosen push_policy) changes nothing.\n let flagPatch: Partial<ArcaneManifest> = {};\n if (hasManifestFlags(options.manifestFlags)) {\n if (scope !== \"repo\") {\n console.error(\n \"\\n ✗ The manifest-question flags describe a repository; the user tier has none of those fields.\\n\",\n );\n process.exit(1);\n return;\n }\n try {\n flagPatch = resolveManifestFlags(options.manifestFlags, manifest, join(targetDir, \".arcane.json\"));\n } catch (err) {\n if (!(err instanceof ManifestFlagError)) throw err;\n console.error(`\\n ✗ ${err.message}\\n`);\n process.exit(1);\n return;\n }\n }\n const answered: ArcaneManifest = { ...manifest, ...flagPatch };\n // ARC-045 decision 4 / CS-05: where THIS repository takes its spells\n // from. Absent means \"repo\", so every manifest written before 1.2.0\n // behaves exactly as it did. Meaningless for the store itself, which is\n // the thing being pointed at -- hence the scope === \"repo\" guards below.\n const spellScope: InstallScope = effectiveSpellScope(manifest);\n // Components this repository is re-adopting after opting back in; see\n // findReadoptedSpellComponents. Empty in every other case.\n const readopted = findReadoptedSpellComponents(manifest.components, scope, spellScope);\n const homeDir = dirname(targetDir);\n for (const line of await missingTierWarning(scope, spellScope)) console.log(line);\n\n if (scope === \"repo\") {\n console.warn(\n `WARNING: Arcane v${packageVersion} update safety notice: commit your work before updating.`,\n );\n console.warn(\n \"Updates can replace managed files. A clean committed baseline is required for recovery.\",\n );\n\n const gitState = await inspectGitRepository(targetDir);\n if (gitState.status === \"not-repository\") {\n console.error(\n \"Update refused: this directory is not a Git repository or Git is unavailable.\",\n );\n process.exit(1);\n return;\n }\n if (gitState.status === \"no-commits\") {\n console.error(\n \"Update refused: this repository has no commits. Commit the current baseline before updating.\",\n );\n process.exit(1);\n return;\n }\n if (gitState.uncommittedChanges > 0) {\n console.error(\n `Update refused: this repository has ${gitState.uncommittedChanges} uncommitted change${gitState.uncommittedChanges === 1 ? \"\" : \"s\"}. Commit or otherwise clean the working tree before updating.`,\n );\n process.exit(1);\n return;\n }\n }\n\n // PRD D-15, computed before anything is written. Retirements read the\n // manifest as it was, before migration renames a legacy entry away. The\n // newly-available list is repository-only: `spell add` has no --user.\n const registryChanges: RegistryChanges = {\n retired: findTrackedRetirements(manifest.components),\n ...(scope === \"repo\"\n ? {\n profile: manifest.profile,\n newlyAvailable: await findNewlyAvailable(\n targetDir,\n manifest.profile,\n migrateLegacyComponents(manifest.components).components,\n spellScope,\n ),\n missingRequires: findMissingRequires(manifest.components, manifest.profile),\n }\n : {}),\n };\n // ARC-052: beyond refreshing what is installed, `update` installs the\n // `requires` prerequisites of installed components, and the newly available\n // components the operator opted into (R-293b). The plan is made once, from\n // the disk, before anything is written: the dry run prints it and the real\n // run executes it. What is installed leaves the lists printed below.\n let installPlan: UpdateInstallPlan = { items: [], leftAlone: [] };\n if (scope === \"repo\" && manifest.components.length > 0) {\n const missingRequires = registryChanges.missingRequires ?? [];\n const selectedNew = await selectNewComponents(\n offerableNewComponents(registryChanges.newlyAvailable ?? [], missingRequires),\n { addNew: Boolean(options.addNew), interactive: Boolean(process.stdin.isTTY), dryRun: Boolean(options.dryRun) },\n );\n installPlan = await planUpdateInstalls({ targetDir, assetsDir, spellScope, missingRequires, selectedNew });\n const installing = new Set(installPlan.items.map((i) => i.plan.component.name));\n registryChanges.installedByUpdate = installing.size > 0;\n registryChanges.addNewUsed = Boolean(options.addNew);\n registryChanges.newlyAvailable = (registryChanges.newlyAvailable ?? []).filter((c) => !installing.has(c.name));\n registryChanges.missingRequires = missingRequires\n .filter((m) => !installing.has(m.name))\n .map((m) => ({ ...m, leftAloneReason: installPlan.leftAlone.find((l) => l.name === m.name)?.reason }));\n }\n const printRegistryChanges = (): void => {\n for (const line of formatRegistryChanges(registryChanges)) console.log(line);\n };\n const applyInstalls = async (): Promise<InstalledComponent[]> => {\n const declined = installPlan.leftAlone.filter((l) => l.kind === \"new\");\n if (installPlan.items.length === 0 && declined.length === 0) return [];\n console.log(options.dryRun ? \"\\n[dry-run] Components this update would install (ARC-052):\" : \"\\nComponents installed by this update (ARC-052):\");\n for (const l of declined) console.log(` ! Not installed: ${l.name} — ${l.reason}`);\n if (options.dryRun) {\n for (const line of describeInstallPlan(installPlan)) console.log(line);\n return [];\n }\n const { installed, lines } = await executeUpdateInstalls(installPlan, targetDir, packageVersion);\n for (const line of lines) console.log(line);\n return installed;\n };\n\n // Already up to date -- unless a tracked file has gone missing, in which\n // case a same-version run restores exactly that and nothing else (the\n // ARC-045 / CS-03 remedy path; see findMissingTrackedFiles).\n let sameVersionRestore = false;\n if (manifest.version === packageVersion && manifest.components.length > 0) {\n const missing = await findMissingTrackedFiles(targetDir, manifest.components, scope, spellScope, readopted);\n const unmanaged = findUnmanagedTrackedFiles(manifest.components, scope, spellScope);\n if (missing.length === 0 && unmanaged.length === 0) {\n console.log(installPlan.items.length === 0 ? \"Already up to date.\" : \"Installed components are at the current version.\");\n const newlyInstalled = await applyInstalls();\n // Still surfaced here: a component the operator has not added yet is\n // news on every run, not only on the run that changed the version.\n printRegistryChanges();\n // Flags still record their answers on a current install: a harness\n // finishing setup is not upgrading anything.\n if (scope === \"repo\" && (Object.keys(flagPatch).length > 0 || newlyInstalled.length > 0)) {\n if (!options.dryRun) {\n await writeManifest(targetDir, { ...answered, components: [...answered.components, ...newlyInstalled] });\n }\n if (Object.keys(flagPatch).length > 0) reportFlagPatch(flagPatch, manifest, answered, options.dryRun);\n }\n // The user tier's client files can go missing (or a renderer can\n // change) with the store itself intact -- reconcile them on every\n // same-version run too. A no-op when nothing changed.\n if (scope === \"user\") {\n const fanout = await syncUserTierFanout({\n ownedClients: SPELL_FANOUT_CLIENTS,\n homeDir,\n storeRoot: targetDir,\n spellIds: spellIdsInStore(manifest.components),\n previous: manifest.fanout,\n dryRun: options.dryRun,\n fallbackDir: assetsDir,\n });\n for (const line of describeFanoutOutcomes(fanout.outcomes, options.dryRun)) console.log(` ${line}`);\n const scopePatch = defaultScopePatch(options, manifest);\n if (\n !options.dryRun\n && (JSON.stringify(fanout.record) !== JSON.stringify(manifest.fanout ?? {})\n || scopePatch !== undefined)\n ) {\n await writeManifest(targetDir, { ...manifest, ...scopePatch, fanout: fanout.record });\n }\n }\n return;\n }\n sameVersionRestore = true;\n const prefix = options.dryRun ? \"[dry-run] \" : \"\";\n if (missing.length > 0) {\n console.log(\n `${prefix}Already at v${packageVersion}, but ${missing.length} tracked file${missing.length === 1 ? \" is\" : \"s are\"} missing — restoring.`,\n );\n } else {\n console.log(\n `${prefix}Already at v${packageVersion}, but ${unmanaged.length} tracked file${unmanaged.length === 1 ? \" is\" : \"s are\"} no longer managed here — reviewing.`,\n );\n }\n }\n\n if (manifest.components.length === 0) {\n console.log(\"No components installed. Nothing to update.\");\n if (scope === \"repo\" && Object.keys(flagPatch).length > 0) {\n if (!options.dryRun) await writeManifest(targetDir, answered);\n reportFlagPatch(flagPatch, manifest, answered, options.dryRun);\n }\n return;\n }\n\n let fileCount = 0;\n const updatedComponents: InstalledComponent[] = [];\n // ARC-038 decision 1: files whose merge produced genuine conflict markers,\n // collected across every component so the operator gets one summary at the\n // end rather than a per-file interruption -- the update still completes\n // for every other file either way.\n const conflictedFiles: string[] = [];\n // ARC-045 / CS-03: generated client shims the operator had edited, kept\n // untouched rather than merged -- see the per-file branch below.\n const customizedShims: string[] = [];\n // TODO.md T10: files tracked before this update that no longer belong to\n // any current component, reported always and deleted only with --prune\n // (and only when untouched -- see resolveOrphan).\n const orphanReport: Array<{ file: string; status: OrphanStatus }> = [];\n // Collected during the component loop, resolved in one sweep after it.\n // The decision cannot be made per component: `updatedFiles` below is\n // per-component, so a file another component still ships looks orphaned\n // from here. Only the union of every component's claims answers the actual\n // question -- \"is this file part of any current component?\" -- and getting\n // that wrong means deleting a live file under --prune.\n const orphanCandidates: Array<{\n owner: string;\n file: string;\n recordedHash: string | undefined;\n }> = [];\n\n const { components: componentsToUpdate, inherited } =\n migrateLegacyComponents(manifest.components);\n\n // Files a legacy component wrote, whose entry has just been expanded away.\n // They belong to no component now by construction, so they enter the sweep\n // as candidates like any other unclaimed file.\n if (inherited.carrier !== null) {\n for (const file of inherited.files) {\n orphanCandidates.push({\n owner: inherited.carrier,\n file,\n recordedHash: inherited.fileHashes[file],\n });\n }\n }\n\n for (const installed of componentsToUpdate) {\n // Look up the current registry definition (source of truth for file paths)\n let component;\n try {\n // ARC-045 decision 4 / CS-05: in a repository that takes its spells\n // from the user tier, every spells-* component installs nothing -- and\n // the files it installed before opting out fall through to the orphan\n // path below, which reports them always and deletes them only under\n // --prune, only while their recorded hash still holds.\n component = componentForSpellScope(\n componentForScope(getComponent(installed.name), scope),\n scope === \"repo\" ? spellScope : \"repo\",\n );\n } catch (err) {\n if (err instanceof ComponentNotFoundError) {\n // Component removed from registry entirely -- every file it used to\n // track is now orphaned, not just individually dropped ones. Report\n // even during dry-run (that's the point of a preview); only the\n // actual deletion is dry-run-gated, inside resolveOrphan's `prune`.\n console.log(` ! ${installed.name} not in registry — skipping.`);\n // A retired user-owned component (#328): an edited or never-hashed\n // file is the operator's and leaves the manifest; only an untouched\n // copy stays on the orphan path. See isReleasableUserOwnedFile.\n const userOwned = REGISTRY_RETIREMENTS.some(\n (r) => r.kind === \"component\" && r.name === installed.name && r.userOwned === true,\n );\n const stillTracked: string[] = [];\n for (const file of installed.files) {\n const recordedHash = installed.fileHashes?.[file];\n if (userOwned && (await isReleasableUserOwnedFile(targetDir, file, recordedHash))) {\n console.log(\n ` ${options.dryRun ? \"[dry-run] Would release\" : \"Released\"}: ${file} — yours (edited since Arcane wrote it, or never written by Arcane), no longer tracked. Delete it yourself if you do not want it.`,\n );\n continue;\n }\n const status = await resolveOrphan(\n targetDir,\n file,\n recordedHash,\n Boolean(options.prune) && !options.dryRun,\n );\n orphanReport.push({ file, status });\n if (status === \"reported\") stillTracked.push(file);\n }\n if (userOwned && !options.dryRun) {\n // Released, pruned or gone: nothing left for a later run to retry.\n if (stillTracked.length === 0) continue;\n const fileHashes: Record<string, string> = {};\n for (const file of stillTracked) {\n const hash = installed.fileHashes?.[file];\n if (hash !== undefined) fileHashes[file] = hash;\n }\n updatedComponents.push({ ...installed, files: stillTracked, fileHashes });\n continue;\n }\n // Drop the manifest entry once every file it tracked is gone or was\n // never there; otherwise keep it so a future update can retry\n // pruning what --prune (or a hash mismatch) left behind.\n if (\n options.prune &&\n !options.dryRun &&\n installed.files.every((f) => orphanReport.find((o) => o.file === f)?.status !== \"reported\")\n ) {\n continue;\n }\n updatedComponents.push(installed);\n continue;\n }\n throw err;\n }\n\n // Copy files using current registry paths (handles path changes between versions)\n const updatedFiles: string[] = [];\n const fileHashes: Record<string, string> = {};\n for (const file of component.files) {\n const srcPath = join(assetsDir, component.sourceOverrides?.[file] ?? file);\n const targetExists = await fileExists(join(targetDir, file));\n const preserveExisting = Boolean(component.skipExisting) && targetExists;\n const recordedHash = installed.fileHashes?.[file];\n\n // initOnly: update never creates these. Their appearance alone changes\n // how Git treats the whole repository, so adding one mid-life is the\n // operator's call, not a side effect of a version upgrade (EF-17). That\n // holds even for a `requires` prerequisite: update installs nothing on\n // its own except the prerequisites of installed components, and never an\n // initOnly one (ARC-052).\n if (component.initOnly && !targetExists) {\n console.log(\n ` ! Missing: ${file} — not added automatically, because doing so would change how Git treats existing files. Run \"spell add ${component.name}\" if you want it.`,\n );\n continue;\n }\n\n // Same-version restore (see the up-to-date check above): only a file\n // that is tracked, absent, and shipped by a non-user-owned component\n // falls through to be written -- exactly the set the gate counted.\n // Everything else keeps its recorded state: a present file is not\n // re-hashed or merged, an untracked registry file is neither created\n // nor claimed, and a user-owned (skipExisting) file is left to the\n // version-change backfill it always had.\n if (sameVersionRestore) {\n const tracked = installed.files.includes(file) || readopted.has(installed.name);\n const restorable = tracked && !targetExists && !component.skipExisting;\n if (!restorable) {\n if (tracked) {\n updatedFiles.push(file);\n if (recordedHash !== undefined) fileHashes[file] = recordedHash;\n }\n continue;\n }\n }\n\n // ARC-038 decision 1: on-disk content that no longer matches what\n // Arcane last wrote means the operator edited this file, and that edit\n // must never be silently discarded.\n const editedByOperator =\n recordedHash !== undefined &&\n targetExists &&\n !preserveExisting &&\n !(await fileMatchesHash(join(targetDir, file), recordedHash));\n\n if (preserveExisting) {\n // skipExisting keeps its pre-ARC-038 whole-file behavior unchanged --\n // no hash tracking, no merge machinery (ARC-038 decision 1).\n console.log(` ${options.dryRun ? \"[dry-run] Would preserve\" : \"Preserved\"}: ${file}`);\n } else if (editedByOperator && isClientShimPath(file)) {\n // ARC-045 decision 2 / CS-03: a generated client shim (Copilot\n // prompt, Claude command, Codex skill) carries no authored prose, so\n // there is nothing an operator's edit could be merged INTO. A\n // three-way merge here either reports success while leaving the edit\n // dangling under the new shim, or writes conflict markers over a body\n // that is gone -- both observed live against a real consumer fixture\n // (2026-09-09, features/codex-support/architecture.md). Keep the\n // operator's file byte-untouched, name it, and carry the previously\n // recorded hash forward: recording the edited content would make the\n // next update read the file as untouched and overwrite it; recording\n // nothing would drop it into the pre-ARC-038 overwrite path. The\n // canonical spell is written beside it as an ordinary new file, so the\n // spell keeps working in every client meanwhile.\n customizedShims.push(file);\n fileHashes[file] = recordedHash!;\n console.log(\n ` ${options.dryRun ? \"[dry-run] Would keep\" : \"Kept\"} customized (not merged): ${file}`,\n );\n } else if (options.dryRun) {\n console.log(` [dry-run] Would ${sameVersionRestore ? \"restore missing\" : \"update\"}: ${file}`);\n fileCount++;\n } else {\n let handled = false;\n\n if (editedByOperator) {\n // Fetch by the ASSET path: the published tarball holds the source\n // (`docs-baseline/gitignore`, `.arcane/spells/<id>.md`), not the\n // installed name a sourceOverrides component or the user tier's\n // store gives it.\n const oldVendorContent = await fetchPublishedFile(\n installed.installedVersion,\n component.sourceOverrides?.[file] ?? file,\n );\n if (oldVendorContent === undefined) {\n console.log(\n ` ! Could not fetch the previously published version of ${file} to merge your edits — left your version untouched. Update it manually if you want the latest.`,\n );\n // Untouched means still edited: keep the recorded hash so the\n // next update recognizes the edit again instead of reading the\n // file as Arcane-written and overwriting it.\n fileHashes[file] = recordedHash!;\n handled = true;\n } else {\n const [currentContent, newContent] = await Promise.all([\n readFile(join(targetDir, file), \"utf-8\"),\n readFile(srcPath, \"utf-8\"),\n ]);\n const result = merge3(oldVendorContent, currentContent, newContent);\n await writeFile(join(targetDir, file), result.content, \"utf-8\");\n // Record the VENDOR content's hash, not the merged file's. The\n // recorded hash means \"what a clean install of this version\n // wrote\"; recording the merged content made the next update read\n // the file as untouched and overwrite it, so an operator's edit\n // survived exactly one update (observed live 2026-09-09, variant D\n // in features/codex-support/architecture.md).\n fileHashes[file] = await hashFile(srcPath);\n if (result.hasConflict) {\n conflictedFiles.push(file);\n console.log(` ⚠ Merge conflict in ${file} — resolve the <<<<<<< markers before committing.`);\n } else {\n console.log(` Merged your edits into: ${file}`);\n }\n handled = true;\n }\n }\n\n if (!handled) {\n fileHashes[file] = await copyFile(srcPath, targetDir, file, { force: true });\n if (sameVersionRestore) console.log(` Restored missing: ${file}`);\n }\n fileCount++;\n }\n\n // Only record a preserved file if Arcane installed it in the first\n // place. Recording one it merely declined to overwrite would claim\n // ownership of an operator-authored file, and `spell uninstall` deletes\n // everything the manifest lists -- so an operator's own .gitignore or\n // DECISIONS.md would be destroyed by an uninstall that never wrote it.\n // A file Arcane DID write must stay recorded, or uninstall leaks it.\n if (!preserveExisting || installed.files.includes(file)) {\n updatedFiles.push(file);\n }\n }\n // Also copy directories\n for (const dir of component.directories ?? []) {\n const srcDirPath = join(assetsDir, dir);\n if (options.dryRun) {\n console.log(` [dry-run] Would update directory: ${dir}/`);\n } else {\n // Directory-tracked files are always freshly generated content\n // (agent rosters, etc.), never hand-edited in place -- unconditional\n // overwrite, same as before ARC-038, is still correct here.\n const copied = await copyDirectory(srcDirPath, targetDir, dir, { force: true });\n for (const { path: copiedPath, hash } of copied) {\n updatedFiles.push(copiedPath);\n fileHashes[copiedPath] = hash;\n }\n fileCount += copied.length;\n }\n }\n\n // TODO.md T10: a file this component tracked before this update but no\n // longer does (dropped from the registry's current file/directory list)\n // is orphaned -- still on disk, no longer represented in the manifest\n // this update is about to write.\n for (const file of installed.files) {\n if (updatedFiles.includes(file)) continue;\n orphanCandidates.push({\n owner: installed.name,\n file,\n recordedHash: installed.fileHashes?.[file],\n });\n }\n\n updatedComponents.push({\n ...installed,\n files: updatedFiles,\n installedVersion: packageVersion,\n fileHashes,\n });\n }\n\n updatedComponents.push(...(await applyInstalls()));\n\n // One sweep, once every component has declared what it claims. A candidate\n // some other component still ships is not an orphan at all -- the check a\n // per-component loop structurally cannot make, and getting it wrong means\n // deleting a live file under --prune.\n const claimed = new Set(updatedComponents.flatMap((c) => c.files));\n const resolvedOrphans = new Set<string>();\n\n for (const { owner, file, recordedHash } of orphanCandidates) {\n if (claimed.has(file) || resolvedOrphans.has(file)) continue;\n resolvedOrphans.add(file);\n\n const status = await resolveOrphan(\n targetDir,\n file,\n recordedHash,\n Boolean(options.prune) && !options.dryRun,\n );\n orphanReport.push({ file, status });\n if (status !== \"reported\") continue;\n\n let into = updatedComponents.find((c) => c.name === owner);\n if (!into) {\n // The owner's entry was dropped (gone from the registry, fully pruned),\n // but this file is still on disk, so it still needs somewhere to live.\n into = { name: owner, files: [], installedVersion: packageVersion, fileHashes: {} };\n updatedComponents.push(into);\n }\n await retainReportedOrphan(targetDir, file, recordedHash, into);\n }\n\n // TODO.md T10: report orphans -- files tracked before this update but no\n // longer part of any current component -- in both dry-run and real runs.\n // Only files still present matter; anything already gone needs no action.\n const orphansToReport = orphanReport.filter((o) => o.status !== \"not-found\");\n if (orphansToReport.length > 0) {\n const verb = options.dryRun ? \"[dry-run] Found\" : \"Found\";\n console.log(\n `\\n${verb} ${orphansToReport.length} orphaned file(s) (tracked before this update, no longer part of any current component):`,\n );\n for (const { file, status } of orphansToReport) {\n const marker = status === \"pruned\" ? \"pruned\" : options.prune ? \"kept (see reason above)\" : \"not removed\";\n console.log(` ${file} — ${marker}`);\n }\n if (scope === \"repo\" && spellScope === \"user\") {\n console.log(\n ' This repository takes its spells from the user tier (spell_scope: \"user\"), so the files above are no longer managed here — every client reads ~/.arcane instead.',\n );\n }\n if (!options.prune) {\n console.log(\" Run `spell update --prune` to remove the ones that are safe to delete.\");\n }\n }\n\n printRegistryChanges();\n\n // ARC-045 / CS-03: one summary for every customized client shim, in both\n // dry-run and real runs, with the remedy spelled out once.\n if (customizedShims.length > 0) {\n const verb = options.dryRun ? \"[dry-run] Would keep\" : \"Kept\";\n console.log(\n `\\n! ${verb} ${customizedShims.length} customized client file(s) untouched (ARC-045): an edited Copilot prompt, Claude command or Codex skill is never merged into the new generated shim, because a shim carries no prose of its own to merge into.\\n` +\n customizedShims.map((f) => ` ${f}`).join(\"\\n\") +\n `\\n Each keeps working in its client as-is. To converge: move your edits into the canonical spell (.arcane/spells/<id>.md — later updates merge edits there), delete the customized file, commit, and run \\`spell update\\` again — it restores the generated shim at the current version.`,\n );\n }\n\n // ARC-045 decision 3 / CS-04: with the store current, reconcile the client\n // files outside it -- a renderer change regenerates every shim, a dropped\n // spell's shims are pruned, a missing one comes back, and an edited or\n // foreign file is left alone and named. Dry-run reports the same decisions.\n let fanoutRecord: Record<string, string> | undefined;\n if (scope === \"user\") {\n const fanout = await syncUserTierFanout({\n ownedClients: SPELL_FANOUT_CLIENTS,\n homeDir,\n storeRoot: targetDir,\n spellIds: spellIdsInStore(updatedComponents),\n previous: manifest.fanout,\n dryRun: options.dryRun,\n // A dry run has restored nothing yet, so a missing store spell renders\n // from the vendor asset the real run would write (review finding F1).\n fallbackDir: assetsDir,\n });\n const lines = describeFanoutOutcomes(fanout.outcomes, options.dryRun);\n if (lines.length > 0) console.log();\n for (const line of lines) console.log(` ${line}`);\n fanoutRecord = fanout.record;\n }\n\n if (options.dryRun) {\n console.log(\n `\\n[dry-run] Would update ${fileCount} files.`,\n );\n if (Object.keys(flagPatch).length > 0) reportFlagPatch(flagPatch, manifest, answered, true);\n const applicableRetrofits =\n scope === \"repo\" ? MANIFEST_RETROFITS.filter((r) => r.needsRetrofit(answered)) : [];\n if (applicableRetrofits.length > 0) {\n console.log(\n `[dry-run] Would ask ${applicableRetrofits.length} manifest retrofit question${applicableRetrofits.length === 1 ? \"\" : \"s\"}: ${applicableRetrofits.map((r) => r.field).join(\", \")}.`,\n );\n }\n return;\n }\n\n // Retrofit wizard: ask about any manifest field this install predates\n // (e.g. `role`), once, before writing the updated manifest.\n //\n // Skipped without a TTY. These are interactive questions, and @inquirer\n // throws ExitPromptError on a closed stdin -- which would crash `update`\n // AFTER files were copied but BEFORE the manifest was written, leaving the\n // repo with new files and a stale recorded version. CI, piped input, and\n // scripted upgrades all land here. The fields stay unset and are asked on\n // the next interactive run, which is exactly how a scripted `init` already\n // behaves.\n //\n // Skipped entirely for the user tier: every retrofit field describes a\n // repository (hub role, tracking mode, subject root, sensitivity, push\n // policy), none of which the store has.\n // Flags (PRD D-02) are applied first, so a flagged question is answered\n // without a prompt, with or without a terminal.\n const interactive = Boolean(process.stdin.isTTY) && scope === \"repo\";\n const retrofitPatch = interactive ? await runManifestRetrofits(answered, targetDir) : {};\n if (!interactive && scope === \"repo\") {\n const pending = MANIFEST_RETROFITS.filter((r) => r.needsRetrofit(answered));\n if (pending.length > 0) {\n console.log(\n ` ! Skipped ${pending.length} manifest question${pending.length === 1 ? \"\" : \"s\"} (${pending\n .map((r) => r.field)\n .join(\", \")}) — no interactive terminal. Run \\`spell update\\` from a terminal, or answer by flags:`,\n );\n for (const line of manifestFlagLine(answered) ?? []) console.log(` ${line}`);\n }\n }\n\n // Update manifest with new version, refreshed component file paths, and\n // any retrofit answers (and, for the user tier, the reconciled fan-out).\n const updated: ArcaneManifest = {\n ...manifest,\n version: packageVersion,\n components: updatedComponents,\n ...flagPatch,\n ...retrofitPatch,\n ...(fanoutRecord !== undefined ? { fanout: fanoutRecord } : {}),\n ...defaultScopePatch(options, manifest),\n };\n await writeManifest(targetDir, updated);\n\n console.log(\n `\\n\\u2713 Updated ${fileCount} files.`,\n );\n\n if (Object.keys(flagPatch).length > 0) {\n reportFlagPatch(flagPatch, manifest, updated, false);\n } else if (updated.push_policy !== manifest.push_policy) {\n for (const line of pushPolicyNotice(updated.push_policy)) console.log(line);\n }\n\n // ARC-038 decision 1: the update completes regardless of conflicts -- a\n // conflicted file blocking every other file's update would be a worse\n // outcome than a clearly flagged conflict the operator resolves before\n // committing (this repository's own pre-commit/pre-push hooks, and any\n // CI the operator has, still catch an unresolved <<<<<<< marker before it\n // reaches main).\n if (conflictedFiles.length > 0) {\n console.log(\n `\\n\\u26a0 ${conflictedFiles.length} file(s) have unresolved merge conflicts:\\n` +\n conflictedFiles.map((f) => ` ${f}`).join(\"\\n\") +\n `\\n Search for \"<<<<<<< yours\" and resolve before committing.`,\n );\n }\n\n // If this update just turned the repo into a hub, offer to scaffold the\n // venture registry from whatever already exists under business_root.\n if (scope === \"repo\" && manifest.role !== \"hub\" && updated.role === \"hub\") {\n // `--role hub` reaches this without a terminal; the scaffold offer is a prompt.\n if (process.stdin.isTTY) {\n await offerRegistryScaffold(targetDir, updated.business_root ?? \"ventures\");\n } else {\n console.log(\n ` This repository is now a hub. Run \\`spell update\\` from a terminal to scaffold ${updated.business_root ?? \"ventures\"}/registry.json from existing venture folders.`,\n );\n }\n }\n}\n","/**\n * Line-based three-way text merge (ARC-038 decision 1).\n *\n * `spell update` uses this to reconcile an operator's edited copy of a\n * shipped file against a new vendor version, without silently discarding the\n * edit: `base` is the OLD vendor content the operator's edit started from,\n * `ours` is the operator's current on-disk content, `theirs` is the NEW\n * vendor content about to be installed. Non-overlapping changes on each side\n * merge automatically; a region both sides changed differently becomes a\n * conflict, marked the same way `git merge` marks one.\n *\n * Deliberately line-granular, not character-granular -- the same mental\n * model a human resolving a merge conflict already has, and the one ARC-038\n * itself names (\"the same shape copier's regenerate-old/diff/regenerate-new/\n * merge approach uses\").\n */\n\nexport const CONFLICT_START = \"<<<<<<< yours (edited)\";\nexport const CONFLICT_MID = \"=======\";\nexport const CONFLICT_END = \">>>>>>> incoming (vendor update)\";\n\ninterface DiffOp {\n type: \"equal\" | \"replace\";\n baseStart: number;\n baseEnd: number;\n otherStart: number;\n otherEnd: number;\n}\n\n/**\n * Line-based LCS diff of `base` against `other`. DP table is O(n*m) time and\n * space -- fine for governance-doc-sized files (hundreds to low thousands of\n * lines); `merge3` falls back to a whole-file conflict rather than running\n * this on pathologically large inputs (see MAX_LINES_FOR_LCS below).\n */\nfunction computeLcsDiff(base: string[], other: string[]): DiffOp[] {\n const n = base.length;\n const m = other.length;\n const dp: Uint32Array[] = Array.from({ length: n + 1 }, () => new Uint32Array(m + 1));\n for (let i = n - 1; i >= 0; i--) {\n for (let j = m - 1; j >= 0; j--) {\n dp[i]![j] =\n base[i] === other[j] ? dp[i + 1]![j + 1]! + 1 : Math.max(dp[i + 1]![j]!, dp[i]![j + 1]!);\n }\n }\n\n const ops: DiffOp[] = [];\n let i = 0;\n let j = 0;\n let pendingBaseStart = 0;\n let pendingOtherStart = 0;\n\n const flushReplace = (baseEnd: number, otherEnd: number) => {\n if (baseEnd > pendingBaseStart || otherEnd > pendingOtherStart) {\n ops.push({\n type: \"replace\",\n baseStart: pendingBaseStart,\n baseEnd,\n otherStart: pendingOtherStart,\n otherEnd,\n });\n }\n };\n\n while (i < n && j < m) {\n if (base[i] === other[j]) {\n flushReplace(i, j);\n const eqBaseStart = i;\n const eqOtherStart = j;\n while (i < n && j < m && base[i] === other[j]) {\n i++;\n j++;\n }\n ops.push({ type: \"equal\", baseStart: eqBaseStart, baseEnd: i, otherStart: eqOtherStart, otherEnd: j });\n pendingBaseStart = i;\n pendingOtherStart = j;\n } else if (dp[i + 1]![j]! >= dp[i]![j + 1]!) {\n i++;\n } else {\n j++;\n }\n }\n flushReplace(n, m);\n return ops;\n}\n\nfunction arraysEqual(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false;\n return a.every((line, idx) => line === b[idx]);\n}\n\nexport interface Merge3Result {\n content: string;\n hasConflict: boolean;\n}\n\n/**\n * Files larger than this fall back to a single whole-file conflict rather\n * than running the O(n*m) LCS diff -- a correctness-preserving degradation\n * (the operator is never silently overwritten, they're just not offered an\n * auto-merge for a file this large), not a crash or a silent choice of one\n * side. No governance/prompt content shipped by Arcane comes close to this.\n */\nconst MAX_LINES_FOR_LCS = 4000;\n\n/** Splits on \\n, preserving trailing-newline presence as the final \"\" element does. */\nfunction splitLines(text: string): string[] {\n return text.split(\"\\n\");\n}\n\n/**\n * Merges `theirs` (new vendor content) onto `ours` (the operator's edited\n * content), using `base` (old vendor content) as the common ancestor.\n *\n * Never throws and never silently prefers one side: a region changed\n * differently by both sides is always marked as a conflict in the returned\n * content, and `hasConflict` tells the caller whether any such region\n * existed, so update.ts can report it rather than commit it quietly.\n */\nexport function merge3(base: string, ours: string, theirs: string): Merge3Result {\n const baseLines = splitLines(base);\n const oursLines = splitLines(ours);\n const theirsLines = splitLines(theirs);\n\n if (\n baseLines.length > MAX_LINES_FOR_LCS ||\n oursLines.length > MAX_LINES_FOR_LCS ||\n theirsLines.length > MAX_LINES_FOR_LCS\n ) {\n return {\n content: [CONFLICT_START, ours, CONFLICT_MID, theirs, CONFLICT_END].join(\"\\n\"),\n hasConflict: true,\n };\n }\n\n const oursOps = computeLcsDiff(baseLines, oursLines).filter((op) => op.type === \"replace\");\n const theirsOps = computeLcsDiff(baseLines, theirsLines).filter((op) => op.type === \"replace\");\n\n if (oursOps.length === 0 && theirsOps.length === 0) {\n return { content: base, hasConflict: false };\n }\n\n interface ChangedInterval {\n baseStart: number;\n baseEnd: number;\n source: \"ours\" | \"theirs\";\n lines: string[];\n }\n const intervals: ChangedInterval[] = [\n ...oursOps.map((op) => ({\n baseStart: op.baseStart,\n baseEnd: op.baseEnd,\n source: \"ours\" as const,\n lines: oursLines.slice(op.otherStart, op.otherEnd),\n })),\n ...theirsOps.map((op) => ({\n baseStart: op.baseStart,\n baseEnd: op.baseEnd,\n source: \"theirs\" as const,\n lines: theirsLines.slice(op.otherStart, op.otherEnd),\n })),\n ].sort((a, b) => a.baseStart - b.baseStart || a.baseEnd - b.baseEnd);\n\n // Coalesce intervals that overlap in BASE-line space, from either side,\n // into groups -- two edits any distance apart in \"ours\" and \"theirs\" text\n // can still collide if they touch the same base lines, which is exactly\n // the case a merge must resolve as one unit, not two independent ones.\n const groups: ChangedInterval[][] = [];\n let groupMaxEnd = -1;\n for (const iv of intervals) {\n const current = groups[groups.length - 1];\n if (current && iv.baseStart < groupMaxEnd) {\n current.push(iv);\n groupMaxEnd = Math.max(groupMaxEnd, iv.baseEnd);\n } else {\n groups.push([iv]);\n groupMaxEnd = iv.baseEnd;\n }\n }\n\n const resultLines: string[] = [];\n let hasConflict = false;\n let basePos = 0;\n\n for (const group of groups) {\n const groupStart = Math.min(...group.map((g) => g.baseStart));\n const groupEnd = Math.max(...group.map((g) => g.baseEnd));\n\n resultLines.push(...baseLines.slice(basePos, groupStart));\n\n const oursInGroup = group.filter((g) => g.source === \"ours\");\n const theirsInGroup = group.filter((g) => g.source === \"theirs\");\n\n if (oursInGroup.length > 0 && theirsInGroup.length === 0) {\n for (const g of oursInGroup) resultLines.push(...g.lines);\n } else if (theirsInGroup.length > 0 && oursInGroup.length === 0) {\n for (const g of theirsInGroup) resultLines.push(...g.lines);\n } else {\n const oursGroupLines = oursInGroup.flatMap((g) => g.lines);\n const theirsGroupLines = theirsInGroup.flatMap((g) => g.lines);\n if (arraysEqual(oursGroupLines, theirsGroupLines)) {\n // Converged edit -- both sides changed this region to the same\n // result, so there is nothing to actually resolve.\n resultLines.push(...oursGroupLines);\n } else {\n hasConflict = true;\n resultLines.push(CONFLICT_START, ...oursGroupLines, CONFLICT_MID, ...theirsGroupLines, CONFLICT_END);\n }\n }\n\n basePos = groupEnd;\n }\n resultLines.push(...baseLines.slice(basePos));\n\n return { content: resultLines.join(\"\\n\"), hasConflict };\n}\n","import { join } from \"node:path\";\nimport { fileExists } from \"./copier.js\";\nimport {\n ComponentNotFoundError,\n LEGACY_COMPONENT_MIGRATIONS,\n REGISTRY_RETIREMENTS,\n SPELL_COMPONENT_NAMES,\n getComponent,\n listProfiles,\n} from \"./registry.js\";\nimport type { InstallScope, InstalledComponent, Profile, RegistryRetirement } from \"../types.js\";\n\n/**\n * The \"Registry changes since your install\" section `spell update` prints\n * (PRD D-15). Report-only by construction: everything here reads the manifest,\n * the registry and the disk and returns lines; nothing writes, installs or\n * deletes (EF-17: adding files to an existing repository is the operator's\n * call). `spell update` installs the `requires` prerequisites of installed\n * components and, on request, newly available ones (ARC-052) -- in its own\n * step, then removes what it installed from these lists before they print.\n */\nexport interface RegistryChanges {\n /** Renamed or retired spells/components this manifest still tracks (#279). */\n retired: RegistryRetirement[];\n /** Components the manifest's profile includes today but the manifest lacks. */\n newlyAvailable?: NewlyAvailableComponent[];\n /** The profile `newlyAvailable` was computed against. */\n profile?: Profile;\n /** Components an installed component `requires` that are not installed. */\n missingRequires?: MissingRequirement[];\n /** `spell update` installed something this run, so \"nothing was added\" would be untrue. */\n installedByUpdate?: boolean;\n /** The run already used `--add-new`, so the hint to use it would be circular. */\n addNewUsed?: boolean;\n}\n\nexport interface MissingRequirement {\n name: string;\n requiredBy: string[];\n /** Why `spell update` did not install it this run (ARC-052), when it declined. */\n leftAloneReason?: string;\n}\n\n/**\n * Components named in an installed component's `requires` that the manifest\n * does not list, in registry order of first mention. A legacy entry counts as\n * the components that replaced it; a name the registry no longer knows\n * requires nothing.\n *\n * Only a requirement the repository's own profile includes is reported: a\n * profile that deliberately leaves the standards out (lite, methodology) must\n * not warn on a fresh install. An unrecognized or absent profile reports all.\n */\nexport function findMissingRequires(installed: InstalledComponent[], profile?: Profile): MissingRequirement[] {\n const names = new Set(installed.flatMap((c) => LEGACY_COMPONENT_MIGRATIONS[c.name] ?? [c.name]));\n const promised = listProfiles().find((p) => p.id === profile)?.components;\n const missing = new Map<string, string[]>();\n for (const name of names) {\n let requires: string[];\n try {\n requires = getComponent(name).requires ?? [];\n } catch (err) {\n if (err instanceof ComponentNotFoundError) continue;\n throw err;\n }\n for (const required of requires) {\n if (names.has(required)) continue;\n if (promised && !promised.includes(required)) continue;\n missing.set(required, [...(missing.get(required) ?? []), name]);\n }\n }\n return [...missing].map(([name, requiredBy]) => ({ name, requiredBy }));\n}\n\nexport interface NewlyAvailableComponent {\n name: string;\n description: string;\n /** Files of a user-owned (skipExisting) component already on disk. */\n alreadyPresent: string[];\n}\n\n// Every shape a spell file has been installed under: canonical source, the\n// Copilot/Claude shims, the Codex skill, and the user tier's store.\nconst SPELL_ID_IN_PATH = /(?:^|\\/)(spell-[a-z0-9-]+)(?:\\/SKILL\\.md|\\.prompt\\.md|\\.md)$/;\n\nexport function spellIdFromTrackedPath(file: string): string | undefined {\n return SPELL_ID_IN_PATH.exec(file)?.[1];\n}\n\n/**\n * Retirements a manifest still tracks: a component entry under a retired\n * name, or a tracked file belonging to a retired spell. Pass the manifest's\n * components as read -- before any legacy migration renames them away.\n */\nexport function findTrackedRetirements(components: InstalledComponent[]): RegistryRetirement[] {\n const componentNames = new Set(components.map((c) => c.name));\n const spellIds = new Set(\n components\n .flatMap((c) => c.files)\n .map(spellIdFromTrackedPath)\n .filter((id): id is string => id !== undefined),\n );\n return REGISTRY_RETIREMENTS.filter((entry) =>\n entry.kind === \"component\" ? componentNames.has(entry.name) : spellIds.has(entry.name),\n );\n}\n\n/**\n * Components the repository's profile would install today that its manifest\n * does not list -- typically ones added to the registry after this repository\n * was installed. Pass the components after legacy migration, so a renamed\n * entry counts as the components that replaced it.\n *\n * A repository that takes its spells from the user tier is not offered\n * spells-* components: it opted out of them. A user-owned (skipExisting)\n * component whose every file is already on disk is left out -- the operator\n * already has their own, and `spell add` would refuse to overwrite it.\n */\nexport async function findNewlyAvailable(\n targetDir: string,\n profile: Profile,\n installed: InstalledComponent[],\n spellScope: InstallScope,\n): Promise<NewlyAvailableComponent[]> {\n // An unrecognized profile (hand-edited manifest) has nothing to compare to.\n const definition = listProfiles().find((p) => p.id === profile);\n if (!definition) return [];\n\n const installedNames = new Set(installed.map((c) => c.name));\n const found: NewlyAvailableComponent[] = [];\n for (const name of definition.components) {\n if (installedNames.has(name)) continue;\n if (spellScope === \"user\" && SPELL_COMPONENT_NAMES.includes(name)) continue;\n const component = getComponent(name);\n const alreadyPresent: string[] = [];\n if (component.skipExisting) {\n for (const file of component.files) {\n if (await fileExists(join(targetDir, file))) alreadyPresent.push(file);\n }\n if (component.files.length > 0 && alreadyPresent.length === component.files.length) continue;\n }\n found.push({ name, description: component.description, alreadyPresent });\n }\n return found;\n}\n\nexport function formatRegistryChanges(changes: RegistryChanges): string[] {\n const missingRequires = changes.missingRequires ?? [];\n // A component listed as a missing prerequisite is not repeated below.\n const newlyAvailable = (changes.newlyAvailable ?? []).filter(\n (c) => !missingRequires.some((m) => m.name === c.name),\n );\n if (changes.retired.length === 0 && newlyAvailable.length === 0 && missingRequires.length === 0) return [];\n\n const lines = [\"\", \"Registry changes since your install:\"];\n if (changes.retired.length > 0) {\n lines.push(\" Renamed or retired, still tracked here:\");\n for (const entry of changes.retired) {\n const successor = entry.successors.length > 0 ? entry.successors.join(\", \") : \"no successor\";\n const ownership = entry.userOwned\n ? \" (user-owned: a copy you edited is released from tracking, never deleted)\"\n : \"\";\n lines.push(` ${entry.name} (${entry.kind}) → ${successor}: ${entry.reason}${ownership}`);\n }\n lines.push(\n \" Nothing is deleted because of this list: their files go through the orphan handling above, which removes an untouched one only under `spell update --prune`.\",\n );\n }\n if (newlyAvailable.length > 0) {\n const profile = changes.profile ? ` \"${changes.profile}\"` : \"\";\n lines.push(\n changes.installedByUpdate\n ? ` Newly available for your${profile} profile, not installed by this run:`\n : ` Newly available for your${profile} profile, not installed — nothing was added:`,\n );\n for (const component of newlyAvailable) {\n lines.push(` ${component.name} — ${component.description}`);\n lines.push(` spell add ${component.name}`);\n if (component.alreadyPresent.length > 0) {\n lines.push(\n ` (already here and yours: ${component.alreadyPresent.join(\", \")} — \\`spell add\\` keeps it rather than overwrite it)`,\n );\n }\n }\n if (!changes.addNewUsed) lines.push(\" To install them in one step: `spell update --add-new`\");\n }\n if (missingRequires.length > 0) {\n lines.push(\" Missing prerequisites — installed components cite these, and they are not installed:\");\n for (const { name, requiredBy, leftAloneReason } of missingRequires) {\n lines.push(` ${name} (required by ${requiredBy.join(\", \")})`);\n lines.push(` spell add ${name}`);\n if (leftAloneReason) lines.push(` (not installed by update: ${leftAloneReason})`);\n }\n }\n return lines;\n}\n","import { checkbox } from \"@inquirer/prompts\";\nimport { getComponent } from \"./registry.js\";\nimport { componentForSpellScope } from \"./user-tier.js\";\nimport {\n executeInstallPlan,\n planComponentInstall,\n type InstallPlan,\n} from \"./component-install.js\";\nimport type { MissingRequirement, NewlyAvailableComponent } from \"./registry-changes.js\";\nimport type { InstallScope, InstalledComponent } from \"../types.js\";\n\n/**\n * What `spell update` installs on top of refreshing what is already there\n * (ARC-052): the `requires` prerequisites of installed components, and the\n * newly available components the operator opted into (R-293b). Nothing here\n * installs an `initOnly` component, and nothing here installs over a file\n * that is already on disk.\n */\nexport interface UpdateInstallItem {\n plan: InstallPlan;\n kind: \"prerequisite\" | \"new\";\n /** Installed components that cite this one; empty for `new`. */\n requiredBy: string[];\n}\n\n/** A prerequisite `update` left alone, and why. It stays in the report. */\nexport interface LeftAlone {\n name: string;\n kind: \"prerequisite\" | \"new\";\n reason: string;\n}\n\nexport interface UpdateInstallPlan {\n items: UpdateInstallItem[];\n leftAlone: LeftAlone[];\n}\n\n/** Newly available components `--add-new` or the checklist may offer: never `initOnly`. */\nexport function offerableNewComponents(\n newlyAvailable: NewlyAvailableComponent[],\n missingRequires: MissingRequirement[],\n): NewlyAvailableComponent[] {\n return newlyAvailable.filter(\n (c) => !getComponent(c.name).initOnly && !missingRequires.some((m) => m.name === c.name),\n );\n}\n\n/**\n * Which newly available components to install: all of them under `--add-new`,\n * the operator's ticks in a terminal, none otherwise (R-293b).\n */\nexport async function selectNewComponents(\n candidates: NewlyAvailableComponent[],\n mode: { addNew: boolean; interactive: boolean; dryRun: boolean },\n): Promise<NewlyAvailableComponent[]> {\n if (candidates.length === 0) return [];\n if (mode.addNew) return candidates;\n if (!mode.interactive || mode.dryRun) return [];\n const chosen = await checkbox<string>({\n message: \"Newly available components. Space to select, Enter to confirm (none selected installs nothing):\",\n choices: candidates.map((c) => ({ name: `${c.name} — ${c.description}`, value: c.name, checked: false })),\n });\n return candidates.filter((c) => chosen.includes(c.name));\n}\n\n/**\n * Decides, from the disk, what an update run would install. The dry run\n * prints this plan and the real run executes it, so the two cannot disagree.\n */\nexport async function planUpdateInstalls(args: {\n targetDir: string;\n assetsDir: string;\n spellScope: InstallScope;\n missingRequires: MissingRequirement[];\n selectedNew: NewlyAvailableComponent[];\n}): Promise<UpdateInstallPlan> {\n const items: UpdateInstallItem[] = [];\n const leftAlone: LeftAlone[] = [];\n\n for (const { name, requiredBy } of args.missingRequires) {\n const component = componentForSpellScope(getComponent(name), args.spellScope);\n if (component.initOnly) {\n leftAlone.push({ name, kind: \"prerequisite\", reason: \"it is an initOnly component: adding it mid-life changes how Git treats existing files, so it is your call\" });\n continue;\n }\n if (component.files.length === 0) continue;\n const plan = await planComponentInstall(component, args.targetDir, args.assetsDir, {});\n if (plan.conflicts.length > 0) {\n leftAlone.push({ name, kind: \"prerequisite\", reason: conflictReason(plan) });\n continue;\n }\n items.push({ plan, kind: \"prerequisite\", requiredBy });\n }\n\n for (const candidate of args.selectedNew) {\n const component = componentForSpellScope(getComponent(candidate.name), args.spellScope);\n if (component.initOnly || component.files.length === 0) continue;\n const plan = await planComponentInstall(component, args.targetDir, args.assetsDir, {});\n if (plan.conflicts.length > 0) {\n leftAlone.push({ name: candidate.name, kind: \"new\", reason: conflictReason(plan) });\n continue;\n }\n items.push({ plan, kind: \"new\", requiredBy: [] });\n }\n\n return { items, leftAlone };\n}\n\nfunction conflictReason(plan: InstallPlan): string {\n const first = plan.conflicts[0]!;\n if (first.state === \"directory\") return `${first.file} is a directory, which update does not replace`;\n const more = plan.conflicts.length > 1 ? ` (and ${plan.conflicts.length - 1} more)` : \"\";\n return `${first.file} already exists${more}, and update does not overwrite`;\n}\n\nfunction label(item: UpdateInstallItem): string {\n return item.kind === \"prerequisite\"\n ? `prerequisite of ${item.requiredBy.join(\", \")}`\n : \"newly available\";\n}\n\n/** The `--dry-run` lines: exactly the components a real run would install. */\nexport function describeInstallPlan(plan: UpdateInstallPlan): string[] {\n const lines: string[] = [];\n for (const item of plan.items) {\n lines.push(` [dry-run] Would install: ${item.plan.component.name} (${label(item)}) — ${item.plan.toCopy.length} files`);\n for (const f of item.plan.kept) lines.push(` [dry-run] Would keep: ${f.file} (exists, yours)`);\n }\n return lines;\n}\n\n/** Writes the plan and returns the manifest entries it produced. */\nexport async function executeUpdateInstalls(\n plan: UpdateInstallPlan,\n targetDir: string,\n packageVersion: string,\n): Promise<{ installed: InstalledComponent[]; lines: string[] }> {\n const installed: InstalledComponent[] = [];\n const lines: string[] = [];\n for (const item of plan.items) {\n const { files, fileHashes } = await executeInstallPlan(item.plan, targetDir);\n installed.push({\n name: item.plan.component.name,\n files,\n installedVersion: packageVersion,\n fileHashes,\n });\n lines.push(` Installed: ${item.plan.component.name} (${label(item)}) — ${files.length} files`);\n for (const f of item.plan.kept) lines.push(` Kept existing (yours): ${f.file}`);\n }\n return { installed, lines };\n}\n","/**\n * Fetches one file's content from a specific PUBLISHED version of this\n * package (ARC-038 decision 1) -- the merge base for `spell update`'s\n * content-preserving update, without vendoring a second copy of every past\n * release: npm's own registry already permanently retains every published\n * tarball.\n *\n * npm's registry API has no \"browse one file from one version\" endpoint, and\n * hand-rolling tarball extraction for a read-one-file use case is a\n * correctness risk this project has no existing dependency to cover safely.\n * unpkg.com serves directly from that same registry tarball, so this reaches\n * the identical permanent history the ADR describes, through a widely-used,\n * purpose-built CDN rather than new vendoring infrastructure. Resolved\n * in-implementation (ARC-038 left the exact fetch mechanism open); no local\n * caching, also resolved in-implementation -- within one `spell update` run\n * each (version, file) pair is fetched at most once already, and this\n * command runs rarely enough that cross-run caching is not worth the extra\n * invalidation surface.\n */\nconst NPM_PACKAGE_NAME = \"arcane-cli\";\n\n/**\n * Returns the file's text content, or `undefined` if it could not be\n * fetched (network failure, non-2xx response, or any other error) --\n * callers must treat `undefined` as \"cannot verify what changed,\" never as\n * \"no prior content,\" since silently treating a fetch failure as an empty\n * base would make the merge invent a huge fake diff instead of correctly\n * refusing to guess.\n */\nexport async function fetchPublishedFile(\n version: string,\n relativeAssetPath: string,\n): Promise<string | undefined> {\n const normalized = relativeAssetPath.replace(/\\\\/g, \"/\").replace(/^\\/+/, \"\");\n const url = `https://unpkg.com/${NPM_PACKAGE_NAME}@${version}/dist/assets/${normalized}`;\n try {\n const response = await fetch(url);\n if (!response.ok) return undefined;\n return await response.text();\n } catch {\n return undefined;\n }\n}\n","import { dirname } from \"node:path\";\nimport {\n readManifest,\n ManifestNotFoundError,\n} from \"../modules/manifest.js\";\nimport {\n checkForUpdate,\n getFeedUrl,\n} from \"../modules/version-check.js\";\nimport { generateVersionDriftDiagram } from \"../modules/diagram-generator.js\";\nimport {\n CLAUDE_PRECEDENCE_NOTE,\n FANOUT_CLIENT_LABELS,\n effectiveSpellScope,\n VSCODE_USER_TIER_NOTE,\n inspectUserTierFanout,\n misplacedUserManifestMessage,\n resolveInstallScope,\n userTierRoot,\n} from \"../modules/user-tier.js\";\nimport type { InstallScope } from \"../types.js\";\n\n/**\n * Runs the `spell status` command.\n *\n * Reads .arcane.json, checks for an available update, then prints\n * a formatted table of installed components + a version footer.\n *\n * @param targetDir Directory containing the Arcane installation (the\n * `~/.arcane` store when `options.user` is set)\n * @param packageVersion Current package version string\n * @param options.user Report on the per-user tier instead of a repository (CS-04)\n */\nexport async function runStatus(\n targetDir: string,\n packageVersion: string,\n options: { user?: boolean } = {},\n): Promise<void> {\n // Read existing manifest\n let manifest;\n try {\n manifest = await readManifest(targetDir);\n } catch (err) {\n if (err instanceof ManifestNotFoundError) {\n console.error(\n `Not initialized. Run \"spell init${options.user ? \" --user\" : \"\"}\" first.`,\n );\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n throw err;\n }\n\n const resolvedScope = resolveInstallScope(targetDir, manifest.scope, options.user);\n if (resolvedScope.misplacedUserManifest) {\n console.error(misplacedUserManifestMessage(targetDir));\n process.exit(1);\n return;\n }\n const scope: InstallScope = resolvedScope.scope;\n\n if (manifest.components.length === 0) {\n console.log(\"No components installed.\");\n return;\n }\n\n // Check for update (never throws)\n let feedUrl: string;\n try {\n feedUrl = getFeedUrl();\n } catch {\n feedUrl = \"\";\n }\n\n const versionResult = feedUrl\n ? await checkForUpdate(packageVersion, feedUrl)\n : { current: packageVersion, latest: null, updateAvailable: false, error: \"Feed URL unavailable\" };\n\n // ─── Print component table ─────────────────────────────────────────────────\n\n // Compute column widths\n const nameWidth = Math.max(\n \"Component\".length,\n ...manifest.components.map((c) => c.name.length),\n );\n const filesWidth = \"Files\".length;\n const versionWidth = Math.max(\n \"Version\".length,\n ...manifest.components.map((c) => c.installedVersion.length),\n );\n\n const pad = (s: string, n: number) => s.padEnd(n);\n const rpad = (s: string, n: number) => s.padStart(n);\n\n console.log(\n ` ${pad(\"Component\", nameWidth)} ${rpad(\"Files\", filesWidth)} ${pad(\"Version\", versionWidth)}`,\n );\n console.log(\n ` ${\"-\".repeat(nameWidth)} ${\"-\".repeat(filesWidth)} ${\"-\".repeat(versionWidth)}`,\n );\n\n for (const component of manifest.components) {\n const label = component.name;\n const fileCount = component.files.length.toString();\n const displayVersion = component.installedVersion;\n console.log(\n ` ${pad(label, nameWidth)} ${rpad(fileCount, filesWidth)} ${displayVersion}`,\n );\n }\n\n // ─── Scope (ARC-045 decision 3 / CS-04) ────────────────────────────────────\n // Which tier this manifest is, and -- for the user tier -- the state of the\n // client files it fanned out beyond the store; for a repository, whether a\n // user tier exists on this machine at all, since Claude Code will prefer\n // its copy of a same-named command (D7 in the CS-04 architecture).\n\n console.log(\"\");\n if (scope === \"user\") {\n console.log(` Scope: user — ${targetDir}`);\n const health = await inspectUserTierFanout(dirname(targetDir), manifest.fanout);\n console.log(\n ` Client files: ${health.total} (${health.byClient.codex} ${FANOUT_CLIENT_LABELS.codex}, ${health.byClient.claude} ${FANOUT_CLIENT_LABELS.claude})`,\n );\n if (health.missing.length > 0 || health.customized.length > 0) {\n const parts: string[] = [];\n if (health.missing.length > 0) parts.push(`${health.missing.length} missing`);\n if (health.customized.length > 0) parts.push(`${health.customized.length} customized`);\n console.log(\n ` ! ${parts.join(\", \")} — \\`spell update --user\\` regenerates missing files; customized files are never overwritten.`,\n );\n }\n } else {\n // ARC-045 decision 4 / CS-05: a repository that opted out says so on\n // this line, because it explains why it carries no spell files of its\n // own -- and, when the store is gone, why no client can find a spell.\n const spellScope = effectiveSpellScope(manifest);\n console.log(spellScope === \"user\" ? \" Scope: repo (spells: user tier)\" : \" Scope: repo\");\n const store = userTierRoot();\n try {\n const userManifest = await readManifest(store);\n console.log(` User tier: v${userManifest.version} at ${store} (spell status --user)`);\n } catch {\n if (spellScope === \"user\") {\n console.log(\n ` ! User tier: not installed at ${store} — this repository expects it for every spell. Run \\`spell init --user\\` (\\`spell doctor\\` explains).`,\n );\n }\n // Otherwise: no user tier on this machine (or an unreadable one --\n // `spell doctor` reports that); nothing to add here.\n }\n }\n\n // ─── Version footer ────────────────────────────────────────────────────────\n\n console.log(\"\");\n\n const latestStr = versionResult.error\n ? \"unable to check\"\n : (versionResult.latest ?? manifest.version);\n\n let footer = ` Installed: ${packageVersion} Latest: ${latestStr}`;\n\n if (versionResult.updateAvailable) {\n footer += \" (spell update available)\";\n }\n\n console.log(footer);\n\n // ─── Version-drift diagram (ARC-036 R8) ────────────────────────────────────\n // Axis A (repo-files vs installed CLI) needs no network call and is always\n // checkable. Axis B (installed CLI vs npm-latest) needs versionResult.latest,\n // which is null when the npm check failed — skip the diagram entirely in\n // that case rather than showing a partial, potentially misleading picture;\n // the \"Latest: unable to check\" footer above already covers that failure.\n if (versionResult.latest !== null) {\n const diagram = generateVersionDriftDiagram(\n manifest.version,\n packageVersion,\n versionResult.latest,\n );\n\n if (diagram !== null) {\n console.log(\"\");\n if (process.stdout.isTTY) {\n // Interactive terminal: a fenced Mermaid block doesn't render here —\n // print the same three readings as plain aligned text instead.\n console.log(\" Version drift:\");\n console.log(` repo-files: ${manifest.version}`);\n console.log(` installed-cli: ${packageVersion}`);\n console.log(` latest (npm): ${versionResult.latest}`);\n } else {\n // Piped (file, another tool, a captured PR body): emit the real\n // diagram so the redirected output is directly usable.\n console.log(\"```mermaid\");\n console.log(diagram);\n console.log(\"```\");\n }\n }\n }\n\n if (scope === \"user\") {\n console.log(\"\");\n for (const line of VSCODE_USER_TIER_NOTE) console.log(` ${line}`);\n console.log(` ${CLAUDE_PRECEDENCE_NOTE}`);\n }\n}\n","import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { createRequire } from \"node:module\";\nimport type { VersionCheckResult } from \"../types.js\";\n\nconst require = createRequire(import.meta.url);\n\ninterface PackageJson {\n publishConfig?: { registry?: string };\n}\n\n/** Read the feed URL from the package's own publishConfig.registry — never hardcoded. */\nexport function getFeedUrl(): string {\n const pkg = require(\"../../package.json\") as PackageJson;\n const url = pkg.publishConfig?.registry;\n if (!url) {\n throw new Error(\n \"publishConfig.registry is not set in package.json. Cannot resolve npm feed URL.\",\n );\n }\n return url;\n}\n\nconst DEFAULT_CACHE_PATH = join(homedir(), \".arcane\", \"version-cache.json\");\nconst CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours\n\ninterface CacheEntry {\n latest: string;\n checkedAt: string;\n}\n\n/**\n * Reads the version cache file. Returns null if missing or malformed.\n */\nexport async function readCache(\n cachePath: string,\n): Promise<CacheEntry | null> {\n try {\n const content = await readFile(cachePath, \"utf-8\");\n const parsed = JSON.parse(content) as CacheEntry;\n if (typeof parsed.latest !== \"string\" || typeof parsed.checkedAt !== \"string\") {\n return null;\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\n/**\n * Writes the version cache file, creating parent directories as needed.\n */\nexport async function writeCache(\n cachePath: string,\n latest: string,\n): Promise<void> {\n await mkdir(dirname(cachePath), { recursive: true });\n const entry: CacheEntry = { latest, checkedAt: new Date().toISOString() };\n await writeFile(cachePath, JSON.stringify(entry, null, 2), \"utf-8\");\n}\n\n/**\n * Fetches the latest published version of arcane-cli from the public npm\n * registry. Returns null (does not throw) on any network failure.\n */\nexport async function getLatestVersion(feedUrl: string): Promise<string | null> {\n const normalizedBase = feedUrl.endsWith(\"/\") ? feedUrl : feedUrl + \"/\";\n const url = `${normalizedBase}arcane-cli`;\n try {\n const response = await fetch(url, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(5000),\n });\n if (!response.ok) return null;\n const body = await response.json() as { \"dist-tags\"?: { latest?: string } };\n return body[\"dist-tags\"]?.latest ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Checks for a newer version of the package, using a 24h file cache.\n * Never throws — errors are captured in VersionCheckResult.error.\n */\nexport async function checkForUpdate(\n currentVersion: string,\n feedUrl: string,\n cachePath: string = DEFAULT_CACHE_PATH,\n): Promise<VersionCheckResult> {\n try {\n let latest: string | null = null;\n\n const cached = await readCache(cachePath);\n if (cached) {\n const age = Date.now() - new Date(cached.checkedAt).getTime();\n if (age < CACHE_TTL_MS) {\n latest = cached.latest;\n }\n }\n\n if (latest === null) {\n latest = await getLatestVersion(feedUrl);\n if (latest !== null) {\n try {\n await writeCache(cachePath, latest);\n } catch {\n // Cache write failure is non-fatal\n }\n }\n }\n\n return {\n current: currentVersion,\n latest,\n updateAvailable: latest !== null && latest !== currentVersion,\n };\n } catch (err) {\n return {\n current: currentVersion,\n latest: null,\n updateAvailable: false,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n}\n","/**\n * Generates the canonical version-drift gitGraph diagram (ARC-036, R2) from\n * the three already-computed version readings. Pure — no I/O, no network\n * calls. Mirrors spell-open-session.prompt.md's \"Arcane version check\n * (two-axis)\" template exactly, so the CLI (`spell status`) and the prompt\n * produce the identical shape from the same three inputs.\n */\n\n/**\n * Returns the gitGraph body (no ```mermaid fence — the caller chooses\n * fencing per R5) for the three-reading version-drift diagram, or `null`\n * when all three readings match (the applicability guard — nothing to\n * visualize for a single, matching reading).\n *\n * One commit per *distinct* value among the three readings, in\n * repo-files -> installed-CLI -> npm-latest order; a consecutive repeated\n * value collapses into its neighbor's commit rather than repeating it.\n * Branches off after the commit for each axis that drifts.\n */\nexport function generateVersionDriftDiagram(\n repoFilesVersion: string,\n installedCliVersion: string,\n npmLatestVersion: string,\n): string | null {\n if (\n repoFilesVersion === installedCliVersion &&\n installedCliVersion === npmLatestVersion\n ) {\n return null;\n }\n\n const lines = [\"gitGraph\", ` commit id: \"${repoFilesVersion}\"`];\n let lastDistinctVersion = repoFilesVersion;\n\n if (installedCliVersion !== repoFilesVersion) {\n lines.push(\n \" branch repo-files\",\n \" checkout main\",\n ` commit id: \"${installedCliVersion}\"`,\n );\n lastDistinctVersion = installedCliVersion;\n }\n\n if (npmLatestVersion !== lastDistinctVersion) {\n lines.push(\n \" branch installed-cli\",\n \" checkout main\",\n ` commit id: \"${npmLatestVersion}\" tag: \"latest\"`,\n );\n } else {\n lines[lines.length - 1] += ' tag: \"latest\"';\n }\n\n return lines.join(\"\\n\");\n}\n","import { rm } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { confirm } from \"@inquirer/prompts\";\nimport { removeWithin } from \"../modules/copier.js\";\nimport {\n readManifest,\n ManifestNotFoundError,\n} from \"../modules/manifest.js\";\nimport { stripMarkerSection } from \"../modules/merger.js\";\nimport { isHookEnforced, blockedRemotes } from \"../modules/push-safety.js\";\nimport { removeSecretsPrecommitHook } from \"../modules/secrets-scan.js\";\nimport {\n USER_TIER_SPELLS_DIR,\n describeFanoutOutcomes,\n misplacedUserManifestMessage,\n removeDirectoryIfEmpty,\n resolveInstallScope,\n syncUserTierFanout,\n} from \"../modules/user-tier.js\";\nimport type { ArcaneManifest } from \"../types.js\";\n\n/**\n * Runs the `spell uninstall` command.\n *\n * Deletes every file tracked in .arcane.json then removes .arcane.json itself.\n * Prompts for confirmation unless --yes is passed.\n *\n * @param options.yes Skip confirmation prompt\n * @param options.dryRun Preview what would be removed without deleting files\n * @param options.user Uninstall the per-user tier at ~/.arcane instead (CS-04)\n * @param targetDir Directory containing the Arcane installation\n */\nexport async function runUninstall(\n options: { yes?: boolean; dryRun?: boolean; user?: boolean },\n targetDir: string,\n): Promise<void> {\n // Read existing manifest\n let manifest;\n try {\n manifest = await readManifest(targetDir);\n } catch (err) {\n if (err instanceof ManifestNotFoundError) {\n console.error(`Not initialized. Run \"spell init${options.user ? \" --user\" : \"\"}\" first.`);\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n throw err;\n }\n\n // ARC-045 decision 3 / CS-04: the user tier has no push policy, no agent\n // outputs, no marker sections and no hooks -- only the store and the client\n // files it fanned out, which come off under the same hash rule they went\n // on with.\n const resolvedScope = resolveInstallScope(targetDir, manifest.scope, options.user);\n if (resolvedScope.misplacedUserManifest) {\n console.error(misplacedUserManifestMessage(targetDir));\n process.exit(1);\n return;\n }\n if (resolvedScope.scope === \"user\") {\n await uninstallUserTier(options, targetDir, manifest);\n return;\n }\n\n // A push-blocked repository must not be uninstalled while the block stands.\n //\n // Uninstall deletes .arcane.json but has no idea about core.hooksPath or the\n // sentinel push URLs, so the repo was left unable to push while `spell\n // unblock-push` -- the only supported way out -- refused to run because the\n // manifest it reads was gone. The operator was told \"Uninstalled\" and handed\n // a repository whose recovery required hand-editing git config.\n //\n // Refusing is the right shape rather than silently unblocking: ARC-034 R5\n // makes lifting the block a deliberate, interactive, separately-confirmed act,\n // and quietly doing it as a side effect of `uninstall --yes` would hand any\n // script exactly the bypass that command exists to deny.\n // Keyed on what the repository ACTUALLY has, not only on what the manifest\n // declares. A manifest-only check is the same declaration-versus-enforcement\n // confusion this feature exists to remove, and it is reachable without anyone\n // hand-editing: a partially-failed `unblock-push` writes push_policy \"open\"\n // while remotes stay blocked, after which uninstall would strand the repo\n // exactly as before.\n const policy = manifest.push_policy ?? \"open\";\n const controlsInForce =\n (await isHookEnforced(targetDir)) || (await blockedRemotes(targetDir)).length > 0;\n if ((policy === \"blocked\" || controlsInForce) && !options.dryRun) {\n const why =\n policy === \"blocked\"\n ? `this repository's push_policy is \"blocked\"`\n : `this repository still has Arcane's push block in force (the manifest says \"${policy}\", ` +\n \"but the controls are actually installed)\";\n console.error(\n `Refusing to uninstall: ${why}.\\n` +\n \"Uninstalling now would delete .arcane.json while leaving the pre-push hook and the\\n\" +\n \"disabled push URLs in place — the repository could not push, and `spell unblock-push`\\n\" +\n \"would no longer recognise it.\\n\\n\" +\n \"Run `spell unblock-push` first (interactive), then uninstall.\",\n );\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n\n // Count total tracked files\n const allFiles = manifest.components.flatMap((c) => c.files);\n const totalFiles = allFiles.length;\n\n // Dry-run: print what would be removed without deleting\n if (options.dryRun) {\n for (const file of allFiles) {\n console.log(`[dry-run] Would remove: ${file}`);\n }\n console.log(`[dry-run] Would remove: .arcane.json`);\n console.log(`[dry-run] Would remove: .github/agents/ (directory)`);\n console.log(`[dry-run] Would remove: .arcane/agents/ (directory)`);\n console.log(`[dry-run] Would remove: .arcane/generated/ (directory)`);\n console.log(`[dry-run] Would remove: .arcane/agents.yaml`);\n console.log(`[dry-run] Would strip arcane sections from: CLAUDE.md, AGENTS.md, copilot-instructions.md`);\n console.log(`\\n[dry-run] ${totalFiles} file(s) + manifest + agent outputs would be removed.`);\n if (policy === \"blocked\" || controlsInForce) {\n console.log(\n `[dry-run] BUT the real run would refuse: Arcane's push block is in force. Run \\`spell unblock-push\\` first.`,\n );\n }\n return;\n }\n\n // Confirmation prompt (skipped with --yes)\n if (!options.yes) {\n const confirmed = await confirm({\n message: `This will remove ${totalFiles} files and .arcane.json. Continue?`,\n default: false,\n });\n\n if (!confirmed) {\n console.log(\"Uninstall cancelled.\");\n return;\n }\n }\n\n // Delete each file tracked in the manifest -- through the same traversal\n // guard every write path has: a manifest entry that resolves outside the\n // repository is named and skipped, never deleted.\n let removed = 0;\n for (const component of manifest.components) {\n for (const file of component.files) {\n try {\n await removeWithin(targetDir, file);\n removed++;\n } catch (err) {\n console.log(\n ` ! Skipped ${file}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n // Delete .arcane.json\n await rm(join(targetDir, \".arcane.json\"), { force: true });\n\n // ── Clean up agent sync outputs (not tracked in manifest) ──────────────\n // Directories created by agent init/sync\n const agentDirs = [\n \".github/agents\",\n \".arcane/agents\",\n \".arcane/generated\",\n ];\n for (const dir of agentDirs) {\n await rm(join(targetDir, dir), { recursive: true, force: true });\n }\n // Roster file\n await rm(join(targetDir, \".arcane/agents.yaml\"), { force: true });\n\n // Strip arcane marker sections from merged files (keeps user content intact)\n const markerFiles = [\n \"CLAUDE.md\",\n \"AGENTS.md\",\n \".github/copilot-instructions.md\",\n ];\n for (const file of markerFiles) {\n await stripMarkerSection(targetDir, file, { dryRun: false });\n }\n\n // Secrets pre-commit hook (ARC-037): installed unconditionally by `spell\n // init` regardless of push_policy, so -- unlike the push-safety pre-push\n // hook -- there is no push_policy invariant above already guaranteeing\n // nothing is left behind. Without this, the hook would keep running\n // `spell doctor --leaks` against a project that no longer has an\n // .arcane.json to configure it, since `spell` itself is a separate global\n // install this command never touches. Best-effort: a failure here is a\n // leftover local convenience hook, not a reason to report uninstall itself\n // as failed.\n try {\n await removeSecretsPrecommitHook(targetDir);\n } catch {\n // Non-fatal.\n }\n\n console.log(`\\n✓ Uninstalled — ${removed} files removed.`);\n}\n\n// ─── spell uninstall --user ───────────────────────────────────────────────────\n\n/**\n * Removes the user tier: every client file the store manifest recorded (only\n * where its content still matches what Arcane wrote -- an edited one is kept\n * and named, exactly as `update --user` treats it), then the store's spells\n * and manifest, then the store directories if they are empty. `~/.arcane`\n * itself is removed only when nothing else lives there (the version cache\n * and the org-token file share it).\n */\nasync function uninstallUserTier(\n options: { yes?: boolean; dryRun?: boolean },\n storeRoot: string,\n manifest: ArcaneManifest,\n): Promise<void> {\n const homeDir = dirname(storeRoot);\n const storeFiles = manifest.components.flatMap((c) => c.files);\n const recordedCount = Object.keys(manifest.fanout ?? {}).length;\n\n if (options.dryRun) {\n for (const file of storeFiles) console.log(`[dry-run] Would remove: ${storeRoot}/${file}`);\n const preview = await syncUserTierFanout({\n homeDir,\n storeRoot,\n spellIds: [],\n previous: manifest.fanout,\n dryRun: true,\n });\n for (const line of describeFanoutOutcomes(preview.outcomes, true)) console.log(line);\n console.log(`[dry-run] Would remove: ${storeRoot}/.arcane.json`);\n console.log(\n `\\n[dry-run] ${storeFiles.length} spell(s), up to ${recordedCount} client file(s) + manifest would be removed from the user tier.`,\n );\n return;\n }\n\n if (!options.yes) {\n const confirmed = await confirm({\n message: `This will remove ${storeFiles.length} spells, ${recordedCount} client files and .arcane.json from the user tier at ${storeRoot}. Continue?`,\n default: false,\n });\n if (!confirmed) {\n console.log(\"Uninstall cancelled.\");\n return;\n }\n }\n\n // Client files first (they reference the store), under the hash rule.\n const fanout = await syncUserTierFanout({\n homeDir,\n storeRoot,\n spellIds: [],\n previous: manifest.fanout,\n });\n\n let removed = 0;\n for (const file of storeFiles) {\n try {\n await removeWithin(storeRoot, file);\n removed++;\n } catch (err) {\n console.log(` ! Skipped ${file}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n await rm(join(storeRoot, \".arcane.json\"), { force: true });\n // The agent roster and its definition files, mirroring the repository\n // tier's own cleanup: they are written by `spell agents init --user` and\n // tracked by no component, so nothing else would ever remove them.\n await rm(join(storeRoot, \"agents\"), { recursive: true, force: true });\n await rm(join(storeRoot, \"agents.yaml\"), { force: true });\n await removeDirectoryIfEmpty(join(storeRoot, USER_TIER_SPELLS_DIR));\n await removeDirectoryIfEmpty(storeRoot);\n\n for (const line of describeFanoutOutcomes(fanout.outcomes)) console.log(` ${line}`);\n const pruned = fanout.outcomes.filter((o) => o.status === \"pruned\").length;\n const kept = fanout.outcomes.filter((o) => o.status === \"kept-edited\").length;\n console.log(\n `\\n✓ Uninstalled the user tier — ${removed} spells and ${pruned} client files removed${\n kept > 0 ? `; ${kept} edited client file(s) kept` : \"\"\n }.`,\n );\n}\n","import { access, readdir, readFile, mkdir, copyFile as fsCopyFile } from \"node:fs/promises\";\nimport { join, dirname } from \"node:path\";\nimport { INCIDENT_QUEUE } from \"../config/incidents.js\";\nimport type { Delegation, DelegationsFile } from \"../types.js\";\nimport { evaluateIncidentGate } from \"../modules/incident-gate.js\";\nimport { runGit } from \"../modules/git.js\";\nimport {\n readManifest,\n resolveSecretsScanExcludePrefixes,\n ManifestNotFoundError,\n} from \"../modules/manifest.js\";\nimport { rosterExists } from \"../modules/agent-loader.js\";\nimport {\n USER_TIER_SPELLS_DIR,\n effectiveSpellScope,\n inspectUserTierFanout,\n resolveHomeDir,\n userTierRoot,\n} from \"../modules/user-tier.js\";\nimport { scanRepository } from \"../modules/denylist-scan.js\";\nimport { SECRETS_RULES } from \"../modules/secrets-scan.js\";\nimport {\n isHookEnforced,\n listRemotes,\n undisabledRemotes,\n pushTargets,\n} from \"../modules/push-safety.js\";\nimport {\n parseGitHubRemote,\n parseAdoRemote,\n fetchGitHubRulesets,\n fetchAdoMergeTypePolicies,\n evaluateGitHubMergePolicy,\n evaluateAdoMergePolicy,\n} from \"../modules/platform-policy.js\";\nimport { scanGovernancePlaceholders, PLACEHOLDER_STANDARD_FILE } from \"../modules/placeholders.js\";\nimport { findMissingRequires, findNewlyAvailable } from \"../modules/registry-changes.js\";\nimport { LEGACY_COMPONENT_MIGRATIONS, getComponent } from \"../modules/registry.js\";\n\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ninterface CheckResult {\n name: string;\n passed: boolean;\n message: string;\n /** If false, this is a warning, not a blocker. Defaults to true (blocking). */\n blocking?: boolean;\n /**\n * The check could not run here (e.g. what it reads is not installed). Shown\n * as \"skip\" with its message; never affects the exit code. Set only with\n * `passed: true`.\n */\n skipped?: boolean;\n}\n\n// ─── Runtime validation helpers ───────────────────────────────────────────────\n// Every JSON file below is an external boundary (hand-editable, or written by\n// an older/foreign version of this tool) -- `JSON.parse` only proves \"valid\n// JSON\", never \"matches the shape this code assumes\". An unchecked `as` cast\n// on a non-object parse (e.g. a file containing the literal `null`) let a\n// later `.foo` access throw *outside* the surrounding try/catch, crashing\n// the whole `spell doctor` run instead of reporting one degraded check.\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n// ─── Individual checks ────────────────────────────────────────────────────────\n\nasync function checkNodeVersion(): Promise<CheckResult> {\n const name = \"Node.js version\";\n const raw = process.version; // e.g. \"v20.11.0\"\n const major = parseInt(raw.replace(/^v/, \"\").split(\".\")[0] ?? \"0\", 10);\n if (major >= 18) {\n return { name, passed: true, message: `${raw} (>=18 required)` };\n }\n return {\n name,\n passed: false,\n message: `${raw} — Node.js 18 or later is required`,\n };\n}\n\n/**\n * `homeDir` is resolved through `resolveHomeDir()` (os.homedir()), never from\n * `process.env.HOME` directly: HOME is routinely unset on Windows, where the\n * home directory is USERPROFILE. Reading it directly made both probe paths\n * resolve to a drive root on every ordinary Windows run, so the check fell\n * through to the `--version` probes below -- which had no timeout, and were\n * measured taking 25s+ each, twice per `spell doctor`.\n */\nexport async function checkVSCodeExtension(\n id: string,\n label: string,\n homeDir: string = resolveHomeDir(),\n): Promise<CheckResult> {\n const name = `VS Code extension: ${label}`;\n // Check extension directories on disk — more reliable than --list-extensions\n // in child process contexts (snap binaries may not output to non-TTY stdout).\n const flavours = [\n { dir: join(homeDir, \".vscode\", \"extensions\"), bin: \"code\" },\n { dir: join(homeDir, \".vscode-insiders\", \"extensions\"), bin: \"code-insiders\" },\n ];\n\n // Which binary to name in the remedy is decided from the extension\n // directories already being read, never by spawning the binaries to ask.\n // The old probe shelled out to `code --version` and then `code-insiders\n // --version` on every run that got this far -- measured at 2.5s apiece on a\n // developer machine where both answer normally, and unbounded when one does\n // not -- to choose one word in the message of a check that is already a\n // non-blocking warning. An existing extensions directory is the same\n // evidence at no cost.\n let installBin: string | null = null;\n\n for (const flavour of flavours) {\n let entries: string[];\n try {\n entries = await readdir(flavour.dir);\n } catch {\n continue; // That flavour of VS Code isn't installed for this user.\n }\n installBin ??= flavour.bin;\n\n const prefix = id.toLowerCase() + \"-\";\n const found = entries.some(\n (e) => e.toLowerCase() === id.toLowerCase() || e.toLowerCase().startsWith(prefix),\n );\n if (found) {\n return { name, passed: true, message: `${id} is installed` };\n }\n }\n\n return {\n name,\n passed: false,\n message: `${id} not found. Install via: ${installBin ?? \"code\"} --install-extension ${id}`,\n blocking: false,\n };\n}\n\nexport async function checkArcaneManifest(targetDir: string): Promise<CheckResult> {\n const name = \"Arcane manifest (.arcane.json)\";\n const manifestPath = join(targetDir, \".arcane.json\");\n try {\n await access(manifestPath);\n } catch {\n const selfHostedManifestPath = join(targetDir, \"src\", \"assets\", \".arcane.json\");\n try {\n const raw = await readFile(selfHostedManifestPath, \"utf8\");\n const manifest: unknown = JSON.parse(raw);\n if (\n isRecord(manifest) &&\n manifest[\"selfHosted\"] === true &&\n manifest[\"tracking_mode\"] === \"internal\"\n ) {\n return {\n name,\n passed: true,\n message: \"self-hosted source tree — committed source manifest is authoritative\",\n };\n }\n } catch {\n // No valid self-hosting marker — report the normal missing-manifest warning.\n }\n return {\n name,\n passed: false,\n message: \"No .arcane.json found. Run `spell init` to initialize Arcane in this project.\",\n blocking: false,\n };\n }\n\n try {\n const raw = await readFile(manifestPath, \"utf8\");\n const manifest: unknown = JSON.parse(raw);\n if (\n !isRecord(manifest) ||\n typeof manifest[\"version\"] !== \"string\" ||\n !manifest[\"version\"] ||\n !Array.isArray(manifest[\"components\"])\n ) {\n return {\n name,\n passed: false,\n message: \".arcane.json is present but missing required fields (version, components). Re-run `spell init`.\",\n };\n }\n return {\n name,\n passed: true,\n message: `v${manifest[\"version\"]} — ${manifest[\"components\"].length} component(s) installed`,\n };\n } catch {\n return {\n name,\n passed: false,\n message: \".arcane.json is present but contains invalid JSON. Re-run `spell init`.\",\n };\n }\n}\n\n/**\n * EF-32: independently verify the EFFECTIVE (not just repository-local)\n * pull.rebase resolves to \"true\", catching drift regardless of source --\n * an install that predates `spell init`'s own fix, a value unset again\n * later, or any other path that bypassed init's one-time correction.\n * Non-blocking: this repo has no CI job running inside the operator's own\n * working copy, so a doctor warning is the only enforcement mode available\n * here (see ARC-023's inline-enforcement-contract requirement).\n */\nexport async function checkPullRebase(targetDir: string): Promise<CheckResult> {\n const name = \"Git pull.rebase (rebase-and-fast-forward mandate)\";\n try {\n // --type=bool normalizes any of git's valid boolean spellings\n // (true/yes/on/1, false/no/off/0) to canonical \"true\"/\"false\" -- a raw\n // string comparison against \"true\" alone would false-positive-warn on\n // a fully compliant \"yes\"/\"on\"/\"1\".\n const { stdout } = await runGit(targetDir, [\"config\", \"--type=bool\", \"--get\", \"pull.rebase\"]);\n const value = stdout.trim();\n if (value === \"true\") {\n return { name, passed: true, message: \"pull.rebase=true\" };\n }\n return {\n name,\n passed: false,\n message: `pull.rebase resolves to \"${value}\", not \"true\" -- a bare \\`git pull\\` will create merge commits, contradicting git-conventions.md's rebase-and-fast-forward mandate. Run: git config --local pull.rebase true`,\n blocking: false,\n };\n } catch {\n // Two cases collapse here: unset entirely, or set to a non-boolean\n // value (e.g. \"merges\") --type=bool can't coerce -- both mean\n // \"not affirmatively true\", which is what this check cares about.\n return {\n name,\n passed: false,\n message: \"pull.rebase is unset, not a recognized boolean, or this isn't a Git repository -- effective behavior may not be rebase-and-fast-forward, and Git for Windows defaults this to false when unset. Run: git config --local pull.rebase true\",\n blocking: false,\n };\n }\n}\n\nexport function checkIncidentReleaseGate(): CheckResult {\n const result = evaluateIncidentGate(INCIDENT_QUEUE);\n if (result.blocked) {\n return {\n name: \"Incident release gate (ARC-024)\",\n passed: false,\n message: result.blockers.join(\"; \"),\n };\n }\n const accepted = result.acceptedRisks.length > 0\n ? `; ${result.acceptedRisks.length} explicitly deferred`\n : \"\";\n return {\n name: \"Incident release gate (ARC-024)\",\n passed: true,\n message: `${result.checked} incidents classified${accepted}`,\n };\n}\n\n/**\n * ARC-037 decision 7: `spell check-leaks` folded into `spell doctor` as an\n * on-demand mode (`--leaks`) instead of a standalone command -- one fewer\n * entry point for the same scan the build's own CI backstop already runs\n * (scripts/copy-assets.ts). Not part of the default battery below: a\n * full-repository walk is a different cost profile than doctor's other\n * checks, and (per decision 6) the real defense is the CI backstop --\n * this is a fast, opt-in, run-it-yourself pass, mirroring `spell ward`'s\n * own separateness from the default battery.\n */\nexport async function checkSecrets(targetDir: string): Promise<CheckResult> {\n const name = \"Secrets scan (ARC-037)\";\n const excludePrefixes = await resolveSecretsScanExcludePrefixes(targetDir);\n const findings = await scanRepository(targetDir, SECRETS_RULES, undefined, excludePrefixes);\n if (findings.length === 0) {\n return { name, passed: true, message: \"no credential-shaped patterns found\" };\n }\n\n const summary = findings.map((f) => `${f.file}:${f.line} [${f.rule}]`).join(\"; \");\n return {\n name,\n passed: false,\n message: `${findings.length} credential-shaped pattern(s) found: ${summary}`,\n };\n}\n\n// ─── Session continuity checks ────────────────────────────────────────────────\n\n/** Files required for spell-close-session / spell-open-session to function. */\nexport const SESSION_CONTINUITY_FILES = [\n \"TODO.md\",\n \"DECISIONS.md\",\n \"ai-context/system-prompt-context.md\",\n \"journal/.gitkeep\",\n];\n\nexport async function checkSessionContinuity(targetDir: string): Promise<CheckResult[]> {\n const results: CheckResult[] = [];\n\n for (const file of SESSION_CONTINUITY_FILES) {\n const name = `Session file: ${file}`;\n const filePath = join(targetDir, file);\n try {\n await access(filePath);\n results.push({ name, passed: true, message: \"present\" });\n } catch {\n results.push({\n name,\n passed: false,\n message: `Missing. Required for spell-close-session. Run \\`spell doctor --fix\\` to create it.`,\n blocking: false,\n });\n }\n }\n\n return results;\n}\n\nexport async function fixSessionContinuity(targetDir: string, assetsDir: string): Promise<string[]> {\n const created: string[] = [];\n\n for (const file of SESSION_CONTINUITY_FILES) {\n const destPath = join(targetDir, file);\n try {\n await access(destPath);\n // File exists — skip\n } catch {\n // File missing — create from template\n const srcPath = join(assetsDir, file);\n await mkdir(dirname(destPath), { recursive: true });\n await fsCopyFile(srcPath, destPath);\n created.push(file);\n }\n }\n\n return created;\n}\n\n// ─── Main ─────────────────────────────────────────────────────────────────────\n\nexport interface DoctorOptions {\n fix?: boolean;\n /** ARC-037 decision 7: run only the on-demand secrets scan, skipping the default battery. */\n leaks?: boolean;\n /** The running CLI's version, for the user-tier compatibility check (CS-04). Passed by the CLI entry. */\n packageVersion?: string;\n}\n\n/**\n * ARC-045 decision 3 / CS-04. Non-blocking, always: the user tier is\n * optional. Absent → pass with the pointer. Present → warn when the store's\n * `major.minor` is behind (or ahead of) this CLI, or when a client file the\n * store manifest recorded is gone -- both remedied by `spell update --user`.\n * A recorded file the operator edited is reported in the pass message, not\n * warned about: keeping it is the designed behavior, not a defect. Reads the\n * store manifest and file hashes only; VS Code's own settings are neither\n * read nor needed (the CS-04 architecture's finding 2).\n */\nexport async function checkUserTier(\n homeDir: string = resolveHomeDir(),\n cliVersion?: string,\n): Promise<CheckResult> {\n const name = \"User tier (ARC-045)\";\n const storeRoot = userTierRoot(homeDir);\n\n let manifest;\n try {\n manifest = await readManifest(storeRoot);\n } catch (err) {\n if (err instanceof ManifestNotFoundError) {\n return {\n name,\n passed: true,\n blocking: false,\n message: \"no user tier installed (optional — `spell init --user` shares every spell across the repositories on this machine)\",\n };\n }\n return {\n name,\n passed: false,\n blocking: false,\n message: `could not read ${storeRoot}/.arcane.json (${err instanceof Error ? err.message : String(err)}) — remove it and re-run \\`spell init --user\\``,\n };\n }\n\n // Everything below reads a file the operator (or an older/foreign tool)\n // may have shaped oddly. readManifest validates the enum fields, not\n // `version`; a check that throws here would take the whole `spell doctor`\n // run down with it, in every repository on the machine (review finding\n // F2), so every failure becomes one non-blocking row instead.\n try {\n if (typeof manifest.version !== \"string\" || manifest.version.length === 0) {\n return {\n name,\n passed: false,\n blocking: false,\n message: `${storeRoot}/.arcane.json has no version field — remove the store and re-run \\`spell init --user\\``,\n };\n }\n const majorMinor = (v: string): string => v.split(\".\").slice(0, 2).join(\".\");\n const problems: string[] = [];\n if (cliVersion !== undefined && majorMinor(manifest.version) !== majorMinor(cliVersion)) {\n problems.push(`installed at v${manifest.version}, this CLI is v${cliVersion}`);\n }\n const health = await inspectUserTierFanout(homeDir, manifest.fanout);\n if (health.missing.length > 0) {\n problems.push(`${health.missing.length} of ${health.total} client file(s) missing`);\n }\n\n if (problems.length > 0) {\n return {\n name,\n passed: false,\n blocking: false,\n message: `${problems.join(\"; \")} — run \\`spell update --user\\` (regenerates missing files; never overwrites edited ones)`,\n };\n }\n const customized = health.customized.length > 0 ? `, ${health.customized.length} customized` : \"\";\n return {\n name,\n passed: true,\n blocking: false,\n message: `v${manifest.version} at ${storeRoot} — ${health.total} client file(s) in place${customized}`,\n };\n } catch (err) {\n return {\n name,\n passed: false,\n blocking: false,\n message: `could not inspect the user tier at ${storeRoot} (${err instanceof Error ? err.message : String(err)}) — run \\`spell update --user\\`; if that fails too, remove the store and re-run \\`spell init --user\\``,\n };\n }\n}\n\n/**\n * EF-09 R4. Reports the repository's declared push posture and, for a\n * \"blocked\" repo, whether the controls are actually in place.\n *\n * Deliberately fires for every \"guarded\" repo REGARDLESS of remote state. An\n * earlier draft only reported when no remote was configured, which meant the\n * reminder went silent the instant any remote was added -- including a wrong\n * one -- making \"guarded\" behave identically to \"open\" exactly when it\n * mattered most.\n */\nexport async function checkPushPolicy(targetDir: string): Promise<CheckResult> {\n const name = \"Push policy (EF-09)\";\n let manifest;\n try {\n manifest = await readManifest(targetDir);\n } catch {\n return { name, passed: true, message: \"not initialized — nothing to check\" };\n }\n\n const policy = manifest.push_policy ?? \"open\";\n\n if (policy === \"open\") {\n return { name, passed: true, message: \"open — pushes allowed\" };\n }\n\n if (policy === \"guarded\") {\n // Name AND URL: the reminder's whole job is catching a wrong remote, and the\n // name alone reveals nothing about where it points.\n const targets = await pushTargets(targetDir);\n const where =\n targets.length === 0\n ? \"none configured\"\n : targets\n .map(({ remote, urls }) => `${remote} → ${urls.length > 0 ? urls.join(\", \") : \"unknown\"}`)\n .join(\"; \");\n return {\n name,\n passed: false,\n blocking: false,\n message: `guarded — no technical control installed. Push targets: ${where}. Confirm those are the remotes you intend before pushing.`,\n };\n }\n\n // blocked: verify the controls are genuinely IN FORCE, not merely declared.\n // Checking config alone was a declaration check wearing an enforcement\n // check's name -- deleting the hook file leaves core.hooksPath intact and\n // pushes succeed, while this reported the hook as present.\n const hookActive = await isHookEnforced(targetDir);\n const remotes = await listRemotes(targetDir);\n const open = await undisabledRemotes(targetDir);\n\n if (hookActive && open.length === 0) {\n if (remotes.length === 0) {\n // Don't claim a push URL is \"in place\" when there is no remote at all.\n // The hook is the only live control here, and it is the one a\n // `--no-verify` push walks straight past -- so say that plainly rather\n // than reporting full protection.\n return {\n name,\n passed: false,\n blocking: false,\n message:\n \"blocked — pre-push hook in place, but no remote is configured, so no push URL is disabled. If a remote is added later, run `spell doctor` again: a --no-verify push to it would succeed until the URL is disabled too.\",\n };\n }\n return {\n name,\n passed: true,\n message: `blocked — pre-push hook in place and push URL disabled for ${remotes.join(\", \")}`,\n };\n }\n\n const missing = [\n hookActive ? null : \"pre-push hook (config or hook file absent)\",\n open.length > 0 ? `push URL still live for: ${open.join(\", \")}` : null,\n ].filter(Boolean);\n\n return {\n name,\n passed: false,\n blocking: false,\n // ARC-049 decision 3: name the command that closes the gap.\n message: `declared \"blocked\" but not enforced — ${missing.join(\"; \")}. The manifest claims a protection this repository does not have. Run \\`spell block-push\\` to install the controls.`,\n };\n}\n\n/**\n * ARC-051 decision 4 (#277). Warns -- never fails -- on a `{UPPER_SNAKE}`\n * token in a `status: active` installed governance document that is neither\n * in spell-authoring-standards.md's runtime-placeholders list nor in a\n * `status: template` document. Every unknown token is named with its file.\n * Skipped when the standard (or its list) is not installed, since without\n * the list every token would read as unknown.\n */\nexport async function checkGovernancePlaceholders(targetDir: string): Promise<CheckResult> {\n const name = \"Governance placeholders (ARC-051)\";\n let scan;\n try {\n scan = await scanGovernancePlaceholders(join(targetDir, \".arcane\", \"governance\"));\n } catch (err) {\n return {\n name,\n passed: false,\n blocking: false,\n message: `could not scan .arcane/governance (${err instanceof Error ? err.message : String(err)})`,\n };\n }\n\n if (scan.status === \"skipped\") {\n return { name, passed: true, blocking: false, skipped: true, message: `skipped — ${scan.reason}` };\n }\n\n if (scan.findings.length === 0) {\n return {\n name,\n passed: true,\n blocking: false,\n message: `${scan.activeDocs.length} active doc(s) scanned, no unknown placeholders`,\n };\n }\n\n const listed = scan.findings.map(({ file, tokens }) => `.arcane/governance/${file}: ${tokens.join(\", \")}`);\n return {\n name,\n passed: false,\n blocking: false,\n message:\n `unknown placeholder(s) in status: active doc(s) — ${listed.join(\"; \")}. ` +\n `Fill each one in; or, if an agent resolves it at use time, add it to the runtime-placeholders list in ` +\n `.arcane/governance/${PLACEHOLDER_STANDARD_FILE}; or, if the document is meant to be filled in by hand, ` +\n \"mark it `status: template`.\",\n };\n}\n\ninterface McpServerConfig {\n timeout?: number;\n args?: unknown;\n}\n\n// A scope nobody on this project owns: the scaffold used to ship `@example/mcp-server`, which anyone\n// could publish (#295). A server whose args still name it runs a stranger's code on the next `npx -y`.\nconst UNOWNED_EXAMPLE_SCOPE = \"@example/\";\n\n// The placeholder the retired `mcp-config-template` scaffold shipped (1.10.0 through 1.10.2, #328).\n// npm rejects it, so the server fails on every session start of every client that loads the file.\nconst RETIRED_SCAFFOLD_PLACEHOLDER = \"<your-mcp-server-package>\";\n\ninterface McpConfigFile {\n mcpServers?: Record<string, McpServerConfig>;\n}\n\n/**\n * I12/BC-22. Checks that any configured .mcp.json server sets a per-server\n * \"timeout\" -- companion to the MCP fail-fast/fallback rule in\n * git-conventions.md. Without one, a hung server runs to the client's own\n * default idle limit (as long as 30 minutes) instead of a predictable one.\n * Also warns on the two placeholders the old scaffold shipped (#295, #328).\n * A missing .mcp.json is a silent pass -- MCP is optional. Advisory only:\n * the file is the operator's, and doctor never edits it.\n */\n/**\n * TODO.md \"a spell can be installed without the governance doc it cites\".\n * Warns -- never fails -- when an installed component's `requires` names a\n * component the manifest does not list; installs nothing (EF-17). `spell\n * update` is what installs them (ARC-052), and the message names it.\n */\nexport async function checkComponentRequires(targetDir: string): Promise<CheckResult> {\n const name = \"Component prerequisites\";\n let manifest;\n try {\n manifest = await readManifest(targetDir);\n } catch {\n // checkArcaneManifest reports a missing or unreadable manifest.\n return { name, passed: true, blocking: false, skipped: true, message: \"skipped — no readable .arcane.json\" };\n }\n if (manifest.scope === \"user\") {\n return { name, passed: true, blocking: false, skipped: true, message: \"skipped — the user tier holds spells only\" };\n }\n\n // A hand-edited manifest may lack components; checkArcaneManifest reports that.\n if (!Array.isArray(manifest.components)) {\n return { name, passed: true, blocking: false, skipped: true, message: \"skipped — .arcane.json lists no components\" };\n }\n const missing = findMissingRequires(manifest.components, manifest.profile);\n if (missing.length === 0) {\n return { name, passed: true, blocking: false, message: \"every installed component's prerequisites are installed\" };\n }\n return {\n name,\n passed: false,\n blocking: false,\n message: missing\n .map(({ name: required, requiredBy }) => `${requiredBy.join(\", \")} cites ${required}, which is not installed — ${getComponent(required).initOnly ? \"\" : \"`spell update` installs it, or \"}\\`spell add ${required}\\``)\n .join(\"; \"),\n };\n}\n\n/**\n * Names the components the repository's profile gained since it was installed\n * and the command that installs them (R-293b). Read-only and non-blocking:\n * doctor installs nothing (ARC-052). Prerequisites are left to\n * checkComponentRequires, which reports them.\n */\nexport async function checkNewlyAvailableComponents(targetDir: string): Promise<CheckResult> {\n const name = \"Newly available components\";\n let manifest;\n try {\n manifest = await readManifest(targetDir);\n } catch {\n return { name, passed: true, blocking: false, skipped: true, message: \"skipped — no readable .arcane.json\" };\n }\n if (manifest.scope === \"user\" || !Array.isArray(manifest.components)) {\n return { name, passed: true, blocking: false, skipped: true, message: \"skipped — no repository install to compare\" };\n }\n const installed = manifest.components.flatMap((c) =>\n (LEGACY_COMPONENT_MIGRATIONS[c.name] ?? [c.name]).map((n) => ({ ...c, name: n })),\n );\n const prerequisites = findMissingRequires(manifest.components, manifest.profile).map((m) => m.name);\n const fresh = (await findNewlyAvailable(targetDir, manifest.profile, installed, effectiveSpellScope(manifest)))\n .filter((c) => !prerequisites.includes(c.name));\n if (fresh.length === 0) {\n return { name, passed: true, blocking: false, message: \"the profile has nothing this install lacks\" };\n }\n const offerable = fresh.filter((c) => !getComponent(c.name).initOnly).map((c) => c.name);\n const byHand = fresh.filter((c) => getComponent(c.name).initOnly).map((c) => c.name);\n const parts: string[] = [];\n if (offerable.length > 0) {\n parts.push(`${offerable.join(\", \")} — \\`spell update --add-new\\` installs ${offerable.length === 1 ? \"it\" : \"them\"}`);\n }\n if (byHand.length > 0) {\n parts.push(`${byHand.join(\", \")} — initOnly, so only ${byHand.map((n) => `\\`spell add ${n}\\``).join(\", \")}`);\n }\n return { name, passed: false, blocking: false, message: `newly available for your profile, not installed: ${parts.join(\"; \")}` };\n}\n\nexport async function checkMcpConfig(targetDir: string): Promise<CheckResult> {\n const name = \"MCP config (per-server timeout, I12)\";\n const filePath = join(targetDir, \".mcp.json\");\n\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf8\");\n } catch {\n return { name, passed: true, blocking: false, message: \"no .mcp.json — nothing to check\" };\n }\n\n let rawParsed: unknown;\n try {\n rawParsed = JSON.parse(raw);\n } catch {\n return {\n name,\n passed: false,\n blocking: false,\n message: \".mcp.json exists but contains invalid JSON\",\n };\n }\n\n if (!isRecord(rawParsed)) {\n return {\n name,\n passed: false,\n blocking: false,\n message: \".mcp.json exists but is not a JSON object\",\n };\n }\n\n // Field-level shape (mcpServers, per-server timeout) is untrusted beyond\n // this point -- every access below is `?.`-guarded rather than assumed.\n const parsed = rawParsed as McpConfigFile;\n const servers = parsed.mcpServers ?? {};\n const serverNames = Object.keys(servers);\n if (serverNames.length === 0) {\n return { name, passed: true, blocking: false, message: \"no MCP servers configured\" };\n }\n\n const exampleHits: string[] = [];\n const placeholderHits: string[] = [];\n for (const n of serverNames) {\n const args = servers[n]?.args;\n if (!Array.isArray(args)) continue;\n for (const a of args) {\n if (typeof a !== \"string\") continue;\n if (a.startsWith(UNOWNED_EXAMPLE_SCOPE)) exampleHits.push(`${n} (${a})`);\n if (a === RETIRED_SCAFFOLD_PLACEHOLDER) placeholderHits.push(n);\n }\n }\n if (exampleHits.length > 0) {\n return {\n name,\n passed: false,\n blocking: false,\n message:\n `.mcp.json runs a package under the ${UNOWNED_EXAMPLE_SCOPE} scope nobody here owns: ${exampleHits.join(\", \")}. ` +\n \"The old `spell init` scaffold shipped it as a placeholder, and `npx -y` would run whoever publishes it. \" +\n \"Replace the entry with your own MCP server package, or remove it; the file is yours, so doctor does not edit it\",\n };\n }\n if (placeholderHits.length > 0) {\n return {\n name,\n passed: false,\n blocking: false,\n message:\n `.mcp.json still holds the placeholder server from the retired mcp-config-template scaffold: ${placeholderHits.join(\", \")} (${RETIRED_SCAFFOLD_PLACEHOLDER}). ` +\n \"Clients load this file as live MCP config, so the placeholder fails on every session start. \" +\n \"Delete the file if you run no MCP servers, or replace the entry with a real server package; the file is yours, so doctor does not edit it. \" +\n \"An untouched scaffold is removed by `spell update --prune`\",\n };\n }\n\n const missingTimeout = serverNames.filter((n) => typeof servers[n]?.timeout !== \"number\");\n if (missingTimeout.length > 0) {\n return {\n name,\n passed: false,\n blocking: false,\n message:\n `server(s) missing a \"timeout\": ${missingTimeout.join(\", \")} — add \"timeout\": 30000 (milliseconds; Claude Code ignores a value below 1000) ` +\n \"to each entry under mcpServers, so a stuck tool call ends after 30 s instead of running to the client's own idle limit (as long as 30 minutes)\",\n };\n }\n\n return {\n name,\n passed: true,\n blocking: false,\n message: `${serverNames.length} server(s) configured, all with a timeout set`,\n };\n}\n\nfunction isDelegation(value: unknown): value is Delegation {\n return (\n isRecord(value) &&\n typeof value[\"id\"] === \"string\" &&\n typeof value[\"status\"] === \"string\" &&\n typeof value[\"scope\"] === \"string\" &&\n Array.isArray(value[\"excludedActions\"])\n );\n}\n\nfunction isDelegationsFile(value: unknown): value is DelegationsFile {\n return isRecord(value) && Array.isArray(value[\"delegations\"]) && value[\"delegations\"].every(isDelegation);\n}\n\n/**\n * T13/BC-19. Lists standing-authority delegations recorded in\n * `.arcane/delegations.json` -- the solo-operator/no-roster counterpart to\n * agent-policies.md's roster-based power-level matrix. A missing file is a\n * silent pass, not a warning: a repo with no ad hoc grants has nothing to\n * report. This check only reports; it never grants, revokes, or validates\n * authority -- revocation is the git-native act of editing the file itself.\n */\nexport async function checkDelegations(targetDir: string): Promise<CheckResult> {\n const name = \"Standing delegations (T13)\";\n const filePath = join(targetDir, \".arcane\", \"delegations.json\");\n\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf8\");\n } catch {\n return { name, passed: true, blocking: false, message: \"no delegations recorded\" };\n }\n\n let rawParsed: unknown;\n try {\n rawParsed = JSON.parse(raw);\n } catch {\n return {\n name,\n passed: false,\n blocking: false,\n message: `.arcane/delegations.json exists but contains invalid JSON`,\n };\n }\n\n if (!isDelegationsFile(rawParsed)) {\n return {\n name,\n passed: false,\n blocking: false,\n message: `.arcane/delegations.json exists but is not a valid delegations file (expected a \"delegations\" array of well-formed entries)`,\n };\n }\n const parsed = rawParsed;\n\n const active = parsed.delegations.filter((d) => d.status === \"active\");\n if (active.length === 0) {\n return { name, passed: true, blocking: false, message: \"no active delegations\" };\n }\n\n const summary = active\n .map((d) => `${d.id} (scope: ${d.scope}; excludes: ${d.excludedActions.length} action(s))`)\n .join(\"; \");\n return {\n name,\n passed: true,\n blocking: false,\n message: `${active.length} active: ${summary}`,\n };\n}\n\n/**\n * T11/BC-17. Verifies the live platform branch/merge policy matches the\n * declared ladder (git-conventions.md: merge + rebase sanctioned, squash\n * never sanctioned). GitHub is checked via Rulesets, never the classic\n * `/branches/{branch}/protection` endpoint alone -- that endpoint 404s\n * \"not protected\" for a Rulesets-only repo (confirmed live against this\n * repo's own main branch), a false negative this check must not repeat.\n * Report-only: never mutates a platform setting. Degrades to a\n * non-blocking warning for any provider it can't resolve, any CLI it\n * can't reach, or any auth/network failure -- this check depends on\n * external state doctor's other checks never do, so it must never be the\n * reason `doctor` crashes.\n */\nexport async function checkPlatformBranchPolicy(targetDir: string): Promise<CheckResult> {\n const name = \"Platform branch/merge policy (T11)\";\n const targets = await pushTargets(targetDir);\n const origin = targets.find((t) => t.remote === \"origin\") ?? targets[0];\n const url = origin?.urls[0];\n\n if (!url) {\n return { name, passed: true, message: \"no remote configured — nothing to check\", blocking: false };\n }\n\n const github = parseGitHubRemote(url);\n if (github) {\n const rulesets = await fetchGitHubRulesets(github.owner, github.repo);\n if (rulesets === null) {\n return {\n name,\n passed: false,\n blocking: false,\n message: `could not query GitHub Rulesets for ${github.owner}/${github.repo} — is \\`gh\\` installed and authenticated? Verify manually: gh api repos/${github.owner}/${github.repo}/rulesets`,\n };\n }\n const result = evaluateGitHubMergePolicy(rulesets);\n return { name, passed: result.passed, blocking: false, message: result.message };\n }\n\n const ado = parseAdoRemote(url);\n if (ado) {\n const policies = await fetchAdoMergeTypePolicies(ado.org, ado.project, ado.repo, \"main\");\n if (policies === null) {\n return {\n name,\n passed: false,\n blocking: false,\n message: `could not query the Azure DevOps \"Limit merge types\" branch policy for ${ado.org}/${ado.project}/${ado.repo} — is \\`az\\` installed and authenticated? Verify manually in the ADO web UI under Branch Policies.`,\n };\n }\n const result = evaluateAdoMergePolicy(policies);\n return { name, passed: result.passed, blocking: false, message: result.message };\n }\n\n return {\n name,\n passed: true,\n blocking: false,\n message: \"remote is neither GitHub nor Azure DevOps — platform policy verification not available for this provider\",\n };\n}\n\n/**\n * ARC-045 decision 4 / CS-05. The one BLOCKING user-tier check, and it can\n * only fire in a repository that opted out on purpose: a repository whose\n * manifest says `spell_scope: \"user\"` has no spells of its own, so a missing\n * store leaves every client with nothing to run -- exactly the silently\n * unreachable state that decision exists to prevent. A repository that has\n * not opted in (the default, and every manifest written before 1.2.0) gets a\n * non-blocking pass, so the row never disappears without saying why.\n *\n * Complementary to checkUserTier above, not a duplicate: that one asks\n * whether the tier is healthy and is always advisory; this one asks whether\n * THIS repository depends on it.\n */\nexport async function checkSpellScope(\n targetDir: string,\n homeDir: string = resolveHomeDir(),\n): Promise<CheckResult> {\n const name = \"Spell scope (ARC-045)\";\n\n let manifest;\n try {\n manifest = await readManifest(targetDir);\n } catch {\n // No install here, or an unreadable manifest -- checkArcaneManifest\n // above is what reports that; nothing to add.\n return { name, passed: true, blocking: false, message: \"no .arcane.json — nothing to check\" };\n }\n\n if (effectiveSpellScope(manifest) === \"repo\") {\n return {\n name,\n passed: true,\n blocking: false,\n message: \"repo — this repository carries its own spells (the default)\",\n };\n }\n\n const storeRoot = userTierRoot(homeDir);\n let store;\n try {\n store = await readManifest(storeRoot);\n } catch (err) {\n return {\n name,\n passed: false,\n message:\n `this repository takes its spells from the user tier (spell_scope: \"user\"), but ${storeRoot} ` +\n `${err instanceof ManifestNotFoundError ? \"is not installed\" : `could not be read (${err instanceof Error ? err.message : String(err)})`}` +\n \" — no client can reach a spell here. Run `spell init --user`, or set spell_scope back to \\\"repo\\\" and run `spell update`.\",\n };\n }\n\n // Verified, not assumed: count the store's spells and probe a named one.\n let spellCount = 0;\n let probed = false;\n const probe = \"spell-status.md\";\n try {\n const entries = await readdir(join(storeRoot, USER_TIER_SPELLS_DIR));\n const spells = entries.filter((e) => e.startsWith(\"spell-\") && e.endsWith(\".md\"));\n spellCount = spells.length;\n probed = spells.includes(probe);\n } catch {\n // Left at 0/false -- reported as the empty-store failure below.\n }\n if (spellCount === 0 || !probed) {\n return {\n name,\n passed: false,\n message:\n `the user tier at ${storeRoot} holds ${spellCount} spell(s)` +\n `${spellCount > 0 && !probed ? ` and not ${probe}` : \"\"}` +\n \" — this repository expects it to supply every spell. Run `spell update --user`.\",\n };\n }\n\n const majorMinor = (v: string): string => v.split(\".\").slice(0, 2).join(\".\");\n if (typeof manifest.version === \"string\" && majorMinor(store.version) !== majorMinor(manifest.version)) {\n return {\n name,\n passed: false,\n blocking: false,\n message:\n `this repository is at v${manifest.version} but the user tier it uses is at v${store.version}` +\n \" — run `spell update --user` (and `spell update` here) to bring them together.\",\n };\n }\n\n // CS-06 / ARC-047 decision 9. An opted-out repository writes no\n // `.github/agents` files, so its agent modes can only come from the tier.\n // Failed only where agents are actually expected: a repository with its own\n // roster expects them, and one that has never run `spell agents init`\n // expects nothing and is not failed for having nothing.\n const repoExpectsAgents = await rosterExists(targetDir);\n if (repoExpectsAgents && !(await rosterExists(storeRoot, \"user\"))) {\n return {\n name,\n passed: false,\n message:\n `this repository has an agent roster and takes its client files from the user tier, but ` +\n `${storeRoot} has no roster of its own — no agent mode can reach a client here. ` +\n \"Run `spell agents init --user`, or set spell_scope back to \\\"repo\\\" and run `spell agents sync`.\",\n };\n }\n\n return {\n name,\n passed: true,\n blocking: false,\n message:\n `user — ${spellCount} spell(s) from ${storeRoot} (v${store.version}), ${probe} present` +\n `${repoExpectsAgents ? \", agent roster present in the tier\" : \"\"}`,\n };\n}\nexport async function runDoctor(targetDir: string, options: DoctorOptions = {}, assetsDir?: string): Promise<void> {\n console.log(\"\\nspell doctor — checking your Arcane environment\\n\");\n\n if (options.leaks) {\n const result = await checkSecrets(targetDir);\n const icon = result.passed ? \"✓\" : \"✗\";\n console.log(` ${icon} [${result.passed ? \"pass\" : \"FAIL\"}] ${result.name}`);\n if (!result.passed) {\n console.log(` ${result.message}`);\n console.log(\"\\n One or more checks failed. Fix the issues above before proceeding.\\n\");\n process.exitCode = 1;\n } else {\n console.log(\"\\n All checks passed. Your environment is ready.\\n\");\n }\n return;\n }\n\n const results = await Promise.all([\n checkNodeVersion(),\n checkVSCodeExtension(\"GitHub.copilot-chat\", \"GitHub Copilot (Chat)\", resolveHomeDir()),\n checkArcaneManifest(targetDir),\n Promise.resolve(checkIncidentReleaseGate()),\n checkPullRebase(targetDir),\n checkPushPolicy(targetDir),\n checkPlatformBranchPolicy(targetDir),\n checkDelegations(targetDir),\n checkMcpConfig(targetDir),\n checkUserTier(resolveHomeDir(), options.packageVersion),\n checkSpellScope(targetDir),\n checkGovernancePlaceholders(targetDir),\n checkComponentRequires(targetDir),\n checkNewlyAvailableComponents(targetDir),\n ]);\n\n // Add session continuity checks\n const sessionResults = await checkSessionContinuity(targetDir);\n results.push(...sessionResults);\n\n let allPassed = true;\n\n for (const result of results) {\n const skipped = result.passed && result.skipped === true;\n const icon = skipped ? \"–\" : result.passed ? \"✓\" : result.blocking === false ? \"⚠\" : \"✗\";\n const label = skipped\n ? \"skip\"\n : result.passed\n ? \"pass\"\n : result.blocking === false\n ? \"warn\"\n : \"FAIL\";\n console.log(` ${icon} [${label}] ${result.name}`);\n if (!result.passed || skipped) {\n console.log(` ${result.message}`);\n }\n if (!result.passed && result.blocking !== false) {\n allPassed = false;\n }\n }\n\n console.log();\n\n if (allPassed) {\n console.log(\" All checks passed. Your environment is ready.\\n\");\n } else {\n console.log(\" One or more checks failed. Fix the issues above before proceeding.\\n\");\n process.exitCode = 1;\n }\n\n // --fix: remediate missing session continuity files (runs regardless of allPassed,\n // since session continuity checks are warnings, not blocking failures)\n const hasMissingSession = sessionResults.some((r) => !r.passed);\n if (options.fix && hasMissingSession && assetsDir) {\n console.log(\" 🔧 Fixing missing session continuity files...\\n\");\n const created = await fixSessionContinuity(targetDir, assetsDir);\n if (created.length > 0) {\n for (const file of created) {\n console.log(` ✓ Created: ${file}`);\n }\n console.log(`\\n ${created.length} file(s) created. Repo is now close-session-ready.\\n`);\n } else {\n console.log(\" No files needed creation (all present).\\n\");\n }\n } else if (hasMissingSession && !options.fix) {\n console.log(\" 💡 Run `spell doctor --fix` to create missing session files.\\n\");\n }\n}\n","export type IncidentSeverity = \"critical\" | \"high\" | \"medium\" | \"low\";\nexport type IncidentImpact = \"data-integrity\" | \"security\" | \"other\";\nexport type IncidentConfidence = \"confirmed\" | \"research\";\nexport type IncidentState = \"open\" | \"resolved\" | \"deferred\";\n\nexport interface IncidentRecord {\n id: string;\n severity?: IncidentSeverity;\n impact: IncidentImpact;\n confidence: IncidentConfidence;\n state: IncidentState;\n detectedOn: string;\n resolution?: string;\n deferral?: string;\n}\n\nexport const INCIDENT_QUEUE: IncidentRecord[] = [\n { id: \"EF-05\", severity: \"medium\", impact: \"other\", confidence: \"confirmed\", state: \"open\", detectedOn: \"2026-07-31\" },\n { id: \"EF-08\", severity: \"medium\", impact: \"other\", confidence: \"confirmed\", state: \"open\", detectedOn: \"2026-07-31\" },\n { id: \"EF-16\", severity: \"high\", impact: \"other\", confidence: \"confirmed\", state: \"open\", detectedOn: \"2026-07-31\" },\n { id: \"EF-17\", severity: \"high\", impact: \"other\", confidence: \"confirmed\", state: \"open\", detectedOn: \"2026-07-31\" },\n { id: \"EF-20\", severity: \"high\", impact: \"other\", confidence: \"research\", state: \"open\", detectedOn: \"2026-07-31\" },\n { id: \"EF-21\", severity: \"high\", impact: \"other\", confidence: \"confirmed\", state: \"open\", detectedOn: \"2026-07-31\" },\n { id: \"EF-23\", severity: \"medium\", impact: \"other\", confidence: \"confirmed\", state: \"open\", detectedOn: \"2026-07-31\" },\n { id: \"EF-25\", severity: \"high\", impact: \"data-integrity\", confidence: \"confirmed\", state: \"resolved\", detectedOn: \"2026-07-14\", resolution: \"PRs 13 and 14\" },\n { id: \"EF-26\", severity: \"high\", impact: \"security\", confidence: \"confirmed\", state: \"resolved\", detectedOn: \"2026-07-31\", resolution: \"PR 15\" },\n { id: \"EF-27\", severity: \"high\", impact: \"security\", confidence: \"confirmed\", state: \"resolved\", detectedOn: \"2026-07-31\", resolution: \"PR 16\" },\n { id: \"EF-28\", severity: \"high\", impact: \"security\", confidence: \"confirmed\", state: \"resolved\", detectedOn: \"2026-07-31\", resolution: \"PR 17\" },\n { id: \"EF-29\", severity: \"medium\", impact: \"other\", confidence: \"confirmed\", state: \"resolved\", detectedOn: \"2026-07-31\", resolution: \"PR 18\" },\n];","import type { IncidentRecord } from \"../config/incidents.js\";\n\nconst DEFERRAL_PATTERN = /^Deferred \\d{4}-\\d{2}-\\d{2} — known open, accepting the risk\\.$/;\nconst BLOCKING_SEVERITIES = new Set([\"critical\", \"high\"]);\nconst PROTECTED_IMPACTS = new Set([\"data-integrity\", \"security\"]);\n\nexport interface IncidentGateResult {\n blocked: boolean;\n blockers: string[];\n acceptedRisks: string[];\n checked: number;\n}\n\nexport function evaluateIncidentGate(records: IncidentRecord[]): IncidentGateResult {\n const blockers: string[] = [];\n const acceptedRisks: string[] = [];\n const seen = new Set<string>();\n\n for (const record of records) {\n if (seen.has(record.id)) {\n blockers.push(`${record.id}: duplicate incident record`);\n continue;\n }\n seen.add(record.id);\n\n if (record.confidence === \"research\") continue;\n if (!PROTECTED_IMPACTS.has(record.impact)) continue;\n\n if (!record.severity) {\n blockers.push(`${record.id}: confirmed ${record.impact} incident is missing severity`);\n continue;\n }\n if (!BLOCKING_SEVERITIES.has(record.severity)) continue;\n\n if (record.state === \"resolved\") {\n if (!record.resolution?.trim()) {\n blockers.push(`${record.id}: resolved incident is missing resolution evidence`);\n }\n continue;\n }\n\n if (record.state === \"deferred\") {\n if (!record.deferral || !DEFERRAL_PATTERN.test(record.deferral)) {\n blockers.push(\n `${record.id}: deferral must be exactly \"Deferred <date> — known open, accepting the risk.\"`,\n );\n } else {\n acceptedRisks.push(`${record.id}: ${record.deferral}`);\n }\n continue;\n }\n\n blockers.push(\n `${record.id}: unresolved ${record.severity} ${record.impact} incident blocks release`,\n );\n }\n\n return {\n blocked: blockers.length > 0,\n blockers,\n acceptedRisks,\n checked: records.length,\n };\n}","/**\n * Generic denylist scanning engine: word-boundary matching, tree-walking,\n * and dedup -- shared by `scripts/org-token-lint.ts` (Arcane's own private\n * build gate) and `spell ward` (BC-21, a shipped CLI command any consumer\n * repo can run). Extracted here because these functions were never actually\n * org-token-specific -- only the *resolution* of which tokens to scan for\n * (an env var, a CLI flag, a mandatory vendor list) differs per caller.\n */\n\nimport { readFile, readdir } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\n\nexport interface DenylistRule {\n label: string;\n pattern: RegExp;\n}\n\nexport interface DenylistFinding {\n file: string;\n line: number;\n rule: string;\n}\n\nexport function escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/** Word-boundary rules: a token must not be flanked by another word character. */\nexport function createDenylistRules(\n tokens: string[],\n labelPrefix: string,\n): DenylistRule[] {\n return tokens.map((token, index) => ({\n label: `${labelPrefix}-${index + 1}`,\n pattern: new RegExp(\n `(?:^|[^A-Za-z0-9])${escapeRegExp(token)}(?=$|[^A-Za-z0-9])`,\n \"i\",\n ),\n }));\n}\n\n/**\n * Scans one file for denylist rules. Documented {UPPER_SNAKE} placeholders\n * are stripped before matching so they never trip a rule.\n */\nexport async function scanFile(\n absolutePath: string,\n displayPath: string,\n rules: DenylistRule[],\n): Promise<DenylistFinding[]> {\n const findings: DenylistFinding[] = [];\n let content: string;\n try {\n content = await readFile(absolutePath, \"utf8\");\n } catch {\n return findings;\n }\n\n const lines = content.split(\"\\n\");\n for (let index = 0; index < lines.length; index++) {\n const searchable = lines[index]!.replace(/\\{[A-Z][A-Z0-9_]*\\}/g, \"\");\n for (const rule of rules) {\n if (rule.pattern.test(searchable)) {\n findings.push({ file: displayPath, line: index + 1, rule: rule.label });\n break;\n }\n }\n }\n\n return findings;\n}\n\n/**\n * Directories never worth scanning -- generated output, dependencies, VCS\n * metadata. \".claude\" specifically covers `.claude/worktrees/`: each entry\n * there is a full nested checkout of this same repository (a linked git\n * worktree), so anything real inside one is scanned again at the primary\n * checkout's own path -- walking it too only triples every finding without\n * surfacing anything new (confirmed empirically widening the ARC-037 scan\n * repository-wide: every one of a worktree's findings duplicated a root-path\n * finding byte-for-byte).\n */\nexport const SKIP_DIRECTORIES = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \"coverage\",\n \".vitest\",\n \".claude\",\n]);\n\nexport const DEFAULT_SCANNABLE_EXTENSIONS = [\".md\", \".ts\", \".js\", \".json\", \".yml\", \".yaml\"];\n\n/**\n * Recursively collects every scannable file under `root`, returning\n * [absolutePath, repoRelativePath] pairs. `extensions` defaults to text\n * formats; a caller scanning binary asset strings (ward) passes its own list.\n */\nexport async function collectScannableFiles(\n root: string,\n repoRoot: string = root,\n extensions: string[] = DEFAULT_SCANNABLE_EXTENSIONS,\n): Promise<Array<[string, string]>> {\n const collected: Array<[string, string]> = [];\n let entries;\n try {\n entries = await readdir(root, { withFileTypes: true });\n } catch {\n return collected;\n }\n\n for (const entry of entries) {\n const absolutePath = join(root, entry.name);\n if (entry.isDirectory()) {\n if (SKIP_DIRECTORIES.has(entry.name)) continue;\n collected.push(...(await collectScannableFiles(absolutePath, repoRoot, extensions)));\n } else if (extensions.some((ext) => entry.name.endsWith(ext))) {\n const displayPath = relative(repoRoot, absolutePath).replace(/\\\\/g, \"/\");\n collected.push([absolutePath, displayPath]);\n }\n }\n\n return collected;\n}\n\n/**\n * Scans an entire repository tree (text files by default) for denylist rules.\n * `excludePrefixes` drops any file whose repo-relative display path contains\n * one of the given literal substrings before it's ever read -- a scan blind\n * spot the caller opted into, not a post-hoc filter on findings (ARC-037).\n */\nexport async function scanRepository(\n repoRoot: string,\n rules: DenylistRule[],\n extensions?: string[],\n excludePrefixes: string[] = [],\n): Promise<DenylistFinding[]> {\n if (rules.length === 0) return [];\n\n const findings: DenylistFinding[] = [];\n for (const [absolutePath, displayPath] of await collectScannableFiles(repoRoot, repoRoot, extensions)) {\n if (excludePrefixes.some((prefix) => displayPath.includes(prefix))) continue;\n findings.push(...(await scanFile(absolutePath, displayPath, rules)));\n }\n return findings;\n}\n\n/** Merges finding lists, collapsing duplicates reported at the same file:line. */\nexport function dedupeFindings(...lists: DenylistFinding[][]): DenylistFinding[] {\n const seen = new Map<string, DenylistFinding>();\n for (const list of lists) {\n for (const finding of list) {\n const key = `${finding.file}:${finding.line}`;\n if (!seen.has(key)) seen.set(key, finding);\n }\n }\n return [...seen.values()];\n}\n","import { execFile } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { win32 } from \"node:path\";\n\n/**\n * Budget for an external CLI that may reach the network (`gh api`, `az repos`).\n * Long enough for a real API round trip, short enough that an unauthenticated\n * or wedged CLI cannot hold a `spell doctor` run open indefinitely.\n */\nexport const EXTERNAL_CLI_TIMEOUT_MS = 10_000;\n\nexport interface ExecResult {\n stdout: string;\n stderr: string;\n}\n\n/**\n * Runs a child process under a hard timeout, with stdin closed.\n *\n * Mirrors the option set `runGit` already uses (src/modules/git.ts) minus\n * git's own environment, rather than inventing a second mechanism: Node's\n * native `timeout` instead of an AbortController, SIGTERM so a POSIX child\n * can still clean up after itself, `windowsHide` so no console window\n * flashes, and stdin ended immediately so a child that reads it gets EOF\n * instead of blocking on an open, unwritten pipe (EF-20).\n *\n * A timed-out child rejects with Node's own error carrying `killed: true`;\n * callers that only care whether a command answered can treat every rejection\n * the same way.\n *\n * The callers are the `gh` and `az` invocations behind `spell doctor`'s\n * platform-policy checks, which reach the network and previously had no\n * timeout at all -- an unauthenticated or wedged CLI could hold a doctor run\n * open with nothing to stop it.\n */\nexport function execFileWithTimeout(\n file: string,\n args: string[],\n timeoutMs: number,\n options: { env?: NodeJS.ProcessEnv } = {},\n): Promise<ExecResult> {\n return new Promise((resolve, reject) => {\n const child = execFile(\n file,\n args,\n {\n timeout: timeoutMs,\n killSignal: \"SIGTERM\",\n windowsHide: true,\n encoding: \"utf8\",\n ...(options.env ? { env: { ...process.env, ...options.env } } : {}),\n },\n (error, stdout, stderr) => {\n if (error) {\n reject(error);\n return;\n }\n resolve({ stdout, stderr });\n },\n );\n child.stdin?.end();\n });\n}\n\nexport interface AzureCliLaunch {\n file: string;\n prefixArgs: string[];\n env?: NodeJS.ProcessEnv;\n}\n\nexport interface AzureCliProbes {\n platform: NodeJS.Platform;\n pathEnv: string | undefined;\n readText(path: string): string | undefined;\n exists(path: string): boolean;\n}\n\nfunction defaultProbes(): AzureCliProbes {\n return {\n platform: process.platform,\n pathEnv: process.env[\"PATH\"] ?? process.env[\"Path\"],\n readText: (path) => {\n try {\n return readFileSync(path, \"utf8\");\n } catch {\n return undefined;\n }\n },\n exists: (path) => existsSync(path),\n };\n}\n\n// The one launcher line the Azure CLI MSI writes: its own interpreter, isolated, running its own module.\nconst MSI_LAUNCHER_LINE = /^\\s*\"%~dp0\\\\\\.\\.\\\\python\\.exe\"\\s+-IBm\\s+azure\\.cli\\s+%\\*\\s*$/im;\n\n/**\n * How to start the Azure CLI without any shell reading text that came from a git remote.\n *\n * Node cannot spawn `az.cmd` directly on Windows (EINVAL), and the usual cure, a shell, hands `&`, `|`, `\"`\n * and `%VAR%` in a remote's project or repository name to `cmd.exe`. So on Windows this reads the MSI\n * launcher, and only when it is exactly the recognised one, runs the interpreter it points at directly with\n * the same arguments. Anything else (no az.cmd, an unrecognised launcher, no interpreter) gets the plain\n * launch `az` by name, which is what runs an `az.exe` packaging and fails harmlessly otherwise. It never\n * falls back to a shell. Elsewhere `az` is a plain executable and is launched by name.\n */\nexport function resolveAzureCli(probes: AzureCliProbes = defaultProbes()): AzureCliLaunch {\n const plain: AzureCliLaunch = { file: \"az\", prefixArgs: [] };\n if (probes.platform !== \"win32\") return plain;\n\n for (const raw of (probes.pathEnv ?? \"\").split(\";\")) {\n const dir = raw.trim().replace(/^\"(.*)\"$/, \"$1\");\n if (!dir || !win32.isAbsolute(dir)) continue;\n const launcher = probes.readText(win32.join(dir, \"az.cmd\"));\n if (launcher === undefined) continue;\n if (!MSI_LAUNCHER_LINE.test(launcher)) return plain;\n const interpreter = win32.resolve(dir, \"..\", \"python.exe\");\n if (!probes.exists(interpreter)) return plain;\n return { file: interpreter, prefixArgs: [\"-IBm\", \"azure.cli\"], env: { AZ_INSTALLER: \"MSI\" } };\n }\n return plain;\n}\n","/**\n * Platform branch/merge policy verification (T11/BC-17).\n *\n * Report-only: nothing here mutates a repository or organization setting.\n * Rule-interpretation logic is split into pure functions taking\n * already-fetched JSON (evaluate*) so it is unit-testable with fixture\n * data, separate from the thin CLI-invocation wrappers (fetch*) that are\n * not exercised in CI (no live GitHub/ADO credentials to test against).\n *\n * GitHub Rulesets path is live-verified against this repo's own real\n * ruleset. The Azure DevOps path is implemented from documented API\n * shape only -- this repo has no ADO remote to test against -- and that\n * limitation is disclosed here rather than presented as verified.\n */\n\nimport {\n execFileWithTimeout,\n resolveAzureCli,\n EXTERNAL_CLI_TIMEOUT_MS,\n type AzureCliLaunch,\n} from \"./exec.js\";\n\n\n// ─── Declared ladder (git-conventions.md § Merge Strategy by Repo Risk) ──────\n\nexport type MergeMethod = \"merge\" | \"squash\" | \"rebase\";\nexport const SANCTIONED_MERGE_METHODS: readonly MergeMethod[] = [\"merge\", \"rebase\"];\n\nexport interface PolicyEvaluation {\n passed: boolean;\n /** null when no policy/ruleset was found to evaluate at all. */\n effectiveMergeMethods: MergeMethod[] | null;\n /** Methods the raw config nominally allows but a separate rule silently blocks. */\n declaredButBlocked: MergeMethod[];\n /** Methods effectively permitted despite never being sanctioned. */\n disallowedButPermitted: MergeMethod[];\n message: string;\n}\n\nfunction compareToLadder(\n effective: MergeMethod[],\n declaredButBlocked: MergeMethod[],\n interactionNote?: string,\n): PolicyEvaluation {\n const disallowedButPermitted = effective.filter(\n (m) => !SANCTIONED_MERGE_METHODS.includes(m),\n );\n const missingSanctioned = SANCTIONED_MERGE_METHODS.filter(\n (m) => !effective.includes(m),\n );\n const passed = disallowedButPermitted.length === 0 && missingSanctioned.length === 0;\n\n const messages: string[] = [];\n if (interactionNote) messages.push(interactionNote);\n if (disallowedButPermitted.length > 0) {\n messages.push(\n `disallowed method(s) effectively permitted: ${disallowedButPermitted.join(\", \")} (never sanctioned per git-conventions.md)`,\n );\n }\n if (missingSanctioned.length > 0) {\n messages.push(\n `sanctioned method(s) not effectively available: ${missingSanctioned.join(\", \")}`,\n );\n }\n\n return {\n passed,\n effectiveMergeMethods: effective,\n declaredButBlocked,\n disallowedButPermitted,\n message: passed\n ? `effective merge methods [${effective.join(\", \")}] match the declared ladder`\n : messages.join(\"; \"),\n };\n}\n\n// ─── GitHub Rulesets ──────────────────────────────────────────────────────────\n\nexport interface GitHubRulesetRule {\n type: string;\n parameters?: Record<string, unknown>;\n}\n\nexport interface GitHubRuleset {\n id: number;\n target: string;\n enforcement: string;\n rules: GitHubRulesetRule[];\n}\n\n/**\n * Pure evaluation: given already-fetched ruleset detail JSON for the rulesets\n * covering the target branch, determines the EFFECTIVE merge-method policy.\n *\n * Detects the interaction this repo's own 2026-08-24/25 incident found:\n * a `required_linear_history` rule silently blocks merge-commit PRs\n * regardless of what a `pull_request` rule's `allowed_merge_methods`\n * parameter declares.\n */\nexport function evaluateGitHubMergePolicy(\n rulesets: GitHubRuleset[],\n): PolicyEvaluation {\n const active = rulesets.filter(\n (r) => r.enforcement === \"active\" && r.target === \"branch\",\n );\n\n if (active.length === 0) {\n return {\n passed: false,\n effectiveMergeMethods: null,\n declaredButBlocked: [],\n disallowedButPermitted: [],\n message:\n \"No active branch ruleset covers the target branch (checked via Rulesets, not the classic protection endpoint, which false-negatives on Rulesets-only repos).\",\n };\n }\n\n // GitHub applies the most restrictive union when multiple rulesets target\n // the same ref -- intersect allowed_merge_methods across all of them.\n let allowedMergeMethods: MergeMethod[] | null = null;\n let hasRequiredLinearHistory = false;\n\n for (const ruleset of active) {\n for (const rule of ruleset.rules) {\n if (rule.type === \"required_linear_history\") {\n hasRequiredLinearHistory = true;\n }\n if (rule.type === \"pull_request\") {\n const methods = rule.parameters?.[\"allowed_merge_methods\"];\n if (Array.isArray(methods)) {\n const typed = methods as MergeMethod[];\n allowedMergeMethods =\n allowedMergeMethods === null\n ? typed\n : allowedMergeMethods.filter((m) => typed.includes(m));\n }\n }\n }\n }\n\n // No pull_request rule set allowed_merge_methods at all -- GitHub's\n // platform default (all three methods) applies, which is never compliant.\n const nominal = allowedMergeMethods ?? [\"merge\", \"squash\", \"rebase\"];\n const effective = hasRequiredLinearHistory\n ? nominal.filter((m) => m !== \"merge\")\n : nominal;\n const declaredButBlocked: MergeMethod[] =\n hasRequiredLinearHistory && nominal.includes(\"merge\") ? [\"merge\"] : [];\n\n const interactionNote =\n declaredButBlocked.length > 0\n ? `allowed_merge_methods declares [${nominal.join(\", \")}] but a required_linear_history rule silently blocks merge commits regardless -- effective methods are [${effective.join(\", \")}]`\n : undefined;\n\n return compareToLadder(effective, declaredButBlocked, interactionNote);\n}\n\nexport async function fetchGitHubRulesets(\n owner: string,\n repo: string,\n): Promise<GitHubRuleset[] | null> {\n try {\n const { stdout: listOut } = await execFileWithTimeout(\n \"gh\",\n [\"api\", `repos/${owner}/${repo}/rulesets`],\n EXTERNAL_CLI_TIMEOUT_MS,\n );\n const summaries = JSON.parse(listOut) as Array<{ id: number }>;\n const detailed = await Promise.all(\n summaries.map(async (s) => {\n const { stdout } = await execFileWithTimeout(\n \"gh\",\n [\"api\", `repos/${owner}/${repo}/rulesets/${s.id}`],\n EXTERNAL_CLI_TIMEOUT_MS,\n );\n return JSON.parse(stdout) as GitHubRuleset;\n }),\n );\n return detailed;\n } catch {\n return null;\n }\n}\n\n// ─── Azure DevOps branch policy ───────────────────────────────────────────────\n// NOT live-verified (see module doc comment and the PRD's Open Questions).\n\nexport interface AdoMergeTypePolicy {\n isEnabled: boolean;\n isBlocking: boolean;\n settings?: {\n useSquashMerge?: boolean;\n allowNoFastForward?: boolean;\n allowRebase?: boolean;\n allowRebaseMerge?: boolean;\n };\n}\n\nexport function evaluateAdoMergePolicy(\n policies: AdoMergeTypePolicy[],\n): PolicyEvaluation {\n const enabled = policies.filter((p) => p.isEnabled);\n if (enabled.length === 0) {\n return {\n passed: false,\n effectiveMergeMethods: null,\n declaredButBlocked: [],\n disallowedButPermitted: [],\n message: \"No enabled \\\"Limit merge types\\\" branch policy found for the target branch.\",\n };\n }\n\n const effective = new Set<MergeMethod>();\n for (const policy of enabled) {\n const s = policy.settings ?? {};\n if (s.allowNoFastForward) effective.add(\"merge\");\n if (s.allowRebase || s.allowRebaseMerge) effective.add(\"rebase\");\n if (s.useSquashMerge) effective.add(\"squash\");\n }\n\n return compareToLadder([...effective], []);\n}\n\nexport async function fetchAdoMergeTypePolicies(\n org: string,\n project: string,\n repoName: string,\n branch: string,\n launch: AzureCliLaunch = resolveAzureCli(),\n): Promise<AdoMergeTypePolicy[] | null> {\n const az = (args: string[]) =>\n execFileWithTimeout(launch.file, [...launch.prefixArgs, ...args], EXTERNAL_CLI_TIMEOUT_MS, {\n ...(launch.env ? { env: launch.env } : {}),\n });\n try {\n const orgUrl = `https://dev.azure.com/${org}`;\n // az repos policy list needs the repository's GUID, not its name.\n const { stdout: repoOut } = await az([\n \"repos\",\n \"show\",\n \"--repository\",\n repoName,\n \"--org\",\n orgUrl,\n \"--project\",\n project,\n \"--query\",\n \"id\",\n \"--output\",\n \"tsv\",\n ]);\n const repositoryId = repoOut.trim();\n if (!repositoryId) return null;\n\n const { stdout } = await az([\n \"repos\",\n \"policy\",\n \"list\",\n \"--repository-id\",\n repositoryId,\n \"--branch\",\n branch,\n \"--org\",\n orgUrl,\n \"--project\",\n project,\n \"--output\",\n \"json\",\n ]);\n return JSON.parse(stdout) as AdoMergeTypePolicy[];\n } catch {\n return null;\n }\n}\n\n// ─── Remote URL parsing ───────────────────────────────────────────────────────\n\nexport function parseGitHubRemote(url: string): { owner: string; repo: string } | null {\n const match = /github\\.com[/:]([^/]+)\\/([^/.]+?)(?:\\.git)?\\/?$/.exec(url);\n if (!match) return null;\n const [, owner, repo] = match;\n if (!owner || !repo) return null;\n return { owner, repo };\n}\n\n/**\n * One path segment of an Azure DevOps remote, as text. Git stores the remote percent-encoded\n * (`My%20Project`), and `az` wants the name itself. A malformed sequence makes the whole remote\n * unusable (`null`, never a throw), and so does a decoded segment beginning with `-`: the segments\n * become `az` arguments, and its parser would read such a value as an option rather than a name.\n * Decoding changes nothing about how they are launched: resolveAzureCli() never involves a shell, so\n * a decoded `&`, `|`, `\"` or `%` is a character in an argument and nothing more.\n */\nfunction decodeAdoSegment(raw: string): string | null {\n let decoded: string;\n try {\n decoded = decodeURIComponent(raw);\n } catch {\n return null;\n }\n if (!decoded || decoded.startsWith(\"-\")) return null;\n return decoded;\n}\n\nexport function parseAdoRemote(\n url: string,\n): { org: string; project: string; repo: string } | null {\n // Modern: https://dev.azure.com/{org}/{project}/_git/{repo}\n // Legacy: https://{org}.visualstudio.com/{project}/_git/{repo}\n const match =\n /dev\\.azure\\.com\\/([^/]+)\\/([^/]+)\\/_git\\/([^/?]+)/.exec(url) ??\n /([^./]+)\\.visualstudio\\.com\\/([^/]+)\\/_git\\/([^/?]+)/.exec(url);\n if (!match) return null;\n const [, rawOrg, rawProject, rawRepo] = match;\n if (!rawOrg || !rawProject || !rawRepo) return null;\n const org = decodeAdoSegment(rawOrg);\n const project = decodeAdoSegment(rawProject);\n const repo = decodeAdoSegment(rawRepo);\n if (org === null || project === null || repo === null) return null;\n return { org, project, repo };\n}\n","/**\n * ARC-051 — placeholder taxonomy for governance documents (#277).\n *\n * Governance documents use `{UPPER_SNAKE}` tokens in two ways:\n * - runtime-resolved tokens, which an agent resolves when it uses the\n * document (legal in a `status: active` document), enumerated once in\n * `spell-authoring-standards.md` between the runtime-placeholders markers;\n * - fill-in slots, which the operator replaces by hand. A document built\n * around them carries `status: template` and is not scanned.\n *\n * This module finds tokens in `status: active` documents that are in neither\n * category (\"unknown\"). It only reads files; `spell doctor` turns its result\n * into a non-blocking warning (ARC-051 decision 4).\n */\n\nimport { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\n/** The installed standard that holds the runtime-token list. */\nexport const PLACEHOLDER_STANDARD_FILE = \"spell-authoring-standards.md\";\nexport const RUNTIME_PLACEHOLDERS_START = \"<!-- runtime-placeholders:start -->\";\nexport const RUNTIME_PLACEHOLDERS_END = \"<!-- runtime-placeholders:end -->\";\n\n/** `{UPPER_SNAKE}`: an uppercase letter, then uppercase letters/digits in `_`-separated words. */\nconst PLACEHOLDER_PATTERN = /(?<!\\$)\\{([A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*)\\}/g;\n\nconst FRONTMATTER_PATTERN = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---[ \\t]*(?:\\r?\\n|$)/;\n\n/** The document's frontmatter `status:` value, or null when there is none. */\nexport function parseFrontmatterStatus(content: string): string | null {\n const block = FRONTMATTER_PATTERN.exec(content);\n if (!block) return null;\n const status = /^status:[ \\t]*[\"']?([^\"'\\s#]+)/m.exec(block[1]!);\n return status ? status[1]! : null;\n}\n\nfunction stripFrontmatter(content: string): string {\n const block = FRONTMATTER_PATTERN.exec(content);\n return block ? content.slice(block[0].length) : content;\n}\n\n/**\n * Drops fenced code blocks (``` or ~~~, opening fence indented at most three\n * spaces; an unclosed fence runs to the end of the document, as in\n * CommonMark). Indented code blocks are not recognized: in these documents a\n * four-space indent is almost always list continuation, not code.\n */\nfunction stripFencedCode(text: string): string {\n const kept: string[] = [];\n let fence: { char: string; length: number } | null = null;\n for (const line of text.split(/\\r?\\n/)) {\n if (fence) {\n const close = /^ {0,3}(`{3,}|~{3,})[ \\t]*$/.exec(line);\n if (close && close[1]![0] === fence.char && close[1]!.length >= fence.length) fence = null;\n kept.push(\"\");\n continue;\n }\n const open = /^ {0,3}(`{3,}|~{3,})/.exec(line);\n // A backtick fence's info string may not itself contain a backtick.\n if (open && !(open[1]![0] === \"`\" && line.slice(open[0].length).includes(\"`\"))) {\n fence = { char: open[1]![0]!, length: open[1]!.length };\n kept.push(\"\");\n continue;\n }\n kept.push(line);\n }\n return kept.join(\"\\n\");\n}\n\n/**\n * Drops inline code spans. A run of N backticks opens a span that closes at\n * the next run of exactly N backticks in the same paragraph; a run with no\n * match is literal text.\n */\nfunction stripInlineCode(text: string): string {\n return text\n .split(/\\n[ \\t]*\\n/)\n .map((paragraph) => {\n let out = \"\";\n let i = 0;\n while (i < paragraph.length) {\n if (paragraph[i] !== \"`\") {\n out += paragraph[i];\n i += 1;\n continue;\n }\n let runEnd = i;\n while (paragraph[runEnd] === \"`\") runEnd += 1;\n const run = runEnd - i;\n let close = -1;\n let j = runEnd;\n while (j < paragraph.length) {\n if (paragraph[j] !== \"`\") {\n j += 1;\n continue;\n }\n let k = j;\n while (paragraph[k] === \"`\") k += 1;\n if (k - j === run) {\n close = j;\n break;\n }\n j = k;\n }\n if (close === -1) {\n out += paragraph.slice(i, runEnd);\n i = runEnd;\n } else {\n out += \" \";\n i = close + run;\n }\n }\n return out;\n })\n .join(\"\\n\\n\");\n}\n\n/**\n * The distinct `{UPPER_SNAKE}` tokens in a document's prose, in first-seen\n * order. Frontmatter, fenced code and inline code are ignored: a token there\n * is an example or a literal, not a slot the reader is asked to fill.\n * `${NAME}` is shell/template syntax and is not a placeholder.\n */\nexport function findPlaceholderTokens(content: string): string[] {\n const prose = stripInlineCode(stripFencedCode(stripFrontmatter(content)));\n const seen = new Set<string>();\n for (const match of prose.matchAll(PLACEHOLDER_PATTERN)) seen.add(`{${match[1]!}}`);\n return [...seen];\n}\n\n/**\n * The runtime-resolved tokens listed between the runtime-placeholders markers:\n * each list item whose first element is a code span holding one token, e.g.\n * \"- `{ADO_ORG}` — …\". Returns null when the markers are absent or out of\n * order, so a caller can tell \"no list installed\" from \"an empty list\".\n */\nexport function parseRuntimePlaceholders(standardContent: string): Set<string> | null {\n const start = standardContent.indexOf(RUNTIME_PLACEHOLDERS_START);\n const end = standardContent.indexOf(RUNTIME_PLACEHOLDERS_END);\n if (start === -1 || end === -1 || end < start) return null;\n const region = standardContent.slice(start + RUNTIME_PLACEHOLDERS_START.length, end);\n const tokens = new Set<string>();\n for (const match of region.matchAll(/^[ \\t]*[-*][ \\t]+`(\\{[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*\\})`/gm)) {\n tokens.add(match[1]!);\n }\n return tokens;\n}\n\nexport interface PlaceholderFinding {\n /** File name relative to the scanned governance directory. */\n file: string;\n /** Unknown tokens, in first-seen order, braces included. */\n tokens: string[];\n}\n\nexport type PlaceholderScan =\n | { status: \"skipped\"; reason: string }\n | {\n status: \"scanned\";\n runtimeTokens: Set<string>;\n /** `status: active` documents scanned. */\n activeDocs: string[];\n /** `status: template` documents skipped (ARC-051 decision 3). */\n templateDocs: string[];\n /** One entry per active document with at least one unknown token. */\n findings: PlaceholderFinding[];\n };\n\n/**\n * Scans every `*.md` directly inside `governanceDir`. Only `status: active`\n * documents are checked; template, draft, deprecated and status-less documents\n * are not. Skipped (never failed) when the runtime-token list is not installed:\n * without it every token would read as unknown.\n */\nexport async function scanGovernancePlaceholders(governanceDir: string): Promise<PlaceholderScan> {\n let standard: string;\n try {\n standard = await readFile(join(governanceDir, PLACEHOLDER_STANDARD_FILE), \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return { status: \"skipped\", reason: `${PLACEHOLDER_STANDARD_FILE} is not installed` };\n }\n throw err;\n }\n const runtimeTokens = parseRuntimePlaceholders(standard);\n if (!runtimeTokens) {\n return {\n status: \"skipped\",\n reason: `${PLACEHOLDER_STANDARD_FILE} has no runtime-placeholders list (an older version?)`,\n };\n }\n\n const files = (await readdir(governanceDir, { withFileTypes: true }))\n .filter((entry) => entry.isFile() && entry.name.endsWith(\".md\"))\n .map((entry) => entry.name)\n .sort();\n\n const activeDocs: string[] = [];\n const templateDocs: string[] = [];\n const findings: PlaceholderFinding[] = [];\n for (const file of files) {\n const content = await readFile(join(governanceDir, file), \"utf8\");\n const status = parseFrontmatterStatus(content);\n if (status === \"template\") templateDocs.push(file);\n if (status !== \"active\") continue;\n activeDocs.push(file);\n const unknown = findPlaceholderTokens(content).filter((token) => !runtimeTokens.has(token));\n if (unknown.length > 0) findings.push({ file, tokens: unknown });\n }\n return { status: \"scanned\", runtimeTokens, activeDocs, templateDocs, findings };\n}\n","import { readFile } from \"node:fs/promises\";\nimport { runWard, type WardExclusion } from \"../modules/ward.js\";\n\nexport interface WardCliOptions {\n terms?: string;\n termsFile?: string;\n gate?: boolean;\n}\n\nfunction parseTerms(raw?: string): string[] {\n if (!raw) return [];\n return raw\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean);\n}\n\n/**\n * ARC-041: --terms-file escapes the shell-history exposure a --terms value\n * has on any multi-user or logged machine. Same delimiter convention as\n * resolvePrivateTokens() (comma and/or newline) -- one format across every\n * place this framework accepts a local denylist, nothing new to document.\n */\nfunction parseTermsFileContent(raw: string): string[] {\n return raw\n .split(/[,\\r\\n]+/)\n .map((t) => t.trim())\n .filter(Boolean);\n}\n\nasync function readTermsFile(filePath: string): Promise<string[]> {\n try {\n return parseTermsFileContent(await readFile(filePath, \"utf8\"));\n } catch (error: unknown) {\n console.warn(\n `⚠ arcane: could not read --terms-file \"${filePath}\": ${(error as Error).message}`,\n );\n return [];\n }\n}\n\n/**\n * spell ward CLI entry point. Report-only by default (matching\n * spell-check-drift's own convention); --gate exits non-zero on any `leak`\n * finding so it can be wired into CI. A `protected-vendor-identifier`\n * finding is informational -- it exists to make a human look before an\n * automated rename touches a real vendor identifier, not to fail a build by\n * itself.\n */\nexport async function runWardCli(\n targetDir: string,\n options: WardCliOptions = {},\n exclusions: WardExclusion[] = [],\n): Promise<void> {\n const terms = [\n ...parseTerms(options.terms),\n ...(options.termsFile ? await readTermsFile(options.termsFile) : []),\n ];\n const report = await runWard(targetDir, { terms, exclusions });\n\n console.log(\"\\nspell ward — scanning for leaked identifiers\\n\");\n\n const leaks = report.findings.filter((f) => f.category === \"leak\");\n const protectedHits = report.findings.filter((f) => f.category === \"protected-vendor-identifier\");\n\n if (leaks.length === 0 && protectedHits.length === 0) {\n console.log(\" ✓ No denylisted terms found.\");\n }\n\n for (const finding of leaks) {\n const where = finding.line === 0 ? `${finding.file} (filename)` : `${finding.file}:${finding.line}`;\n console.log(` ✗ [leak] ${where} — rule: ${finding.rule}`);\n }\n\n for (const finding of protectedHits) {\n const where = finding.line === 0 ? `${finding.file} (filename)` : `${finding.file}:${finding.line}`;\n console.log(\n ` ⚠ [protected-vendor-identifier] ${where} — rule: ${finding.rule} — verify before any automated rename touches this line`,\n );\n }\n\n if (report.mediaFlags.length > 0) {\n console.log(\"\\n Grep-proof media flagged for manual review:\");\n for (const flag of report.mediaFlags) {\n console.log(` ? ${flag.file} — ${flag.reason}`);\n }\n }\n\n console.log();\n\n if (options.gate && leaks.length > 0) {\n console.log(` ${leaks.length} leak(s) found. Failing (--gate mode).\\n`);\n process.exitCode = 1;\n return;\n }\n\n if (leaks.length > 0) {\n console.log(` ${leaks.length} leak(s) found. Run with --gate to fail CI on this.\\n`);\n }\n}\n","/**\n * spell ward (T18/BC-21): local IP/trademark leakage scan.\n *\n * Finds what leaked IN -- third-party identifiers, brand tokens, org names --\n * baked into a repo's content, filenames, or binary assets. `--terms` is a\n * generic denylist: seed it from the operator's OWN identity too, not just\n * the org's -- a username, machine name, or personal handle leaks the same\n * way a private venture name does (I1; the motivating incident was exactly\n * this -- a branch name containing a username, sitting in this repo's own\n * ai-context file, in a public repo). The opposite concern from spell scry\n * (T18), which clears a candidate name before it ships OUT.\n *\n * Deliberately out of scope: secret/credential detection. That is a\n * different scanning concern reserved for BC-10's secret-detection ADR\n * (ARC-037, still Proposed) -- ward never attempts pattern-matching on\n * keys, tokens, or credentials, only on identifiers and trademarks.\n */\n\nimport { readdir, readFile } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport {\n createDenylistRules,\n collectScannableFiles,\n SKIP_DIRECTORIES,\n type DenylistRule,\n type DenylistFinding,\n} from \"./denylist-scan.js\";\n\n/**\n * Ward's own content scan, deliberately NOT reusing denylist-scan.ts's\n * `scanFile` -- that function stops at the first matching rule per line\n * (correct for org-token-lint, which only needs \"does this line have any\n * hit\"), but ward must distinguish a leak from a protected-vendor-identifier\n * hit even when both land on the same line, so it needs every matching rule\n * per line, not just the first.\n */\nasync function scanFileAllRules(\n absolutePath: string,\n displayPath: string,\n rules: DenylistRule[],\n): Promise<DenylistFinding[]> {\n const findings: DenylistFinding[] = [];\n let content: string;\n try {\n content = await readFile(absolutePath, \"utf8\");\n } catch {\n return findings;\n }\n\n const lines = content.split(\"\\n\");\n for (let index = 0; index < lines.length; index++) {\n const searchable = lines[index]!.replace(/\\{[A-Z][A-Z0-9_]*\\}/g, \"\");\n for (const rule of rules) {\n if (rule.pattern.test(searchable)) {\n findings.push({ file: displayPath, line: index + 1, rule: rule.label });\n }\n }\n }\n\n return findings;\n}\n\n// ─── Mandatory vendor-identifier protection list ─────────────────────────────\n\n/**\n * Always active, independent of any user-supplied terms. A reasonable\n * starting set (not exhaustive -- see the PRD's Open Questions), covering\n * widely-referenced third-party AI model and vendor identifiers that an\n * automated rename/refactor pass could plausibly mistake for a project-local\n * token and \"correct\" into something else, corrupting a real API reference.\n * A match here is reported as PROTECTED, never as a plain leak finding --\n * ward's job for these terms is to stop automated correction, not to flag\n * their legitimate presence as a problem.\n */\nexport const VENDOR_IDENTIFIER_PROTECTION_LIST: readonly string[] = [\n \"claude\",\n \"gpt-4\",\n \"gpt-3.5\",\n \"gemini\",\n \"llama\",\n \"mistral\",\n \"copilot\",\n \"anthropic\",\n \"openai\",\n];\n\nexport interface WardExclusion {\n /** The denylist rule label this exclusion applies to. */\n rule: string;\n /** A substring that, if present on the same line as a match, suppresses it. */\n context: string;\n reason: string;\n}\n\nexport interface WardOptions {\n /** User-supplied terms, in addition to the mandatory vendor list. */\n terms?: string[];\n /** Substring-hazard exclusions (the \"author/provision\" class). */\n exclusions?: WardExclusion[];\n /** Extra file extensions to treat as scannable binary-asset carriers. */\n binaryExtensions?: string[];\n}\n\nexport interface WardFinding extends DenylistFinding {\n category: \"leak\" | \"protected-vendor-identifier\";\n}\n\nexport interface WardMediaFlag {\n file: string;\n reason: string;\n}\n\nexport interface WardReport {\n findings: WardFinding[];\n /** Grep-proof media flagged for manual review, never silently cleared. */\n mediaFlags: WardMediaFlag[];\n}\n\n/** Binary media formats where reliable text extraction isn't possible. */\nconst GREP_PROOF_EXTENSIONS = [\".gif\", \".png\", \".jpg\", \".jpeg\", \".mp4\", \".mov\", \".mp3\", \".wav\"];\n\n/** Extensions ward treats as text-scannable content, beyond denylist-scan's default set. */\nconst WARD_TEXT_EXTENSIONS = [\n \".md\", \".ts\", \".tsx\", \".js\", \".jsx\", \".json\", \".yml\", \".yaml\", \".txt\", \".mdx\",\n];\n\nfunction applyExclusions(\n findings: DenylistFinding[],\n fileLines: Map<string, string[]>,\n exclusions: WardExclusion[],\n): DenylistFinding[] {\n if (exclusions.length === 0) return findings;\n return findings.filter((finding) => {\n const applicable = exclusions.filter((e) => e.rule === finding.rule);\n if (applicable.length === 0) return true;\n const lines = fileLines.get(finding.file);\n const lineText = lines?.[finding.line - 1] ?? \"\";\n return !applicable.some((e) => lineText.includes(e.context));\n });\n}\n\n/** Scans filenames themselves (not just content) against the denylist rules. */\nasync function scanFilenames(\n root: string,\n rules: DenylistRule[],\n repoRoot: string = root,\n): Promise<DenylistFinding[]> {\n const findings: DenylistFinding[] = [];\n let entries;\n try {\n entries = await readdir(root, { withFileTypes: true });\n } catch {\n return findings;\n }\n\n for (const entry of entries) {\n const absolutePath = join(root, entry.name);\n if (entry.isDirectory()) {\n if (SKIP_DIRECTORIES.has(entry.name)) continue;\n findings.push(...(await scanFilenames(absolutePath, rules, repoRoot)));\n continue;\n }\n const displayPath = relative(repoRoot, absolutePath).replace(/\\\\/g, \"/\");\n for (const rule of rules) {\n if (rule.pattern.test(entry.name)) {\n findings.push({ file: displayPath, line: 0, rule: rule.label });\n break;\n }\n }\n }\n\n return findings;\n}\n\n/** Flags grep-proof media files for manual review -- never silently cleared. */\nasync function flagGrepProofMedia(root: string, repoRoot: string = root): Promise<WardMediaFlag[]> {\n const files = await collectScannableFiles(root, repoRoot, GREP_PROOF_EXTENSIONS);\n return files.map(([, displayPath]) => ({\n file: displayPath,\n reason: \"binary media format -- text extraction is unreliable; review manually for embedded identifiers\",\n }));\n}\n\n/**\n * Runs the full ward scan: content, filenames, and grep-proof media flags.\n * Report-only -- never mutates anything. Categorizes each finding as a plain\n * leak or a protected-vendor-identifier hit per VENDOR_IDENTIFIER_PROTECTION_LIST.\n */\nexport async function runWard(repoRoot: string, options: WardOptions = {}): Promise<WardReport> {\n const userTerms = options.terms ?? [];\n const exclusions = options.exclusions ?? [];\n const extensions = [...WARD_TEXT_EXTENSIONS, ...(options.binaryExtensions ?? [])];\n\n const userRules = createDenylistRules(userTerms, \"ward-term\");\n const vendorRules = createDenylistRules([...VENDOR_IDENTIFIER_PROTECTION_LIST], \"ward-vendor\");\n const allRules = [...userRules, ...vendorRules];\n\n const contentFiles = await collectScannableFiles(repoRoot, repoRoot, extensions);\n const fileLines = new Map<string, string[]>();\n const contentFindings: DenylistFinding[] = [];\n for (const [absolutePath, displayPath] of contentFiles) {\n const findings = await scanFileAllRules(absolutePath, displayPath, allRules);\n if (findings.length > 0) {\n try {\n fileLines.set(displayPath, (await readFile(absolutePath, \"utf8\")).split(\"\\n\"));\n } catch {\n // Unreadable after the initial scan (rare race) -- leave unmapped; exclusions simply won't match.\n }\n }\n contentFindings.push(...findings);\n }\n\n // Not dedupeFindings: that collapses on file:line alone, which would\n // wrongly merge a leak and a protected-vendor-identifier hit that land on\n // the same line -- exactly the case ward needs to keep distinct. Content\n // (line >= 1) and filename (line 0) findings can never collide on\n // file:line, and scanFileAllRules never double-reports the same rule on\n // the same line, so a plain concatenation is already duplicate-free.\n const filenameFindings = await scanFilenames(repoRoot, allRules);\n const combined = [...contentFindings, ...filenameFindings];\n const excluded = applyExclusions(combined, fileLines, exclusions);\n\n const vendorLabels = new Set(vendorRules.map((r) => r.label));\n const findings: WardFinding[] = excluded.map((f) => ({\n ...f,\n category: vendorLabels.has(f.rule) ? \"protected-vendor-identifier\" : \"leak\",\n }));\n\n const mediaFlags = await flagGrepProofMedia(repoRoot);\n\n return { findings, mediaFlags };\n}\n","import { basename, resolve } from \"node:path\";\nimport { input } from \"@inquirer/prompts\";\nimport { readManifest, writeManifest, ManifestNotFoundError } from \"../modules/manifest.js\";\nimport {\n removePrePushHook,\n restorePushUrls,\n blockedRemotes,\n isHookEnforced,\n} from \"../modules/push-safety.js\";\nimport { printInfo, printSuccess, printWarning } from \"../modules/banner.js\";\nimport type { ArcaneManifest } from \"../types.js\";\n\n/**\n * Runs `spell unblock-push` (EF-09 R5).\n *\n * Deliberately its own command and never a side effect of `init`/`update`:\n * a protection that can be lifted by a routine command is not a protection.\n *\n * Two gates, and the PRD is honest about what each one is worth:\n *\n * - **Interactive TTY only.** This is the real bar against a scripted agent —\n * the threat actor the PRD ranks second, since a push is irreversible. It is\n * not absolute (a pseudo-TTY defeats it), and the PRD says so rather than\n * claiming otherwise.\n * - **Typed repository name.** This guards a human against unblocking the wrong\n * repository. It is explicitly NOT agent-resistance: anything that can read\n * `.arcane.json` can read the name back.\n */\nexport async function runUnblockPush(targetDir: string): Promise<void> {\n let manifest: ArcaneManifest;\n try {\n manifest = await readManifest(targetDir);\n } catch (err) {\n if (err instanceof ManifestNotFoundError) {\n console.error('Not initialized. Run \"spell init\" first.');\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n throw err;\n }\n\n // Gate on the CONTROLS, not the declaration. A partial lift writes\n // push_policy \"open\" while the hook or a sentinel URL is still in force, and\n // keying the early return on the manifest meant that state made this command\n // refuse to run again — the failed attempt closed its own retry path. Making\n // partial lifts visible (0.20.1) is not the same as making them recoverable.\n const policy = manifest.push_policy ?? \"open\";\n const stillInForce =\n (await isHookEnforced(targetDir)) || (await blockedRemotes(targetDir)).length > 0;\n if (policy === \"open\" && !stillInForce) {\n printInfo(\"This repository is not push-blocked — nothing to undo.\");\n return;\n }\n if (policy === \"open\") {\n printWarning(\n \"The manifest says push_policy: open, but this repository still has Arcane's push block \" +\n \"in force — most likely a previous unblock only partly succeeded. Continuing so it can \" +\n \"be finished.\",\n );\n }\n\n if (!process.stdin.isTTY) {\n console.error(\n \"spell unblock-push must be run from an interactive terminal.\\n\" +\n \"Lifting a push block is a deliberate decision, so it cannot be scripted, piped, or run in CI.\",\n );\n process.exit(1);\n return;\n }\n\n const repoName = basename(resolve(targetDir));\n if (repoName === \"\") {\n // A repository at a filesystem root has no basename, which would turn the\n // confirmation into \"press Enter\". A gate that can silently become vacuous\n // must fail closed instead.\n console.error(\n \"Could not determine a repository name to confirm against (is this repository at a filesystem root?). Refusing to unblock.\",\n );\n process.exit(1);\n return;\n }\n\n printWarning(`This repository's push_policy is \"${policy}\".`);\n printInfo(\n \"Unblocking restores the ability to push this repository's full history to its remote. \" +\n \"Deleting a file later does not undo a push: the content stays in history, in any clone \" +\n \"already made, and in the remote's own backups.\",\n );\n console.log();\n\n const typed = await input({\n message: `Type the repository name to confirm (${repoName}):`,\n });\n\n if (typed.trim() !== repoName) {\n console.error(`\\nName did not match (\"${typed.trim()}\" ≠ \"${repoName}\"). Nothing changed.`);\n process.exit(1);\n return;\n }\n\n await removePrePushHook(targetDir);\n const results = await restorePushUrls(targetDir);\n const restored = results.filter((r) => r.status === \"restored\").map((r) => r.remote);\n const failed = results.filter((r) => r.status === \"failed\");\n // A remote still carrying the sentinel after we claimed to have unblocked it\n // is the manifest-lies-about-posture failure in the opposite direction, so it\n // is checked directly rather than inferred from what we think we did.\n const stillBlocked = await blockedRemotes(targetDir);\n\n // No \"just this once\" mode: the manifest must never claim a protection the\n // repository no longer has.\n const updated: ArcaneManifest = {\n ...manifest,\n push_policy: \"open\",\n push_policy_unblocked_at: new Date().toISOString(),\n };\n await writeManifest(targetDir, updated);\n\n // Check the hook too, not just the URLs: `removePrePushHook` declines to touch\n // a foreign or unreadable core.hooksPath, so it can legitimately do nothing.\n const hookStillOn = await isHookEnforced(targetDir);\n const fullyLifted = failed.length === 0 && stillBlocked.length === 0 && !hookStillOn;\n\n // Never let a partial result read as a clean one. Reporting success first and\n // appending warnings underneath is exactly the shape this feature refuses to\n // accept elsewhere.\n if (fullyLifted) {\n printSuccess(`Push unblocked for \"${repoName}\".`);\n } else {\n printWarning(\n `Push only PARTIALLY unblocked for \"${repoName}\". The manifest now says open; the ` +\n \"repository is not fully open yet. Details below.\",\n );\n }\n if (restored.length > 0) {\n printInfo(`Restored push URL for: ${restored.join(\", \")}.`);\n }\n if (hookStillOn) {\n printWarning(\n \"The pre-push hook is still in force. Arcane did not remove it because `core.hooksPath` is \" +\n \"not one it owns, or could not be read. Check `git config --show-scope --get core.hooksPath`.\",\n );\n }\n if (failed.length > 0) {\n printWarning(\n `Could not restore the push URL for: ${failed\n .map((r) => `${r.remote} (${r.reason ?? \"unknown error\"})`)\n .join(\"; \")}.`,\n );\n }\n if (stillBlocked.length > 0) {\n // Most likely cause: the remote was renamed while blocked, so the recorded\n // original moved with it but a *different* remote now carries the sentinel.\n printWarning(\n `These remotes still have a blocked push URL and no recorded original: ${stillBlocked.join(\", \")}. ` +\n \"Set them yourself with `git remote set-url --push <name> <url>` — until then, pushing to \" +\n \"them will fail even though the manifest now says open.\",\n );\n }\n printInfo(\n \"Recorded in .arcane.json as push_policy: open, with the time it was lifted. \" +\n \"Commit that change so the repository's actual posture stays visible to everyone.\",\n );\n}\n","import { readManifest, writeManifest, ManifestNotFoundError } from \"../modules/manifest.js\";\nimport { inspectGitRepository } from \"../modules/git.js\";\nimport {\n applyBlockedPushControls,\n hookInForce,\n isHookEnforced,\n listRemotes,\n undisabledRemotes,\n} from \"../modules/push-safety.js\";\nimport { printInfo, printSuccess, printWarning } from \"../modules/banner.js\";\nimport type { ArcaneManifest } from \"../types.js\";\n\n/**\n * Runs `spell block-push` (ARC-049 decision 1): enforces `push_policy:\n * \"blocked\"` on an already-initialized repository by installing exactly what\n * `spell init` installs for it, through the same helper and with the same\n * refusals.\n *\n * No TTY gate, unlike `spell unblock-push`: this can only tighten. The\n * manifest is changed to `blocked` only once both controls are in force, so\n * a refusal never leaves it claiming a protection the repository lacks.\n */\nexport async function runBlockPush(targetDir: string): Promise<void> {\n let manifest: ArcaneManifest;\n try {\n manifest = await readManifest(targetDir);\n } catch (err) {\n if (err instanceof ManifestNotFoundError) {\n console.error('Not initialized. Run \"spell init\" first.');\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n throw err;\n }\n\n if (manifest.scope === \"user\") {\n console.error(\n \"spell block-push applies to a repository, not the user tier: push_policy describes a repository.\",\n );\n process.exit(1);\n return;\n }\n\n if ((await inspectGitRepository(targetDir)).status === \"not-repository\") {\n console.error(\"spell block-push needs a Git repository: there is nothing here to block.\");\n process.exit(1);\n return;\n }\n\n const recorded = manifest.push_policy ?? \"open\";\n const alreadyEnforced =\n recorded === \"blocked\" &&\n (await isHookEnforced(targetDir)) &&\n (await undisabledRemotes(targetDir)).length === 0;\n if (alreadyEnforced) {\n printSuccess(\"Push is already blocked and enforced here. Nothing changed.\");\n if ((await listRemotes(targetDir)).length === 0) {\n printWarning(\n \"No remote is configured, so only the pre-push hook is active. Re-run `spell block-push` \" +\n \"after adding a remote to disable its push URL too.\",\n );\n }\n printInfo(\"Run `spell unblock-push` from a terminal to undo this.\");\n return;\n }\n\n const outcome = await applyBlockedPushControls(targetDir, {\n stopOnHookRefusal: true,\n emit: (m) => (m.level === \"warning\" ? printWarning(m.text) : printInfo(m.text)),\n });\n\n const failedRemotes = outcome.urls.filter((u) => u.status === \"failed\");\n if (!hookInForce(outcome.hook) || failedRemotes.length > 0) {\n printWarning(\n outcome.stoppedAtHook\n ? \"Nothing was changed. Resolve the refusal above and re-run `spell block-push`.\"\n : \"Push is NOT fully blocked: the remotes named above can still be pushed to. Fix them and \" +\n \"re-run `spell block-push`.\",\n );\n if (recorded !== \"blocked\") {\n printInfo(\n `The manifest still records push_policy: \"${recorded}\" — it is not changed to \"blocked\" ` +\n \"until both controls are in force.\",\n );\n }\n process.exit(1);\n return;\n }\n\n if (recorded !== \"blocked\") {\n await writeManifest(targetDir, { ...manifest, push_policy: \"blocked\" });\n printSuccess(`Push blocked. Recorded push_policy: \"blocked\" (was \"${recorded}\") in .arcane.json.`);\n printInfo(\"Commit that change so the repository's posture stays visible to everyone.\");\n } else {\n printSuccess('Push blocked. The manifest already recorded push_policy: \"blocked\"; it is now enforced.');\n }\n}\n","import { access } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport {\n discoverPrograms,\n programFromPlanPath,\n runReportGeneration,\n type ReportProgram,\n type SkippedPlan,\n} from \"../modules/show-report/generate.js\";\nimport { isShallowRepository } from \"../modules/show-report/sources.js\";\nimport { REPORT_THEMES, type ReportTheme } from \"../modules/show-report/theme.js\";\n\nexport interface ReportCliOptions {\n /** Report on one PLAN.md instead of auto-discovering docs/plans/*\\/PLAN.md. */\n plan?: string;\n /** Write show-report.{json,html} here instead of next to the plan. */\n out?: string;\n /** Accepted for forward compatibility; external snapshotting is not implemented yet. */\n refresh?: boolean;\n /** Force the toggle's initial state: auto | light | dark (default auto). */\n theme?: string;\n}\n\n/**\n * `spell report` (SR-03): generate a program's Show Report -- show-report.json\n * and show-report.html -- from its PLAN.md, OPERATOR-QUEUE.md, the\n * verification ledger and local git history. Offline by construction (ARC-042):\n * the template ships inside this package's dist/assets/, and nothing here\n * touches the network. Writes only files that differ from a fresh\n * regeneration, so re-running on an up-to-date repository changes nothing.\n */\nexport async function runReport(\n targetDir: string,\n options: ReportCliOptions = {},\n assetsDir: string,\n): Promise<void> {\n const templatePath = join(assetsDir, \"report\", \"show-report.template.html\");\n try {\n await access(templatePath);\n } catch {\n console.error(\n `✖ arcane: report template not found at ${templatePath} -- this arcane-cli build is missing dist/assets/report/. Reinstall the package (or run \\`npm run build\\` in a source checkout).`,\n );\n process.exitCode = 1;\n return;\n }\n\n if (options.refresh) {\n console.log(\n \"--refresh: external-data snapshotting (gh PR titles/merge dates) is not implemented yet; regenerating from local sources.\",\n );\n }\n\n const theme = (options.theme ?? \"auto\") as ReportTheme;\n if (!REPORT_THEMES.includes(theme)) {\n console.error(\n `✖ arcane: --theme must be one of ${REPORT_THEMES.join(\" | \")}, got \"${options.theme}\".`,\n );\n process.exitCode = 1;\n return;\n }\n\n // Degrade visibly, not silently: the page still renders in a shallow clone,\n // but its version span and cast come from history the clone cannot see.\n if (await isShallowRepository(targetDir)) {\n console.warn(\n \"⚠ arcane: shallow clone -- the report's version span and cast are derived from git history and will be omitted. Run `git fetch --unshallow` for a complete report.\",\n );\n }\n\n let programs: ReportProgram[];\n let skipped: SkippedPlan[];\n if (options.plan) {\n const result = await programFromPlanPath(targetDir, options.plan);\n programs = result.program ? [result.program] : [];\n skipped = result.skipped ? [result.skipped] : [];\n } else {\n const discovery = await discoverPrograms(targetDir);\n programs = discovery.programs;\n skipped = discovery.skipped;\n }\n\n for (const s of skipped) console.log(`Skipped ${s.slug} -- ${s.reason}.`);\n\n if (programs.length === 0) {\n console.error(\n \"✖ arcane: no program to report on -- expected docs/plans/<slug>/PLAN.md with `- [x] **ID — Title.**` epic bullets, or pass --plan <path>.\",\n );\n process.exitCode = 1;\n return;\n }\n\n const result = await runReportGeneration({\n rootDir: targetDir,\n templatePath,\n mode: \"write\",\n programs,\n outDir: options.out,\n theme,\n });\n\n for (const u of result.unwritten) {\n console.warn(\n `⚠ arcane: ${u.slug} has ${u.ids.length} epic(s) with no \\`**Report:**\\` line in PLAN.md -- they render as \"unwritten\": ${u.ids.join(\", \")}.`,\n );\n }\n\n for (const slug of result.themeSkipped) {\n console.warn(\n `⚠ arcane: ${slug} -- --theme ${theme} requested, but this template has no theme toggle to set (older vendored template?); left unchanged.`,\n );\n }\n\n if (result.repaired.length === 0) {\n console.log(`Show report: ${programs.length} program(s) already up to date.`);\n return;\n }\n console.log(`Show report: wrote ${result.repaired.length} file(s).`);\n for (const path of result.repaired) console.log(` ${path}`);\n}\n","/**\n * Show Report generation core (SR-02/SR-03): program discovery, the\n * deterministic compile date, JSON + HTML generation, and the check/write\n * parity loop. Shared by scripts/report.ts (this repository's --check/--fix\n * gate, template from src/assets/) and src/commands/report.ts (`spell report`\n * in a consumer repository, template from the installed package's\n * dist/assets/). Lives in src/ because scripts/ is not bundled into the\n * published CLI.\n */\n\nimport { mkdir, readdir, readFile, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, isAbsolute, join, relative, resolve } from \"node:path\";\nimport { buildShowReportModel } from \"./model.js\";\nimport { renderShowReport } from \"./render.js\";\nimport { applyReportTheme, type ReportTheme } from \"./theme.js\";\nimport { parseEpics, parseFrontmatter } from \"./plan-parser.js\";\nimport type { ShowReport } from \"./types.js\";\n\n/**\n * Recorded in provenance.templateVersion. \"v0-interim\" is the hand-CSS template\n * SR-02 ships; SR-06 replaces it with the arcane-ui version the compiled\n * template was built from.\n */\nexport const TEMPLATE_VERSION = \"v0-interim\";\n\nexport const NO_EPICS_REASON = \"no parseable `- [x] **ID — Title.**` epic bullets\";\n\nexport interface ReportProgram {\n slug: string;\n planRelPath: string;\n queueRelPath: string;\n ledgerProgramName: string;\n}\n\nexport interface SkippedPlan {\n slug: string;\n reason: string;\n}\n\nexport interface ReportDiscovery {\n programs: ReportProgram[];\n /** Plans found but skipped, with the reason -- reported, never silent. */\n skipped: SkippedPlan[];\n}\n\n/** `\"Lessons Hardening — Mechanical Enforcement ...\"` -> `\"Lessons Hardening\"`, the form ledger headings use. */\nexport function programNameFromTitle(title: string): string {\n return title.split(\" — \")[0]!.trim();\n}\n\n/**\n * Deterministic compile date: the most recent date the plan itself records.\n * Wall-clock would make --check fail on every run. Limitation, disclosed: for\n * a still-active program this is its activation/creation date, so the\n * ADRs-accepted window (model.ts) ends there until the plan gains a\n * `completed:` date.\n */\nexport function deterministicCompiledAt(frontmatter: {\n completed?: string;\n activated?: string;\n created?: string;\n}): string {\n return frontmatter.completed ?? frontmatter.activated ?? frontmatter.created ?? \"unknown\";\n}\n\nfunction toPosix(path: string): string {\n return path.replace(/\\\\/g, \"/\");\n}\n\nasync function programFromPlan(\n rootDir: string,\n slug: string,\n planRelPath: string,\n): Promise<{ program?: ReportProgram; skipped?: SkippedPlan }> {\n let planContent: string;\n try {\n planContent = await readFile(join(rootDir, planRelPath), \"utf8\");\n } catch {\n return { skipped: { slug, reason: `no PLAN.md at ${planRelPath}` } };\n }\n if (parseEpics(planContent).length === 0) {\n // A plan whose epics live in a table (like show-report's own) parses to\n // zero rows; an empty report for it would be noise, not information.\n return { skipped: { slug, reason: NO_EPICS_REASON } };\n }\n const frontmatter = parseFrontmatter(planContent);\n return {\n program: {\n slug,\n planRelPath,\n queueRelPath: toPosix(join(dirname(planRelPath), \"OPERATOR-QUEUE.md\")),\n ledgerProgramName: programNameFromTitle(frontmatter.title ?? slug),\n },\n };\n}\n\n/** Every `docs/plans/<slug>/PLAN.md` under `rootDir`, sorted by slug. */\nexport async function discoverPrograms(rootDir: string): Promise<ReportDiscovery> {\n const plansDir = join(rootDir, \"docs\", \"plans\");\n let entries;\n try {\n entries = (await readdir(plansDir, { withFileTypes: true })).filter((e) => e.isDirectory());\n } catch (error: unknown) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return { programs: [], skipped: [] };\n throw error;\n }\n\n const programs: ReportProgram[] = [];\n const skipped: SkippedPlan[] = [];\n for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n const result = await programFromPlan(rootDir, entry.name, `docs/plans/${entry.name}/PLAN.md`);\n if (result.program) programs.push(result.program);\n if (result.skipped) skipped.push(result.skipped);\n }\n return { programs, skipped };\n}\n\n/**\n * One program from an explicit PLAN.md path (absolute, or relative to\n * `rootDir`). The slug is the plan's parent directory name, matching the\n * `docs/plans/<slug>/` convention without requiring it.\n */\nexport async function programFromPlanPath(\n rootDir: string,\n planPath: string,\n): Promise<{ program?: ReportProgram; skipped?: SkippedPlan }> {\n const planAbs = isAbsolute(planPath) ? planPath : resolve(rootDir, planPath);\n const planRelPath = toPosix(relative(rootDir, planAbs));\n return programFromPlan(rootDir, basename(dirname(planAbs)), planRelPath);\n}\n\nfunction normalizeLineEndings(content: string): string {\n return content.replace(/\\r\\n?/g, \"\\n\");\n}\n\nasync function readOptionalFile(path: string): Promise<string | null> {\n try {\n return await readFile(path, \"utf8\");\n } catch (error: unknown) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n }\n}\n\nexport interface GeneratedReport {\n json: string;\n html: string;\n /** False when a non-\"auto\" `theme` was requested but the template has no toggle to set. */\n themeApplied: boolean;\n}\n\nexport async function generateReport(\n rootDir: string,\n program: ReportProgram,\n templateHtml: string,\n theme: ReportTheme = \"auto\",\n): Promise<GeneratedReport> {\n const planContent = await readFile(join(rootDir, program.planRelPath), \"utf8\");\n const frontmatter = parseFrontmatter(planContent);\n const model = await buildShowReportModel({\n rootDir,\n slug: program.slug,\n planRelPath: program.planRelPath,\n queueRelPath: program.queueRelPath,\n ledgerProgramName: program.ledgerProgramName,\n compiledAt: deterministicCompiledAt(frontmatter),\n templateVersion: TEMPLATE_VERSION,\n });\n const themed = applyReportTheme(renderShowReport(model, templateHtml), theme);\n return {\n json: `${JSON.stringify(model, null, 2)}\\n`,\n html: themed.html,\n themeApplied: themed.applied,\n };\n}\n\nexport type GenerationMode = \"check\" | \"write\";\n\nexport interface ReportGenerationOptions {\n rootDir: string;\n /** Absolute path to show-report.template.html. */\n templatePath: string;\n mode: GenerationMode;\n programs: ReportProgram[];\n /** Override the output directory (default: next to each program's PLAN.md). Absolute, or relative to `rootDir`. */\n outDir?: string;\n /** Force the toggle's initial state (SR-UP06). Default \"auto\" -- byte-for-byte unchanged from today, so every existing golden stays valid without this. */\n theme?: ReportTheme;\n}\n\nexport interface UnwrittenRow {\n slug: string;\n /** Epic ids whose `**Report:**` line is missing, so the report renders them as \"unwritten\". */\n ids: string[];\n}\n\nexport interface ReportGenerationResult {\n /** Outputs that do not match a regeneration (line-ending-normalized), as rootDir-relative paths. */\n drifted: string[];\n /** Outputs written in `write` mode, as rootDir-relative paths. */\n repaired: string[];\n /**\n * Epics rendering as `unwritten` (SR-04). Advisory, never a failure: the\n * capture point (`## For the record` at PR time) only applies going forward,\n * and a report is more honest showing a visible gap than blocking on one.\n */\n unwritten: UnwrittenRow[];\n /** Program slugs where a non-\"auto\" `theme` was requested but had no toggle to apply to (SR-UP06). */\n themeSkipped: string[];\n}\n\n/**\n * Regenerates every program's show-report.{json,html} in memory and compares\n * against what is on disk; `write` mode also writes the differing files.\n * Mirrors scripts/spell-catalog.ts's parity gate (ARC-012) so a committed\n * generated artifact can never silently drift from its sources.\n */\nexport async function runReportGeneration(options: ReportGenerationOptions): Promise<ReportGenerationResult> {\n const templateHtml = await readFile(options.templatePath, \"utf8\");\n const theme = options.theme ?? \"auto\";\n const drifted: string[] = [];\n const repaired: string[] = [];\n const unwritten: UnwrittenRow[] = [];\n const themeSkipped: string[] = [];\n\n for (const program of options.programs) {\n const generated = await generateReport(options.rootDir, program, templateHtml, theme);\n if (!generated.themeApplied) themeSkipped.push(program.slug);\n const missing = (JSON.parse(generated.json) as ShowReport).sections\n .flatMap((section) => section.rows)\n .filter((row) => row.descriptionState === \"unwritten\")\n .map((row) => row.id);\n if (missing.length > 0) unwritten.push({ slug: program.slug, ids: missing });\n const outAbs = options.outDir\n ? isAbsolute(options.outDir)\n ? options.outDir\n : resolve(options.rootDir, options.outDir)\n : resolve(options.rootDir, dirname(program.planRelPath));\n const outRel = toPosix(relative(options.rootDir, outAbs));\n const outputs: [string, string][] = [\n [`${outRel}/show-report.json`, generated.json],\n [`${outRel}/show-report.html`, generated.html],\n ];\n for (const [relPath, expected] of outputs) {\n const actual = await readOptionalFile(join(options.rootDir, relPath));\n if (actual === null || normalizeLineEndings(actual) !== normalizeLineEndings(expected)) {\n drifted.push(`${relPath} does not match a regeneration from its sources.`);\n if (options.mode === \"write\") {\n await mkdir(outAbs, { recursive: true });\n await writeFile(join(options.rootDir, relPath), expected, \"utf8\");\n repaired.push(relPath);\n }\n }\n }\n }\n\n return { drifted, repaired, unwritten, themeSkipped };\n}\n","/**\n * Builds a ShowReport (schema v2) from a program's real sources -- PLAN.md,\n * OPERATOR-QUEUE.md, docs/verification-ledger.md, and git history (SR-01).\n *\n * Deliberately does not populate `close` (DoD/drift verdicts): both existing\n * programs' close notes are free-form prose with no stable marker to extract\n * a verdict from without risking a misreport on exactly the field a reader\n * would trust most. Omitted rather than guessed, the same discipline the\n * schema's own `descriptionState: \"unwritten\"` convention already applies to\n * a missing `**Report:**` line -- absence stays visible instead of being\n * papered over. A future epic can add real close-note parsing once both\n * programs' close epics agree on a stable marker to key off.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type {\n ShowReport,\n ShowReportCorrections,\n ShowReportNeedsYou,\n ShowReportRow,\n ShowReportSection,\n ShowReportStat,\n ReportCategory,\n} from \"./types.js\";\nimport {\n parseFrontmatter,\n parseEpics,\n parseParkedSection,\n computeCoverageOutcome,\n type ParsedEpic,\n} from \"./plan-parser.js\";\nimport { parseOperatorQueue, type ParsedQueueEntry } from \"./queue-parser.js\";\nimport { parseVerificationLedgerSection, type ParsedLedgerRow } from \"./ledger-parser.js\";\nimport { parseAdrs, countAcceptedAdrsInWindow } from \"./decisions-parser.js\";\nimport { getVersionAtRef, getCloseCommit, getCast } from \"./sources.js\";\n\nexport interface BuildShowReportModelOptions {\n rootDir: string;\n slug: string;\n planRelPath: string;\n queueRelPath: string;\n ledgerProgramName: string;\n compiledAt: string;\n templateVersion: string;\n}\n\nasync function readOptional(path: string): Promise<string | null> {\n try {\n return await readFile(path, \"utf8\");\n } catch {\n return null;\n }\n}\n\n/** `baseline: b0992c1 (main)` -> `b0992c1`. */\nfunction extractSha(baselineField: string): string {\n return baselineField.trim().split(/\\s+/)[0]!;\n}\n\nfunction epicToRow(epic: ParsedEpic): ShowReportRow {\n const category: ReportCategory = (epic.report?.category as ReportCategory | undefined) ?? \"process\";\n const row: ShowReportRow = {\n id: epic.id,\n title: epic.report?.titleOverride ?? epic.title,\n description: epic.report?.description ?? null,\n descriptionState: epic.report ? \"authored\" : \"unwritten\",\n category,\n // Last-cited PR link is closest, in reading order, to the epic's actual\n // completion note (earlier links in a long Done note are often an ADR's\n // own PR or an intermediate fix, not the epic's own shipping PR).\n href: epic.prLinks.length > 0 ? epic.prLinks[epic.prLinks.length - 1] : undefined,\n refs: epic.prLinks,\n };\n return row;\n}\n\nfunction buildSections(epics: ParsedEpic[]): ShowReportSection[] {\n const waveOrder: string[] = [];\n const byWave = new Map<string, ParsedEpic[]>();\n for (const epic of epics) {\n const wave = epic.wave ?? \"Epics\";\n if (!byWave.has(wave)) {\n byWave.set(wave, []);\n waveOrder.push(wave);\n }\n byWave.get(wave)!.push(epic);\n }\n return waveOrder.map((wave, i) => ({\n id: `wave-${i + 1}`,\n title: wave,\n rows: byWave.get(wave)!.map(epicToRow),\n }));\n}\n\nfunction buildStats(\n epics: ParsedEpic[],\n versionSpan: { from: string; to: string } | undefined,\n adrsAccepted: number | null,\n): ShowReportStat[] {\n const done = epics.filter((e) => e.done).length;\n const allPrs = new Set(epics.flatMap((e) => e.prLinks));\n const stats: ShowReportStat[] = [\n { id: \"epics\", value: `${done}/${epics.length}`, label: \"epics shipped\", derived: true },\n { id: \"prs\", value: allPrs.size, label: \"pull requests\", derived: true },\n ];\n if (versionSpan) {\n stats.push({\n id: \"version-span\",\n value: `${versionSpan.from} → ${versionSpan.to}`,\n label: \"version span\",\n derived: true,\n });\n }\n if (adrsAccepted !== null) {\n stats.push({ id: \"adrs\", value: adrsAccepted, label: \"ADRs accepted\", derived: true });\n }\n return stats;\n}\n\nfunction buildNeedsYou(queueEntries: ParsedQueueEntry[]): ShowReportNeedsYou[] {\n return queueEntries\n .filter((e) => !e.done)\n .map((e) => {\n const entry: ShowReportNeedsYou = { id: e.id, title: e.title, reason: e.why ?? e.title };\n if (e.href) entry.href = e.href;\n return entry;\n });\n}\n\n/**\n * Shortens a ledger claim to title length at a word boundary. Cutting at a\n * fixed character offset split words mid-token on the live corpus (\"...own\n * 'Related' line, cor...\"), which reads as a rendering fault rather than an\n * abridgement. Falls back to a hard cut only when the first word alone is\n * already over the limit.\n */\nfunction truncateAtWord(text: string, limit: number): string {\n if (text.length <= limit) return text;\n const window = text.slice(0, limit - 1);\n const lastSpace = window.lastIndexOf(\" \");\n const head = lastSpace > 0 ? window.slice(0, lastSpace) : window;\n return `${head.replace(/[\\s,;:.—-]+$/, \"\")}…`;\n}\n\nfunction buildCorrections(\n ledgerRows: ParsedLedgerRow[],\n programName: string,\n): ShowReportCorrections | undefined {\n if (ledgerRows.length === 0) return undefined;\n const corrected = ledgerRows.filter((r) => r.result === \"corrected\");\n const unverifiable = ledgerRows.filter((r) => r.result === \"unverifiable\").length;\n const highlights: ShowReportRow[] = corrected.slice(0, 3).map((row, i) => ({\n id: `correction-${i + 1}`,\n title: truncateAtWord(row.claim, 80),\n description: row.correction,\n descriptionState: row.correction ? \"authored\" : \"unwritten\",\n category: \"fix\",\n refs: [],\n }));\n return {\n checked: ledgerRows.length,\n corrected: corrected.length,\n unverifiable,\n scopeNote: `${programName} corrections logged in docs/verification-ledger.md`,\n highlights,\n };\n}\n\nexport async function buildShowReportModel(options: BuildShowReportModelOptions): Promise<ShowReport> {\n const planAbsPath = join(options.rootDir, options.planRelPath);\n const queueAbsPath = join(options.rootDir, options.queueRelPath);\n const ledgerRelPath = \"docs/verification-ledger.md\";\n const ledgerAbsPath = join(options.rootDir, ledgerRelPath);\n const decisionsAbsPath = join(options.rootDir, \"DECISIONS.md\");\n\n const [planContent, queueContent, ledgerContent, decisionsContent] = await Promise.all([\n readFile(planAbsPath, \"utf8\"),\n readOptional(queueAbsPath),\n readOptional(ledgerAbsPath),\n readOptional(decisionsAbsPath),\n ]);\n\n const frontmatter = parseFrontmatter(planContent);\n const epics = parseEpics(planContent);\n const parked = parseParkedSection(planContent);\n const outcome = computeCoverageOutcome(planContent);\n const queueEntries = queueContent ? parseOperatorQueue(queueContent) : [];\n const ledgerRows = ledgerContent\n ? parseVerificationLedgerSection(ledgerContent, options.ledgerProgramName)\n : [];\n\n // \"ADRs accepted\" counts DECISIONS.md entries whose Date falls within this\n // program's own [created, completed] window (completed defaults to \"now\"\n // for a still-active program) and whose current Status is Accepted -- a\n // program with no `created` date at all has no window to count against.\n const adrsAccepted =\n decisionsContent && frontmatter.created\n ? countAcceptedAdrsInWindow(\n parseAdrs(decisionsContent),\n frontmatter.created,\n frontmatter.completed ?? options.compiledAt.slice(0, 10),\n )\n : null;\n\n let versionSpan: { from: string; to: string } | undefined;\n const cast = new Map<string, number>();\n if (frontmatter.baseline) {\n const baselineSha = extractSha(frontmatter.baseline);\n // A finished program's close is the commit `main` stood at when its own\n // `completed:` day ended (any path, by landing date) -- see getCloseCommit\n // for why neither \"last commit touching PLAN.md\" nor author dates hold up.\n // An in-progress program has no bound -- \"as of now\" -- except that its\n // own committed report must not count as \"something happened\": the\n // generated outputs next to the plan are excluded from the search.\n const reportDir = options.planRelPath.split(\"/\").slice(0, -1).join(\"/\");\n const generatedOutputs = [\"show-report.json\", \"show-report.html\"].map((f) =>\n reportDir ? `${reportDir}/${f}` : f,\n );\n const closeSha = await getCloseCommit(options.rootDir, frontmatter.completed, generatedOutputs);\n if (closeSha) {\n const [from, to, castMap] = await Promise.all([\n getVersionAtRef(options.rootDir, baselineSha),\n getVersionAtRef(options.rootDir, closeSha),\n // Regeneration-only commits are never part of the cast, for a closed\n // program as much as an active one.\n getCast(options.rootDir, baselineSha, closeSha, generatedOutputs),\n ]);\n if (from && to) versionSpan = { from, to };\n for (const [name, count] of castMap) cast.set(name, count);\n }\n }\n\n const done = epics.filter((e) => e.done).length;\n const title = frontmatter.title ?? options.slug;\n const corrections = buildCorrections(ledgerRows, options.ledgerProgramName);\n\n return {\n schemaVersion: 2,\n program: {\n id: options.slug,\n slug: options.slug,\n title,\n status: frontmatter.status ?? \"unknown\",\n baseline: frontmatter.baseline ?? \"unknown\",\n started: frontmatter.created ?? \"unknown\",\n ...(frontmatter.completed ? { completed: frontmatter.completed } : {}),\n ...(versionSpan ? { versionSpan } : {}),\n },\n masthead: {\n eyebrow: \"Show Report\",\n title,\n dek: `${frontmatter.status ?? \"unknown\"} — ${done} of ${epics.length} epics shipped`,\n },\n stats: buildStats(epics, versionSpan, adrsAccepted),\n ...(outcome ? { outcome } : {}),\n needsYou: buildNeedsYou(queueEntries),\n sections: buildSections(epics),\n ...(corrections ? { corrections } : {}),\n parked,\n cast: [...cast.entries()].map(([name, commits]) => ({ name, commits, source: \"commit-trailer\" as const })),\n provenance: {\n sources: [options.planRelPath, options.queueRelPath, ledgerRelPath],\n compiledAt: options.compiledAt,\n templateVersion: options.templateVersion,\n },\n };\n}\n","/**\n * Parses a program PLAN.md (Become Current / Lessons Hardening shape, and any\n * future program that follows the same conventions) into structured data for\n * show-report's model builder (SR-01).\n *\n * Deliberately does NOT attempt to extract a description from an epic's free\n * -form \"Done:\" prose -- that prose is written for an engineer re-verifying\n * the work, not for a report reader. The data contract (PLAN.md's own\n * \"Authored, once, at the source\" section) instead requires one short,\n * reader-facing `**Report:**` line per epic, authored directly in PLAN.md.\n * This parser only reads that line, plus the mechanically reliable bits\n * (epic ID/title/done-state, PR links already present in the epic's text).\n */\n\nexport interface PlanFrontmatter {\n title?: string;\n status?: string;\n created?: string;\n completed?: string;\n baseline?: string;\n owner?: string;\n executor?: string;\n}\n\nexport interface ParsedEpicReport {\n description: string;\n category: string;\n titleOverride?: string;\n}\n\nexport interface ParsedEpic {\n id: string;\n title: string;\n done: boolean;\n wave: string | null;\n report: ParsedEpicReport | null;\n prLinks: string[];\n block: string;\n}\n\nexport interface ParsedParkedItem {\n title: string;\n reason: string;\n}\n\n/** Reads `key: value` pairs out of a `---\\n...\\n---` frontmatter block. Values are left as raw strings. */\nexport function parseFrontmatter(content: string): PlanFrontmatter {\n const match = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(content);\n if (!match) return {};\n const fields: Record<string, string> = {};\n for (const line of match[1]!.split(/\\r?\\n/)) {\n const kv = /^([a-zA-Z][\\w-]*):\\s*(.*)$/.exec(line);\n if (kv) fields[kv[1]!] = kv[2]!.trim();\n }\n return fields;\n}\n\n// Only the OPEN of an epic bullet -- \"- [x] **BC-01 — \" -- is matched at the\n// line-start position. The title is extracted separately (see below) because\n// it is not always confined to that one line: at least one real epic (LH-11)\n// wraps its bold title across a line break before the closing `.**`, which a\n// single-line regex requiring both ends on one line silently fails to match\n// at all -- not a parse error, just an epic that never gets recognized.\nconst EPIC_START = /^- \\[([ x])\\] \\*\\*([A-Z]+-\\d+) — /gm;\nconst EPIC_TITLE = /^- \\[[ x]\\] \\*\\*[A-Z]+-\\d+ — ([\\s\\S]+?)\\.\\*\\*/;\nconst HEADING_LINE = /^(#{2,3}) (.+)$/gm;\n// `category:` is the last field this parser reads. A trailing `· glyph: <emoji>`\n// is no longer part of the convention (ARC-043) but appears on every `**Report:**`\n// line authored before it was dropped, so the pattern deliberately does not\n// anchor at end-of-line: those lines keep parsing, and the emoji is ignored\n// rather than rendered. Category alone now selects the row's icon.\nconst REPORT_LINE = /\\*\\*Report:\\*\\*\\s*([\\s\\S]*?)\\s*·\\s*category:\\s*([a-z]+)/;\nconst TITLE_OVERRIDE_LINE = /^\\s*title:\\s*(.+)$/m;\nconst PR_LINK = /\\[PR #(\\d+)\\]\\((https:\\/\\/github\\.com\\/[^)\\s]+\\/pull\\/\\d+)\\)/g;\n\ninterface Marker {\n index: number;\n type: \"epic\" | \"h2\" | \"h3\";\n id?: string;\n done?: boolean;\n waveTitle?: string;\n}\n\n/**\n * Splits PLAN.md into top-level epic blocks. A new epic always starts a line\n * with `- [ ]`/`- [x]` followed by `**<PREFIX>-<N> — `; nested detail (Done:\n * sub-bullets, numbered lists, bold lead-ins like \"BC-30 Batch B\") never\n * matches that shape, so it safely stays inside the current block. Position\n * -based (not line-by-line): a title, a Wave heading, or a `## ` section\n * boundary can in principle fall anywhere relative to line breaks, and only\n * comparing character offsets handles that uniformly.\n */\nexport function parseEpics(content: string): ParsedEpic[] {\n const markers: Marker[] = [];\n\n EPIC_START.lastIndex = 0;\n let epicMatch: RegExpExecArray | null;\n while ((epicMatch = EPIC_START.exec(content)) !== null) {\n markers.push({ index: epicMatch.index, type: \"epic\", id: epicMatch[2]!, done: epicMatch[1] === \"x\" });\n }\n\n HEADING_LINE.lastIndex = 0;\n let headingMatch: RegExpExecArray | null;\n while ((headingMatch = HEADING_LINE.exec(content)) !== null) {\n if (headingMatch[1] === \"##\") {\n markers.push({ index: headingMatch.index, type: \"h2\" });\n } else {\n markers.push({ index: headingMatch.index, type: \"h3\", waveTitle: headingMatch[2]!.trim() });\n }\n }\n\n markers.sort((a, b) => a.index - b.index);\n\n const epics: ParsedEpic[] = [];\n let currentWave: string | null = null;\n\n for (let i = 0; i < markers.length; i += 1) {\n const marker = markers[i]!;\n if (marker.type === \"h2\") {\n currentWave = null;\n continue;\n }\n if (marker.type === \"h3\") {\n currentWave = marker.waveTitle!;\n continue;\n }\n // marker.type === \"epic\": block runs to the next marker of any kind, or EOF.\n const blockEnd = i + 1 < markers.length ? markers[i + 1]!.index : content.length;\n const block = content.slice(marker.index, blockEnd);\n const titleMatch = EPIC_TITLE.exec(block);\n const reportMatch = REPORT_LINE.exec(block);\n const titleOverrideMatch = TITLE_OVERRIDE_LINE.exec(block);\n const prLinks = [...new Set([...block.matchAll(PR_LINK)].map((m) => m[2]!))];\n\n epics.push({\n id: marker.id!,\n // A title that still fails to match (e.g. a malformed bullet with no\n // closing `.**` at all) falls back to the raw id rather than throwing --\n // an epic missing from the report is far more visible, and therefore\n // more likely to get fixed, than a crashed report generator.\n title: titleMatch ? titleMatch[1]!.replace(/\\s+/g, \" \").trim() : marker.id!,\n done: marker.done!,\n wave: currentWave,\n report: reportMatch\n ? {\n description: reportMatch[1]!.replace(/\\s+/g, \" \").trim(),\n category: reportMatch[2]!,\n titleOverride: titleOverrideMatch ? titleOverrideMatch[1]!.trim() : undefined,\n }\n : null,\n prLinks,\n block,\n });\n }\n\n return epics;\n}\n\n/**\n * Extracts the body text of a `## <headingPrefix>...` section, up to (but\n * not including) the next `## ` heading or the end of the document.\n *\n * Deliberately line-array based rather than a single lookahead regex: a\n * lookahead built from `\\r?\\n?$` under the `m` flag matches at the END OF\n * EVERY LINE (not just end-of-string, since `m` makes `$` mean \"end of any\n * line\"), so a lazy `[\\s\\S]*?` stops after the very first blank line inside\n * the section instead of running to the next `## ` heading. Splitting into\n * lines and searching by index sidesteps that footgun entirely.\n */\nfunction extractSection(content: string, headingPrefix: string): string | null {\n const lines = content.split(/\\r?\\n/);\n const startIndex = lines.findIndex((line) => line.startsWith(headingPrefix));\n if (startIndex === -1) return null;\n let endIndex = lines.length;\n for (let i = startIndex + 1; i < lines.length; i += 1) {\n if (lines[i]!.startsWith(\"## \")) {\n endIndex = i;\n break;\n }\n }\n return lines.slice(startIndex + 1, endIndex).join(\"\\n\");\n}\n\n/**\n * Parked sections come in two known shapes across the two existing programs:\n * Lessons Hardening's bullet list (`- **Title** — reason`) and Become\n * Current's three-column table (`| Item | Source | reason |`). Both are\n * handled; an unrecognized shape degrades to an empty list rather than\n * throwing, since this field is optional in the schema.\n */\nexport function parseParkedSection(content: string): ParsedParkedItem[] {\n const body = extractSection(content, \"## Parked — Needs Operator\");\n if (body === null) return [];\n\n const tableRows = [...body.matchAll(/^\\|(?!---)([^|]+)\\|([^|]+)\\|([^|]+)\\|\\s*$/gm)];\n if (tableRows.length > 1) {\n // First matched row is the header (`| Item | Source | ... |`); skip it.\n return tableRows.slice(1).map((m) => ({\n title: stripMarkdown(m[1]!.trim()),\n reason: stripMarkdown(m[3]!.trim()),\n }));\n }\n\n // Bullets are unwrapped to one string each BEFORE matching. Matching the raw\n // text line-by-line under `m` lost data two ways on the live corpus, both\n // silently: a reason that wrapped to a second line was cut at the wrap\n // (\"fully solved (git-conventions.md → Content-Verified\", losing \"Branch\n // Deletion)\"), and an item whose `—` fell on the second line never matched at\n // all, dropping it from the report entirely (Lessons Hardening's\n // \"Mechanizing spell-check-drift's judgment-based detectors\" -- 8 of 9 items\n // rendered, with nothing to indicate one was missing).\n return unwrapBullets(body).flatMap((bullet) => {\n const match = /^\\*\\*(.+?)\\*\\*(.*?)\\s*—\\s*(.+)$/.exec(bullet);\n if (!match) return [];\n // Text between the bold lead-in and the `—` belongs to the title (LH's\n // \"…detectors **beyond** LH-07/LH-08's two mechanical inputs\"), except a\n // parenthetical, which is a source note rather than part of the name.\n const tail = match[2]!.replace(/\\([^)]*\\)/g, \"\").replace(/\\s+/g, \" \").trim();\n const title = tail ? `${match[1]!.trim()} ${tail}` : match[1]!.trim();\n return [{ title: stripMarkdown(title), reason: stripMarkdown(match[3]!.trim()) }];\n });\n}\n\n/**\n * Collapses a markdown bullet list into one string per bullet, joining each\n * bullet's wrapped continuation lines. A bullet ends at the next `- ` at\n * column 0, at a blank line, or at any other unindented line.\n */\nfunction unwrapBullets(body: string): string[] {\n const bullets: string[] = [];\n let current: string[] | null = null;\n const flush = (): void => {\n if (current) bullets.push(current.join(\" \").replace(/\\s+/g, \" \").trim());\n current = null;\n };\n for (const line of body.split(/\\r?\\n/)) {\n if (/^- /.test(line)) {\n flush();\n current = [line.slice(2).trim()];\n } else if (current !== null) {\n if (/^\\s+\\S/.test(line)) current.push(line.trim());\n else flush();\n }\n }\n flush();\n return bullets;\n}\n\n/**\n * Generic outcome summary from a \"## Coverage Map\" section: counts data rows\n * (skipping the header/separator rows every markdown table has) and how many\n * cells say \"Parked\", producing the same shape as the schema's own example\n * (\"12 of 12 patterns dispositioned — 9 mechanized, 3 parked\") regardless of\n * which program's own column layout produced them.\n */\nexport function computeCoverageOutcome(content: string): string | undefined {\n const body = extractSection(content, \"## Coverage Map\");\n if (body === null) return undefined;\n const rows = [...body.matchAll(/^\\|(.+)\\|\\s*$/gm)].filter((m) => !/^-+\\s*\\|/.test(m[1]!.trim()));\n if (rows.length < 2) return undefined;\n const dataRows = rows.slice(1); // first row is the header\n // Lessons Hardening's own Coverage Map says \"**Park**\"; Become Current's\n // says \"**Parked**\" -- same disposition, two spellings across the two\n // existing programs (an instance of the exact P-something inconsistency\n // this program's own corrections inventory is full of).\n const parkedCount = dataRows.filter((m) => /\\*\\*Park(?:ed)?\\*\\*/i.test(m[1]!)).length;\n const total = dataRows.length;\n const dispositioned = total - parkedCount;\n return `${total} of ${total} items dispositioned — ${dispositioned} routed to an epic, ${parkedCount} parked`;\n}\n\nfunction stripMarkdown(text: string): string {\n return text\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n .replace(/\\*\\*/g, \"\")\n .replace(/`/g, \"\")\n .trim();\n}\n","/**\n * Parses an OPERATOR-QUEUE.md (Become Current / Lessons Hardening / Show\n * Report shape) into structured entries for show-report's `needsYou` block\n * (SR-01). The loop appends `## Q-NNN — Title` sections, each ending in a\n * `- **Status:** [ ]`/`[x]` line -- that checkbox is the only mechanically\n * reliable \"is this still open\" signal; everything else in an entry is free\n * -form prose written for the operator, not for parsing.\n */\n\nconst LINK = /\\[[^\\]]+\\]\\((https?:\\/\\/[^)\\s]+)\\)/;\n\nexport interface ParsedQueueEntry {\n id: string;\n title: string;\n done: boolean;\n why: string | null;\n href?: string;\n}\n\nexport function parseOperatorQueue(content: string): ParsedQueueEntry[] {\n const sections = content.split(/^## /m).slice(1); // drop the doc's own leading title/preamble\n const entries: ParsedQueueEntry[] = [];\n\n for (const section of sections) {\n const headingMatch = /^(Q-\\d+)\\s*—\\s*(.+?)\\r?\\n/.exec(section);\n if (!headingMatch) continue; // not a Q-NNN section (shouldn't occur after the split, but stay defensive)\n const id = headingMatch[1]!;\n const title = headingMatch[2]!.trim();\n\n const statusMatch = /^-\\s*\\*\\*Status:\\*\\*\\s*\\[([ x])\\]/m.exec(section);\n const done = statusMatch ? statusMatch[1] === \"x\" : false;\n\n const whyMatch = /^-\\s*\\*\\*Why:\\*\\*\\s*([\\s\\S]*?)(?=\\r?\\n-\\s*\\*\\*|\\r?\\n\\r?\\n|$)/m.exec(section);\n const why = whyMatch ? whyMatch[1]!.replace(/\\s+/g, \" \").trim() : null;\n\n const linkMatch = LINK.exec(section);\n entries.push({ id, title, done, why, href: linkMatch ? linkMatch[1] : undefined });\n }\n\n return entries;\n}\n","/**\n * Parses a program's own dated section out of docs/verification-ledger.md\n * (SR-01). Sections are matched by a heading that names the program (e.g.\n * \"## 2026-09-02 — Lessons Hardening corrections (LH-02 → LH-12)\") -- a\n * program can have more than one dated section (Become Current's ledger\n * entry above is one example among what may become several over a program's\n * life), so this returns every row across all matching sections combined.\n */\n\nexport type LedgerResult = \"confirmed\" | \"corrected\" | \"unverifiable\";\n\nexport interface ParsedLedgerRow {\n claim: string;\n result: LedgerResult;\n correction: string | null;\n}\n\n/** `programNameWords` should be how the program is named in ledger headings, e.g. \"Lessons Hardening\". */\nexport function parseVerificationLedgerSection(content: string, programNameWords: string): ParsedLedgerRow[] {\n const sections = content.split(/^## /m).slice(1);\n const rows: ParsedLedgerRow[] = [];\n\n for (const section of sections) {\n const headingLine = section.slice(0, section.indexOf(\"\\n\"));\n if (!headingLine.toLowerCase().includes(programNameWords.toLowerCase())) continue;\n\n const tableRows = [...section.matchAll(/^\\|([^|]+)\\|([^|]+)\\|([^|]+)\\|([^|]+)\\|\\s*$/gm)];\n for (const m of tableRows.slice(1)) {\n // first match is the header row\n const claimCell = m[1]!.trim();\n const resultCell = m[3]!.trim().toLowerCase();\n const correctionCell = m[4]!.trim();\n if (claimCell === \"---\" || claimCell.startsWith(\"---\")) continue;\n if (resultCell !== \"confirmed\" && resultCell !== \"corrected\" && resultCell !== \"unverifiable\") continue;\n rows.push({\n claim: stripMarkdown(claimCell),\n result: resultCell,\n correction: correctionCell === \"—\" ? null : stripMarkdown(correctionCell),\n });\n }\n }\n\n return rows;\n}\n\nfunction stripMarkdown(text: string): string {\n return text\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n .replace(/\\*\\*/g, \"\")\n .replace(/`/g, \"\")\n .trim();\n}\n","/**\n * Parses DECISIONS.md for ADRs (SR-01), specifically to count how many\n * reached `Accepted` status within a program's active window -- the exact\n * stat this program's own research flagged as easy to get wrong by hand:\n * Become Current's hand-built ledger said \"4 ADRs accepted\" where a\n * mechanical recount found 5 (ARC-029/037/038/039/040). `**Status:**` lines\n * sometimes carry a parenthetical after the word itself (e.g. \"Accepted\n * (2026-09-01, operator accept call ...)\"); only the leading status word is\n * matched, so that detail doesn't need parsing.\n */\n\nexport interface ParsedAdr {\n id: string;\n date: string | null;\n status: string | null;\n}\n\nexport function parseAdrs(decisionsContent: string): ParsedAdr[] {\n const headingPattern = /^## ARC-(\\d{3})\\b/gm;\n const positions: { id: string; index: number }[] = [];\n let match: RegExpExecArray | null;\n while ((match = headingPattern.exec(decisionsContent)) !== null) {\n positions.push({ id: `ARC-${match[1]!}`, index: match.index });\n }\n\n return positions.map(({ id, index }, i) => {\n const end = i + 1 < positions.length ? positions[i + 1]!.index : decisionsContent.length;\n const section = decisionsContent.slice(index, end);\n const dateMatch = /^\\*\\*Date:\\*\\*\\s*(\\d{4}-\\d{2}-\\d{2})/m.exec(section);\n const statusMatch = /^\\*\\*Status:\\*\\*\\s*(\\S+)/m.exec(section);\n return { id, date: dateMatch ? dateMatch[1]! : null, status: statusMatch ? statusMatch[1]! : null };\n });\n}\n\n/** Counts ADRs whose `**Date:**` falls within `[fromDate, toDate]` (inclusive, ISO strings) and whose current Status is Accepted. */\nexport function countAcceptedAdrsInWindow(adrs: ParsedAdr[], fromDate: string, toDate: string): number {\n return adrs.filter(\n (adr) => adr.status === \"Accepted\" && adr.date !== null && adr.date >= fromDate && adr.date <= toDate,\n ).length;\n}\n","/**\n * Git-derived data for show-report (SR-01): package.json version at a given\n * ref, and commit-trailer-based cast attribution. Deliberately network-free\n * -- everything here reads from the local git history already on disk, so\n * `--check` (SR-02) never needs a network call. An optional `--refresh` path\n * that snapshots `gh pr list` data is out of SR-01's scope; `refs`/`href` are\n * populated instead from PR links already present in each epic's own\n * `**Report:**`-adjacent text (see plan-parser.ts), which needs no network\n * either.\n */\n\nimport { runGit } from \"../git.js\";\n\nasync function runGitTextOrNull(cwd: string, args: string[]): Promise<string | null> {\n try {\n const { stdout } = await runGit(cwd, args, { commandClass: \"read\" });\n return stdout.trim();\n } catch {\n return null;\n }\n}\n\n/**\n * True for a shallow clone (`--depth`). In one, getCloseCommit /\n * getVersionAtRef / getCast cannot see the history the version span and cast\n * derive from, so they silently omit those fields -- and a parity check then\n * reports the omission as \"drift\". Callers that gate on parity must treat a\n * shallow clone as \"cannot verify\", never as drifted. Found the hard way:\n * publish.yml's default fetch-depth: 1 checkout failed the golden test that\n * ci.yml's full-history checkout had just passed, and v0.34.3 never reached\n * npm because of it.\n */\nexport async function isShallowRepository(cwd: string): Promise<boolean> {\n const out = await runGitTextOrNull(cwd, [\"rev-parse\", \"--is-shallow-repository\"]);\n return out === \"true\";\n}\n\n/** Reads `package.json`'s `version` field as it existed at `ref`, without touching the working tree. */\nexport async function getVersionAtRef(cwd: string, ref: string): Promise<string | null> {\n const content = await runGitTextOrNull(cwd, [\"show\", `${ref}:package.json`]);\n if (content === null) return null;\n try {\n const parsed: unknown = JSON.parse(content);\n const version = (parsed as { version?: unknown }).version;\n return typeof version === \"string\" ? version : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The commit `main` stood at when a program's `completed` day ended: the most\n * recent commit whose COMMITTER date falls on or before that day, ignoring the\n * program's own report files. Without a `completed` date (a program still in\n * progress) the close is the most recent commit that touched anything else --\n * \"as of now\".\n *\n * Measured, not assumed, against both finished programs: Become Current's last\n * PLAN.md-touching commit on its completed day sat at 0.33.0, while two\n * version bumps landed later that same day without touching the plan -- the\n * human record and the hand ledger both say 0.33.2, and only a date-based\n * definition reproduces it (and Lessons Hardening's 0.34.1). Anchoring to\n * PLAN.md at all was the wrong idea: a finished plan gets edited again\n * (SR-01's own backfill did it), and a program's version moves on commits that\n * never touch it.\n *\n * Committer date, not author date, because it is the LANDING date: this repo\n * rebase-merges every PR, so it is the merge time and monotonic along the log,\n * while author dates are not (a PR authored at 03:19 can land above one\n * authored at 06:46). `%cs` renders it in the commit's own recorded offset,\n * so the calendar-day comparison gives the same answer on the operator's\n * machine and on a UTC runner, where a `--until=<instant>` filter would not.\n * Author date was tried for R-CLOSE and rejected: on upstream-intake-2026-09 it\n * moved a published report (cast 55 -> 56), because a commit authored on the\n * `completed:` day landed the next day. A rebase rewrites the committer date,\n * so a PR authored on the completion day and merged after midnight can still\n * make a report differ between its branch and main (PR #305's 56-vs-55 is\n * INFERRED to be that, not reproduced); that stays open in TODO.md.\n *\n * The generated outputs (`generatedOutputs`, repository-relative) are excluded\n * from the search in both branches: a commit that touched only them, a\n * regeneration, is never the close. For an in-progress program without that,\n * committing a regenerated report moved the close onto the very commit that\n * carried the report, whose own `Agent:` trailer the cast then counted, so a\n * freshly committed report was stale the instant it landed (TODO.md, \"the\n * golden-parity gate has no notion of an in-progress program\"). For a\n * completed program the exclusion changes which commit is the close but, today,\n * nothing in the report: getCast already ignores report-only commits and a\n * report-only commit never moves the version. It does NOT help a commit that\n * regenerates the report AND edits something else (that commit is still its own\n * close and joins the cast; PR #304's `b43df06` was one) -- the standing rule\n * that a regeneration is its own report-only commit is what makes it converge.\n * Anchoring on paths rather than on a recorded SHA survives this repo's\n * rebase-merges, which rewrite every SHA on the way to main.\n */\nfunction excludeOutputs(generatedOutputs: string[]): string[] {\n return generatedOutputs.length === 0 ? [] : [\"--\", \".\", ...generatedOutputs.map((p) => `:(exclude)${p}`)];\n}\n\nexport async function getCloseCommit(\n cwd: string,\n completedDate?: string,\n generatedOutputs: string[] = [],\n): Promise<string | null> {\n if (!completedDate) {\n const args =\n generatedOutputs.length === 0\n ? [\"rev-parse\", \"HEAD\"]\n : [\"log\", \"-1\", \"--format=%H\", \"HEAD\", ...excludeOutputs(generatedOutputs)];\n const head = await runGitTextOrNull(cwd, args);\n return head === null || head === \"\" ? null : head;\n }\n const log = await runGitTextOrNull(cwd, [\"log\", \"--format=%H%x09%cs\", \"HEAD\", ...excludeOutputs(generatedOutputs)]);\n if (log === null || log === \"\") return null;\n for (const line of log.split(\"\\n\")) {\n const [sha, landed] = line.split(\"\\t\");\n if (sha && landed && landed <= completedDate) return sha;\n }\n return null;\n}\n\n/**\n * Cast list for the `fromRef..toRef` commit range: one entry per distinct\n * contributor name, counted by commits. Name resolution prefers a commit's\n * `Persona:` trailer (a roster identity) and falls back to its `Agent:`\n * trailer (a runtime/tool name) when no persona was assigned that commit;\n * a commit with neither trailer (ordinary human commits) is not counted --\n * this list is specifically the self-reported AI cast, per the schema's\n * `source: \"commit-trailer\"` / \"self-reported\" note, not full authorship.\n *\n * `generatedOutputs` (repository-relative) are the program's own report\n * files: a commit that touched nothing but them -- a regeneration -- is not\n * part of the cast at all, whatever trailer it carries. Anchoring only the\n * close (getCloseCommit) was not enough: it kept a regeneration from counting\n * itself while it was the tip, but the moment real content landed after it,\n * the earlier regeneration fell inside `fromRef..toRef` and was counted after\n * all -- caught by the regression in test/report-cli.test.ts.\n */\nexport async function getCast(\n cwd: string,\n fromRef: string,\n toRef: string,\n generatedOutputs: string[] = [],\n): Promise<Map<string, number>> {\n const ETX = \"\\x03\";\n const log = await runGitTextOrNull(cwd, [\n \"log\",\n \"--no-merges\",\n `--format=%B${ETX}`,\n `${fromRef}..${toRef}`,\n ...(generatedOutputs.length > 0\n ? [\"--\", \".\", ...generatedOutputs.map((p) => `:(exclude)${p}`)]\n : []),\n ]);\n const cast = new Map<string, number>();\n if (log === null || log === \"\") return cast;\n\n for (const body of log.split(ETX)) {\n const personaMatch = /^Persona:\\s*(.+)$/m.exec(body);\n const agentMatch = /^Agent:\\s*(.+)$/m.exec(body);\n const name = personaMatch?.[1]?.trim() || agentMatch?.[1]?.trim();\n if (!name) continue;\n cast.set(name, (cast.get(name) ?? 0) + 1);\n }\n return cast;\n}\n","/**\n * Renders a ShowReport into HTML through a Mustache template (SR-02).\n *\n * Mustache is logic-less, so anything the template cannot compute -- a\n * category's display label, whether a row has a link, the safe HTML form of\n * a description -- is precomputed here into a view model first. Every plain\n * field goes through Mustache's default `{{x}}` HTML escaping; the ONLY\n * triple-mustache (`{{{x}}}`) field the template may use is `descriptionHtml`,\n * which `inlineMarkupToHtml` produces by escaping everything and then\n * re-admitting exactly two constructs: `` `code` `` and `[text](https-url)`.\n */\n\nimport Mustache from \"mustache\";\nimport type { ReportCategory, ShowReport, ShowReportRow } from \"./types.js\";\n\nexport const CATEGORY_LABELS: Record<ReportCategory, string> = {\n spell: \"New Spell\",\n feature: \"New Feature\",\n governance: \"Governance\",\n decision: \"Decision (ADR)\",\n fix: \"Bug Fix\",\n process: \"Process\",\n docs: \"Docs\",\n platform: \"Platform\",\n};\n\nconst CATEGORY_ORDER: ReportCategory[] = [\n \"spell\",\n \"feature\",\n \"governance\",\n \"decision\",\n \"fix\",\n \"process\",\n \"docs\",\n \"platform\",\n];\n\nexport function escapeHtml(text: string): string {\n return text\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\n/**\n * Escape first, then re-admit only `code` spans and `[text](http(s)://...)`\n * links. Because `\"` and `'` are already entity-escaped by the time the link\n * regex runs, a URL cannot break out of the `href` attribute, and `<`/`>` in\n * link text cannot open a tag -- the two constructs are the whole allowlist.\n */\nexport function inlineMarkupToHtml(text: string): string {\n const escaped = escapeHtml(text);\n const withLinks = escaped.replace(\n /\\[([^\\]]+)\\]\\((https?:\\/\\/[^)\\s]+)\\)/g,\n '<a href=\"$2\">$1</a>',\n );\n return withLinks.replace(/`([^`]+)`/g, \"<code>$1</code>\");\n}\n\nexport interface ShowReportRowView extends ShowReportRow {\n categoryLabel: string;\n descriptionHtml: string | null;\n isUnwritten: boolean;\n hasHref: boolean;\n}\n\nfunction toRowView(row: ShowReportRow): ShowReportRowView {\n return {\n ...row,\n categoryLabel: CATEGORY_LABELS[row.category] ?? row.category,\n descriptionHtml: row.description === null ? null : inlineMarkupToHtml(row.description),\n isUnwritten: row.descriptionState === \"unwritten\",\n hasHref: typeof row.href === \"string\" && row.href.length > 0,\n };\n}\n\nexport function buildShowReportView(model: ShowReport): Record<string, unknown> {\n const corrections = model.corrections\n ? { ...model.corrections, highlights: model.corrections.highlights.map(toRowView) }\n : undefined;\n return {\n ...model,\n // Same reason as parkedIsSingular: the template needs a flag to write \"commit\"\n // rather than \"commits\" for a contributor with exactly one.\n cast: model.cast.map((member) => ({ ...member, isSingular: member.commits === 1 })),\n sections: model.sections.map((section) => ({\n ...section,\n hasNote: typeof section.note === \"string\" && section.note.length > 0,\n // Mustache cannot count a list, so any \"N items\" label in a template needs\n // the count precomputed here. Added for the compiled arcane-ui template,\n // which asks for a per-section row count and a parked total (SR-06).\n rowCount: section.rows.length,\n rows: section.rows.map(toRowView),\n })),\n corrections,\n hasCorrections: corrections !== undefined,\n hasParked: model.parked.length > 0,\n parkedCount: model.parked.length,\n // Mustache has no conditional on a VALUE -- only truthiness of a name -- so a\n // template cannot choose \"item\" vs \"items\" from the number itself. It needs a\n // flag, and only this side can derive one. Without it a one-item program reads\n // \"1 items\", which is what the compiled template shipped.\n parkedIsSingular: model.parked.length === 1,\n hasCast: model.cast.length > 0,\n hasOutcome: typeof model.outcome === \"string\" && model.outcome.length > 0,\n hasVersionSpan: model.program.versionSpan !== undefined,\n needsYouCount: model.needsYou.length,\n compiledAtDate: model.provenance.compiledAt.slice(0, 10),\n legend: CATEGORY_ORDER.map((key) => ({ key, label: CATEGORY_LABELS[key] })),\n sourcesJoined: model.provenance.sources.join(\", \"),\n };\n}\n\nexport function renderShowReport(model: ShowReport, templateHtml: string): string {\n // Mustache's built-in escaper also entity-encodes `/`, `=` and backticks\n // (`1/1` -> `1/1`), which is safe but needlessly ugly in the shipped\n // source. The standard five-character set above is the sufficient HTML\n // escape for text content and quoted attributes, so it is used for both\n // the inline-markup path and every `{{x}}` tag -- one escaper, one rule.\n // Passed per call rather than assigned to `Mustache.escape` so this module\n // never mutates a shared global. Mustache hands the RAW interpolated value\n // to the escaper -- a number for `{{needsYouCount}}`, `{{commits}}` or a\n // numeric stat -- so coerce to string first, as mustache's own escaper does.\n return Mustache.render(templateHtml, buildShowReportView(model), undefined, {\n escape: (value: unknown) => escapeHtml(String(value)),\n });\n}\n","/**\n * Applies a build-time theme selection to a rendered Show Report page (SR-UP06,\n * closes arcane#271) by moving the `checked` attribute in the vendored\n * template's theme toggle (arcane-ui ARCUI-021, three radios sharing\n * `name=\"arcane-report-theme\"`, ids `arcane-report-theme-<mode>`) onto the\n * requested radio.\n *\n * \"auto\" is a deliberate no-op: arcane-ui ships the template with\n * `arcane-report-theme-auto` pre-checked and no CSS override tied to it, so\n * the unmodified template already reproduces today's `prefers-color-scheme`\n * behaviour byte-for-byte (OPERATOR-QUEUE.md Q-006's requirement). This\n * never touches the html when `theme` is \"auto\" -- including the default\n * codepath used by every existing `show-report.html` golden, which must\n * stay unaffected.\n */\n\nexport type ReportTheme = \"auto\" | \"light\" | \"dark\";\n\nexport const REPORT_THEMES: readonly ReportTheme[] = [\"auto\", \"light\", \"dark\"];\n\nexport interface ApplyReportThemeResult {\n html: string;\n /** False when `theme` isn't \"auto\" but the template has no matching toggle to set (older template). */\n applied: boolean;\n}\n\nfunction radioTagPattern(id: ReportTheme): RegExp {\n return new RegExp(`<input\\\\b[^>]*\\\\bid=\"arcane-report-theme-${id}\"[^>]*?/?>`);\n}\n\nfunction withoutChecked(tag: string): string {\n return tag.replace(/\\s+checked(?:=\"[^\"]*\")?/, \"\");\n}\n\nfunction withChecked(tag: string): string {\n return /\\bchecked\\b/.test(tag) ? tag : tag.replace(/(\\/?>)$/, \" checked$1\");\n}\n\nexport function applyReportTheme(html: string, theme: ReportTheme): ApplyReportThemeResult {\n if (theme === \"auto\") return { html, applied: true };\n if (!radioTagPattern(theme).test(html)) return { html, applied: false };\n\n let result = html;\n // Move `checked` onto the target radio: strip it from every other radio in the\n // group first (arcane-ui ships \"auto\" pre-checked by default), so exactly one\n // radio ends up checked rather than leaving two marked at once.\n for (const id of REPORT_THEMES) {\n const match = radioTagPattern(id).exec(result);\n if (!match) continue;\n const original = match[0];\n const replaced = id === theme ? withChecked(original) : withoutChecked(original);\n if (replaced !== original) {\n result = result.slice(0, match.index) + replaced + result.slice(match.index + original.length);\n }\n }\n return { html: result, applied: true };\n}\n"],"mappings":"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AAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;A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","names":["createRequire","dirname","join","select","confirm","input","dirname","join","chalk","readFile","writeFile","path","manifestPath","mkdir","readdir","readFile","rm","rmdir","writeFile","path","path","readFile","readdir","rmdir","mkdir","writeFile","rm","resolve","label","mkdir","writeFile","copyFile","dirname","join","readFile","readdir","access","join","isRecord","path","parseYaml","join","readFile","join","readFile","parseYaml","access","writeFile","mkdir","readdir","join","homedir","readFile","writeFile","rm","path","path","readFile","writeFile","rm","readdir","join","homedir","mkdir","writeFile","dirname","label","r","join","mkdir","writeFile","copyFile","select","input","readdir","writeFile","join","resolve","stdout","select","input","manifestPath","join","readdir","writeFile","mkdir","readFile","writeFile","access","rm","readdir","join","resolve","dirname","resolve","dirname","join","mkdir","writeFile","join","resolve","readdir","readFile","access","rm","join","chalk","confirm","select","profileComponents","input","dirname","readFile","writeFile","join","join","readFile","writeFile","printDryRun","dirname","join","readFile","writeFile","i","j","join","join","checkbox","checkbox","join","dirname","fileHashes","readFile","writeFile","dirname","readFile","writeFile","mkdir","dirname","join","homedir","require","pkg","label","dirname","rm","dirname","join","confirm","confirm","rm","join","dirname","access","readdir","readFile","mkdir","join","dirname","readFile","readdir","join","execFile","resolve","path","readdir","readFile","join","FRONTMATTER_PATTERN","isRecord","label","join","readdir","manifestPath","access","readFile","mkdir","dirname","readFile","readdir","readFile","join","relative","readFile","readdir","join","relative","findings","readFile","basename","resolve","input","basename","resolve","input","access","join","mkdir","readdir","readFile","writeFile","basename","dirname","join","relative","resolve","readFile","join","stripMarkdown","path","readFile","join","path","readFile","join","dirname","readdir","resolve","relative","basename","normalizeLineEndings","program","mkdir","writeFile","join","access","result","path","require","createRequire","join","dirname"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/modules/copier.ts","../src/modules/manifest.ts","../src/config/profiles.ts","../src/modules/registry.ts","../src/modules/user-tier.ts","../src/modules/spell-compiler.ts","../src/modules/banner.ts","../src/modules/agents.ts","../src/config/agent-roles.ts","../src/config/agent-profiles.ts","../src/modules/naming.ts","../src/modules/agent-loader.ts","../src/modules/agent-schema.ts","../src/modules/agent-generator.ts","../src/modules/merger.ts","../src/modules/hub.ts","../src/modules/git.ts","../src/modules/ado-context.ts","../src/modules/push-safety.ts","../src/modules/secrets-scan.ts","../src/modules/component-install.ts","../src/commands/add.ts","../src/commands/update.ts","../src/modules/merge3.ts","../src/modules/registry-changes.ts","../src/modules/update-installs.ts","../src/modules/npm-registry.ts","../src/commands/status.ts","../src/modules/version-check.ts","../src/modules/diagram-generator.ts","../src/commands/uninstall.ts","../src/commands/doctor.ts","../src/config/incidents.ts","../src/modules/incident-gate.ts","../src/modules/denylist-scan.ts","../src/modules/exec.ts","../src/modules/platform-policy.ts","../src/modules/placeholders.ts","../src/commands/ward.ts","../src/modules/ward.ts","../src/commands/unblock-push.ts","../src/commands/block-push.ts","../src/modules/credential-store.ts","../src/modules/identity-config.ts","../src/modules/oidc.ts","../src/modules/session-store.ts","../src/modules/keychain-backend.ts","../src/commands/login.ts","../src/commands/logout.ts","../src/commands/whoami.ts","../src/commands/report.ts","../src/modules/show-report/generate.ts","../src/modules/show-report/model.ts","../src/modules/show-report/plan-parser.ts","../src/modules/show-report/queue-parser.ts","../src/modules/show-report/ledger-parser.ts","../src/modules/show-report/decisions-parser.ts","../src/modules/show-report/sources.ts","../src/modules/show-report/render.ts","../src/modules/show-report/theme.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { createRequire } from \"node:module\";\nimport { fileURLToPath } from \"node:url\";\nimport { dirname, join } from \"node:path\";\nimport { runInit } from \"./commands/init.js\";\nimport { runAdd } from \"./commands/add.js\";\nimport { runUpdate } from \"./commands/update.js\";\nimport { runStatus } from \"./commands/status.js\";\nimport { runUninstall } from \"./commands/uninstall.js\";\nimport { runDoctor } from \"./commands/doctor.js\";\nimport { runWardCli } from \"./commands/ward.js\";\nimport { runUnblockPush } from \"./commands/unblock-push.js\";\nimport { runBlockPush } from \"./commands/block-push.js\";\nimport { runLogin } from \"./commands/login.js\";\nimport { runLogout } from \"./commands/logout.js\";\nimport { runWhoami } from \"./commands/whoami.js\";\nimport { runReport } from \"./commands/report.js\";\nimport { agentsTargetDir, runAgentsInit, runAgentsList, runAgentsSync } from \"./modules/agents.js\";\nimport { printWelcome } from \"./modules/banner.js\";\nimport { userTierRoot } from \"./modules/user-tier.js\";\nimport type {\n Profile,\n AgentInitOptions,\n AgentProfileId,\n InstallScope,\n ManifestFlags,\n NamingStrategy,\n AgentSyncOptions,\n} from \"./types.js\";\n\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"../package.json\") as { version: string };\n\n/**\n * Resolved path to the bundled assets directory.\n * Points to src/assets/ during tests (vitest runs source directly) and\n * dist/assets/ in the published package (tsup bundles to dist/index.js).\n * Can be overridden via ARCANE_ASSETS_DIR env var for testing the built binary.\n */\nexport const ASSETS_DIR =\n process.env[\"ARCANE_ASSETS_DIR\"] ??\n join(dirname(fileURLToPath(import.meta.url)), \"assets\");\n\nconst program = new Command();\n\n/**\n * ARC-045 decision 3 / CS-04: `--user` points init/update/status/uninstall\n * at the per-user tier (`~/.arcane`) instead of the current repository. One\n * description string, so the four commands describe the same thing.\n */\nconst USER_FLAG_DESCRIPTION =\n \"Operate on the per-user tier at ~/.arcane — spells shared by every repository on this machine, fanned out to ~/.agents/skills (Codex, Copilot) and ~/.claude/commands (Claude Code)\";\n\n/**\n * The same flag on `spell agents`, which moves a different set of files: the\n * roster and its rendered agent files, not spells.\n */\nconst USER_TIER_AGENTS_DESCRIPTION =\n \"Operate on the per-user tier at ~/.arcane — one agent roster shared by every repository on this machine, rendered to ~/.copilot/agents (VS Code agent modes)\";\n\nfunction targetDirFor(opts: { user?: boolean }): string {\n return opts.user ? userTierRoot() : process.cwd();\n}\n\n/**\n * PRD D-02: one flag per manifest question, on both `init` and `update`, so a\n * harness can finish setup without a terminal. A flag answers an unset\n * question or tightens push_policy; it never loosens push_policy, which stays\n * `spell unblock-push`'s job (ARC-034 decision 6).\n */\nfunction withManifestFlags(command: Command): Command {\n return command\n .option(\"--role <role>\", \"Answer the hub question: hub | consumer\")\n .option(\"--tracking-mode <mode>\", \"Answer the tracking question: internal | external (external needs --external-provider)\")\n .option(\"--external-provider <provider>\", \"With --tracking-mode external: ado | github | jira | other\")\n .option(\"--content-sensitivity <level>\", \"Answer the content question: standard | sensitive\")\n .option(\n \"--push-policy <policy>\",\n \"Answer the push question: open | guarded | blocked. Can tighten a recorded policy, never loosen it (use `spell unblock-push`)\",\n )\n .option(\"--subject-root <path>\", \"Answer the subject question with a directory (docs profile)\");\n}\n\nfunction manifestFlagsFrom(opts: ManifestFlags): ManifestFlags {\n return {\n role: opts.role,\n trackingMode: opts.trackingMode,\n externalProvider: opts.externalProvider,\n contentSensitivity: opts.contentSensitivity,\n pushPolicy: opts.pushPolicy,\n subjectRoot: opts.subjectRoot,\n };\n}\n\nprogram\n .name(\"spell\")\n .description(\"Arcane framework CLI — scaffold and manage governance files\")\n .version(pkg.version)\n .action(async () => {\n // Commander routes an unrecognized subcommand here too (no root\n // `.arguments()` is declared, so it doesn't emit `command:*`) — the only\n // way to tell \"no subcommand\" from \"an unrecognized one\" is the leftover\n // positional args.\n if (program.args.length > 0) {\n const [attempted] = program.args;\n console.error(`spell: unrecognized command '${attempted}'`);\n console.error();\n console.error(\n `Spells are prompts, not CLI commands — run /spell-${attempted} in your agent client.`,\n );\n console.error();\n console.error(\"Real CLI commands:\");\n for (const cmd of program.commands) {\n console.error(` ${cmd.name().padEnd(14)} ${cmd.description()}`);\n }\n process.exitCode = 1;\n return;\n }\n // Bare `spell` with no subcommand — show the welcome (banner + real repo\n // state + Spell Loop), then the command reference.\n await printWelcome(pkg.version, process.cwd());\n program.outputHelp();\n });\n\nwithManifestFlags(\n program\n .command(\"init\")\n .description(\"Scaffold Arcane framework files into the current repository\")\n .option(\"--profile <profile>\", \"Profile: full | lite | methodology | docs | governance-only\")\n .option(\"--force\", \"Overwrite existing files without error\")\n .option(\"--dry-run\", \"Preview what would be installed without making changes\")\n .option(\"--user\", USER_FLAG_DESCRIPTION),\n)\n .action(\n async (opts: { profile?: string; force?: boolean; dryRun?: boolean; user?: boolean } & ManifestFlags) => {\n await runInit(\n {\n profile: opts.profile as Profile | undefined,\n force: opts.force,\n dryRun: opts.dryRun,\n user: opts.user,\n manifestFlags: manifestFlagsFrom(opts),\n },\n targetDirFor(opts),\n ASSETS_DIR,\n pkg.version,\n );\n },\n );\n\nprogram\n .command(\"add <components...>\")\n .description(\"Add one or more components to an existing Arcane installation, in order; stops at the first failure\")\n .option(\"--force\", \"Overwrite existing files without error\")\n .option(\"--dry-run\", \"Preview what would be added without making changes\")\n .action(\n async (\n components: string[],\n opts: { force?: boolean; dryRun?: boolean },\n ) => {\n await runAdd(\n components,\n { force: opts.force, dryRun: opts.dryRun },\n process.cwd(),\n ASSETS_DIR,\n pkg.version,\n );\n },\n );\n\nwithManifestFlags(\n program\n .command(\"update\")\n .description(\"Update installed components to the current package version\")\n .option(\"--dry-run\", \"Preview what would be updated without making changes\")\n .option(\n \"--prune\",\n \"Also delete orphaned managed files (tracked before this update, no longer part of any current component). Hash-checked: an edited file is reported, never silently deleted\",\n )\n .option(\"--user\", USER_FLAG_DESCRIPTION)\n .option(\n \"--add-new\",\n \"Also install the components your profile gained since your install (never an initOnly component, never over an existing file). Without it a terminal asks and a script installs nothing\",\n )\n .option(\n \"--default-scope <scope>\",\n \"With --user: remember repo | user as the answer `spell init` pre-selects for NEW repositories on this machine. Never rewrites an existing repository, and the question is still asked\",\n ),\n)\n .action(async (opts: { dryRun?: boolean; prune?: boolean; user?: boolean; addNew?: boolean; defaultScope?: string } & ManifestFlags) => {\n if (opts.defaultScope !== undefined) {\n if (!opts.user) {\n console.error(\n \"\\n ✗ --default-scope applies to the user tier only. It records a preference in\" +\n \"\\n ~/.arcane, not in a repository. Re-run it as:\" +\n \"\\n\\n spell update --user --default-scope \" + opts.defaultScope + \"\\n\",\n );\n process.exit(1);\n }\n if (opts.defaultScope !== \"repo\" && opts.defaultScope !== \"user\") {\n console.error(\n \"\\n ✗ --default-scope takes repo or user, not: \" + opts.defaultScope + \"\\n\",\n );\n process.exit(1);\n }\n }\n await runUpdate(\n {\n dryRun: opts.dryRun,\n prune: opts.prune,\n user: opts.user,\n addNew: opts.addNew,\n manifestFlags: manifestFlagsFrom(opts),\n ...(opts.defaultScope === undefined\n ? {}\n : { defaultScope: opts.defaultScope as InstallScope }),\n },\n targetDirFor(opts),\n ASSETS_DIR,\n pkg.version,\n );\n });\n\nprogram\n .command(\"status\")\n .description(\"Show installed components and check for updates\")\n .option(\"--user\", USER_FLAG_DESCRIPTION)\n .action(async (opts: { user?: boolean }) => {\n await runStatus(targetDirFor(opts), pkg.version, { user: opts.user });\n });\n\nprogram\n .command(\"uninstall\")\n .description(\"Remove all installed Arcane framework files\")\n .option(\"--yes\", \"Skip confirmation prompt\")\n .option(\"--dry-run\", \"Preview what would be removed without deleting files\")\n .option(\"--user\", USER_FLAG_DESCRIPTION)\n .action(async (opts: { yes?: boolean; dryRun?: boolean; user?: boolean }) => {\n await runUninstall({ yes: opts.yes, dryRun: opts.dryRun, user: opts.user }, targetDirFor(opts));\n });\n\nprogram\n .command(\"block-push\")\n .description(\"Enforce push_policy: blocked on this repository (pre-push hook + disabled push URLs); only ever tightens\")\n .action(async () => {\n await runBlockPush(process.cwd());\n });\n\nprogram\n .command(\"unblock-push\")\n .description(\"Lift this repository's push block (interactive terminal only)\")\n .action(async () => {\n await runUnblockPush(process.cwd());\n });\n\nprogram\n .command(\"login\")\n .description(\"Sign in to your Arcane account (browser by default; the session is kept in the OS keychain)\")\n .option(\"--device-code\", \"Sign in from another device by typing a code; for machines with no browser\")\n .option(\n \"--insecure-storage\",\n \"Keep the session in a file under ~/.arcane instead of the OS keychain; protected only by your account's file permissions\",\n )\n .action(async (opts: { deviceCode?: boolean; insecureStorage?: boolean }) => {\n await runLogin({ deviceCode: opts.deviceCode, insecureStorage: opts.insecureStorage });\n });\n\nprogram\n .command(\"logout\")\n .description(\"Sign out on this machine: remove the stored session\")\n .action(() => {\n runLogout();\n });\n\nprogram\n .command(\"whoami\")\n .description(\"Show who is signed in; exits 1 when signed out\")\n .option(\"--verify\", \"Also check the session with the identity service\")\n .action(async (opts: { verify?: boolean }) => {\n await runWhoami({ verify: opts.verify });\n });\n\nprogram\n .command(\"doctor\")\n .description(\"Check your environment for Arcane prerequisites\")\n .option(\"--fix\", \"Automatically create missing session continuity files\")\n .option(\"--leaks\", \"Run only the on-demand secrets/credential scan (ARC-037), skipping the default checks\")\n .action(async (opts: { fix?: boolean; leaks?: boolean }) => {\n await runDoctor(\n process.cwd(),\n { fix: opts.fix, leaks: opts.leaks, packageVersion: pkg.version },\n ASSETS_DIR,\n );\n });\n\n// ─── spell ward ───────────────────────────────────────────────────────────────\n\nprogram\n .command(\"ward\")\n .description(\"Scan the repo for leaked identifiers -- third-party trademarks and the operator's own personal identifiers alike (T18)\")\n .option(\n \"--terms <terms>\",\n \"Comma-separated denylist terms, in addition to the built-in vendor-identifier list. Seed this from your OWN identity too, not just org/venture names -- usernames, machine names, and personal handles leak the same way a private venture name does (I1)\",\n )\n .option(\n \"--terms-file <path>\",\n \"Read denylist terms from a file instead of --terms, to avoid shell-history exposure (ARC-041). Same comma/newline-separated format as ARCANE_ORG_TOKENS.\",\n )\n .option(\"--gate\", \"Exit non-zero if any leak is found (for CI)\")\n .action(async (opts: { terms?: string; termsFile?: string; gate?: boolean }) => {\n await runWardCli(process.cwd(), opts);\n });\n\n// ─── spell report ─────────────────────────────────────────────────────────────\n\nprogram\n .command(\"report\")\n .description(\n \"Generate a program's Show Report (show-report.json + .html) from docs/plans/<slug>/PLAN.md, OPERATOR-QUEUE.md and the verification ledger -- offline, no network (ARC-042)\",\n )\n .option(\"--plan <path>\", \"Report on one PLAN.md instead of auto-discovering docs/plans/*/PLAN.md\")\n .option(\"--out <dir>\", \"Write show-report.{json,html} into this directory instead of next to the plan\")\n .option(\n \"--refresh\",\n \"Accepted for forward compatibility; external-data snapshotting is not implemented yet, so this regenerates from local sources\",\n )\n .option(\n \"--theme <mode>\",\n \"Force the in-page toggle's initial state: auto | light | dark (default auto, byte-for-byte unchanged). Pair with --out for a one-off themed copy so it doesn't overwrite the tracked show-report.html\",\n )\n .action(async (opts: { plan?: string; out?: string; refresh?: boolean; theme?: string }) => {\n await runReport(process.cwd(), opts, ASSETS_DIR);\n });\n\n// ─── spell agents ─────────────────────────────────────────────────────────────\n\nconst agentsCommand = program\n .command(\"agents\")\n .description(\"Manage the portable agent system (init, sync, list)\");\n\nagentsCommand\n .command(\"init\")\n .description(\n \"Interactive agent roster setup — choose profile, naming strategy, and install definitions\",\n )\n .option(\n \"--profile <profile>\",\n \"Agent profile: base | full | custom\",\n )\n .option(\n \"--naming <strategy>\",\n \"Naming strategy: arcanos | generic | random | custom\",\n )\n .option(\"--force\", \"Reinitialize even if roster already exists\")\n .option(\"--dry-run\", \"Preview what would be created without writing files\")\n .option(\"--user\", USER_TIER_AGENTS_DESCRIPTION)\n .action(\n async (opts: {\n profile?: string;\n naming?: string;\n force?: boolean;\n dryRun?: boolean;\n user?: boolean;\n }) => {\n const scope: InstallScope = opts.user ? \"user\" : \"repo\";\n await runAgentsInit(\n {\n profile: opts.profile as AgentProfileId | undefined,\n naming: opts.naming as NamingStrategy | undefined,\n force: opts.force,\n dryRun: opts.dryRun,\n scope,\n } satisfies AgentInitOptions,\n agentsTargetDir(scope, process.cwd()),\n ASSETS_DIR,\n );\n },\n );\n\nagentsCommand\n .command(\"sync\")\n .description(\n \"Regenerate all client instruction files from canonical agent definitions\",\n )\n .option(\"--dry-run\", \"Preview what would be generated without writing files\")\n .option(\"--no-openclaw\", \"Skip OpenClaw workspace output\")\n .option(\"--no-copilot\", \"Skip Copilot output\")\n .option(\"--no-claude\", \"Skip Claude output\")\n .option(\"--no-codex\", \"Skip Codex output\")\n .option(\"--user\", USER_TIER_AGENTS_DESCRIPTION)\n .action(\n async (opts: {\n dryRun?: boolean;\n openclaw?: boolean;\n copilot?: boolean;\n claude?: boolean;\n codex?: boolean;\n user?: boolean;\n }) => {\n const scope: InstallScope = opts.user ? \"user\" : \"repo\";\n await runAgentsSync(\n {\n dryRun: opts.dryRun,\n openclaw: opts.openclaw,\n copilot: opts.copilot,\n claude: opts.claude,\n codex: opts.codex,\n scope,\n } satisfies AgentSyncOptions,\n agentsTargetDir(scope, process.cwd()),\n ASSETS_DIR,\n );\n },\n );\n\nagentsCommand\n .command(\"list\")\n .description(\"Show the current agent roster with assigned names and roles\")\n .option(\"--user\", USER_TIER_AGENTS_DESCRIPTION)\n .action(async (opts: { user?: boolean }) => {\n const scope: InstallScope = opts.user ? \"user\" : \"repo\";\n await runAgentsList(agentsTargetDir(scope, process.cwd()), scope);\n });\n\n// Only parse argv when executed directly — not when imported in tests.\n// Use realpathSync to resolve symlinks (e.g. npm global bin → dist/index.js).\nimport { realpathSync } from \"node:fs\";\nif (realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)) {\n await program.parseAsync(process.argv);\n}\n\nexport { program };\n","import { select, confirm, input } from \"@inquirer/prompts\";\nimport { dirname, join } from \"node:path\";\nimport chalk from \"chalk\";\nimport { copyFile, copyDirectory, fileExists } from \"../modules/copier.js\";\nimport {\n readManifest,\n writeManifest,\n isValidSubjectRoot,\n ManifestNotFoundError,\n} from \"../modules/manifest.js\";\nimport { getComponent, getProfile, listProfiles } from \"../modules/registry.js\";\nimport {\n CLAUDE_PRECEDENCE_NOTE,\n USER_FANOUT_PATHS,\n USER_TIER_COMPONENTS,\n VSCODE_USER_TIER_NOTE,\n componentForScope,\n componentForSpellScope,\n preferredSpellScope,\n describeFanoutOutcomes,\n spellIdFromStorePath,\n SPELL_FANOUT_CLIENTS,\n syncUserTierFanout,\n userTierRoot,\n} from \"../modules/user-tier.js\";\nimport {\n printDryRun,\n printStep,\n printSuccess,\n printSection,\n printInfo,\n printNextStep,\n printWarning,\n} from \"../modules/banner.js\";\nimport { runAgentsInit } from \"../modules/agents.js\";\nimport {\n askTrackingQuestions,\n hasManifestFlags,\n manifestFlagLine,\n resolveManifestFlags,\n ManifestFlagError,\n} from \"../modules/hub.js\";\nimport {\n applyBlockedPushControls,\n hookInForce,\n installClosedPrWarningHook,\n describeConfigScope,\n ARCANE_HOOKS_DIR,\n} from \"../modules/push-safety.js\";\nimport { installSecretsPrecommitHook } from \"../modules/secrets-scan.js\";\nimport {\n correctUnbornMasterDefault,\n ensureLocalPullRebase,\n inspectGitRepository,\n} from \"../modules/git.js\";\nimport type {\n ArcaneManifest,\n ContentSensitivity,\n ExternalProvider,\n HubRole,\n InstallScope,\n InstalledComponent,\n Profile,\n RegistryComponent,\n PushPolicy,\n SpellInitOptions,\n TrackingMode,\n} from \"../types.js\";\n\nconst VALID_PROFILES: Profile[] = [\"full\", \"lite\", \"governance-only\", \"methodology\", \"docs\"];\n\n/** Menu glyph per profile. Falls back to 📋 for any profile not listed. */\nconst PROFILE_ICONS: Partial<Record<Profile, string>> = {\n full: \"🧙\",\n lite: \"⚡\",\n docs: \"📖\",\n};\n\n/** True for the capability-scoped spell components (see registry.ts). */\nfunction isSpellComponent(name: string): boolean {\n return name.startsWith(\"spells-\");\n}\n\n// ─── Category labels for preview ─────────────────────────────────────────────\n\ninterface ComponentGroup {\n icon: string;\n label: string;\n count: number;\n}\n\nfunction categorizeComponents(components: RegistryComponent[]): ComponentGroup[] {\n const groups: ComponentGroup[] = [];\n\n // Spells arrive as several capability components rather than one\n // monolith, and each carries a spell's canonical source plus its three\n // generated client shims (ARC-045 / CS-03), so count canonical files --\n // one per spell -- rather than components or shims.\n const spellFiles = components.filter((c) => isSpellComponent(c.name)).flatMap((c) => c.files);\n\n const spellCount = spellFiles.filter((f) => f.startsWith(\".arcane/spells/\")).length;\n if (spellCount > 0) {\n groups.push({ icon: \"✨\", label: \"Spells (Copilot, Claude Code, Codex)\", count: spellCount });\n }\n\n const governance = components.filter(\n (c) =>\n !isSpellComponent(c.name) &&\n c.name !== \"venture-template\" &&\n c.name !== \"agent-definitions\" &&\n c.files.some((f) => f.startsWith(\".arcane/governance/\")),\n );\n if (governance.length > 0) {\n groups.push({\n icon: \"📋\",\n label: \"Governance Docs\",\n count: governance.reduce((n, c) => n + c.files.length, 0),\n });\n }\n\n const agentDef = components.find((c) => c.name === \"agent-definitions\");\n if (agentDef) {\n groups.push({\n icon: \"🛡️\",\n label: \"Agent Policies\",\n count: agentDef.files.length,\n });\n }\n\n const templates = components.find((c) => c.name === \"venture-template\");\n if (templates) {\n groups.push({\n icon: \"📄\",\n label: \"Templates\",\n count: templates.files.length,\n });\n }\n\n return groups;\n}\n\n/**\n * Builds a hover description string for the profile selection menu.\n * Shows the category breakdown so users know what they're getting.\n */\nfunction buildProfileDescription(components: RegistryComponent[], profile: Profile): string {\n const groups = categorizeComponents(components);\n const parts = groups.map((g) => `${g.icon} ${g.count} ${g.label}`);\n if (profile === \"full\" || profile === \"lite\") {\n // Insert agent roles before governance docs (spells + agents first, then the rest)\n const govIndex = groups.findIndex((g) => g.label === \"Governance Docs\");\n parts.splice(govIndex >= 0 ? govIndex : parts.length, 0, \"🤖 12 Agent Roles\");\n }\n return \"\\n\" + parts.join(\"\\n\");\n}\n\n// ─── Main ─────────────────────────────────────────────────────────────────────\n\n/**\n * Runs the `spell init` command.\n *\n * @param options CLI flags (profile, force, dryRun)\n * @param targetDir Directory to initialize (defaults to cwd in CLI)\n * @param assetsDir Path to the bundled assets root (dist/assets/ in prod, src/assets/ in tests)\n * @param packageVersion Current package version string (passed from CLI entry to avoid require path issues)\n */\nexport async function runInit(\n options: SpellInitOptions,\n targetDir: string,\n assetsDir: string,\n packageVersion: string,\n): Promise<void> {\n // The user tier is a different install with a different shape -- see\n // runInitUser. `targetDir` is the store root (~/.arcane) in that case.\n if (options.user) {\n if (hasManifestFlags(options.manifestFlags)) {\n console.error(\n \"\\n ✗ The manifest-question flags describe a repository; the user tier has none of those fields.\\n\",\n );\n process.exit(1);\n return;\n }\n await runInitUser(options, targetDir, assetsDir, packageVersion);\n return;\n }\n\n // Validate profile option if provided\n if (options.profile && !VALID_PROFILES.includes(options.profile)) {\n throw new Error(\n `Invalid profile \"${options.profile}\". Valid options: ${VALID_PROFILES.join(\", \")}`,\n );\n }\n\n // PRD D-02: validated before anything is written. A fresh install has no\n // recorded answers, so a valid flag always applies.\n let flags: Partial<ArcaneManifest> = {};\n try {\n flags = resolveManifestFlags(options.manifestFlags, {}, join(targetDir, \".arcane.json\"));\n } catch (err) {\n if (!(err instanceof ManifestFlagError)) throw err;\n console.error(`\\n ✗ ${err.message}\\n`);\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n\n // Check if already initialized\n try {\n await readManifest(targetDir);\n console.log(chalk.hex(\"#a855f7\")(`\\n ✦ Arcane v${packageVersion}\\n`));\n console.log('Already initialized. Run \"spell update\" to update existing files.');\n if (hasManifestFlags(options.manifestFlags)) {\n console.log(\"Pass the same manifest-question flags to `spell update` to record them here.\");\n }\n return;\n } catch (err) {\n if (!(err instanceof ManifestNotFoundError)) throw err;\n // ManifestNotFoundError = not initialized — proceed\n }\n\n console.log(chalk.hex(\"#a855f7\")(`\\n ✦ Arcane v${packageVersion}\\n`));\n\n // ── Git-state checks (EF-05, EF-32) ─────────────────────────────────────\n // Never runs `git init` or creates a commit -- see\n // features/init-git-state-contract/PRD.md's \"Design decision\" section for\n // why that's declined rather than deferred. Only acts where it's provably\n // safe (an unborn repo has no commits to conflict with) or where the\n // operator already explicitly confirmed continuing (uncommitted changes).\n let repoNotFound = false;\n let repoHasCommits = false;\n if (!options.dryRun) {\n const gitState = await inspectGitRepository(targetDir);\n repoHasCommits = gitState.status !== \"not-repository\" && gitState.status !== \"no-commits\";\n\n if (gitState.status === \"not-repository\") {\n repoNotFound = true;\n } else {\n if (gitState.status === \"no-commits\") {\n const correction = await correctUnbornMasterDefault(targetDir);\n if (correction.corrected) {\n printInfo(\n `Repointed the unborn branch from \"${correction.from}\" to \"${correction.to}\" (avoids the Git for Windows default-branch trap).`,\n );\n } else if (correction.blockedReason === \"target-unreadable\") {\n printWarning(\n 'This repo\\'s \"main\" branch ref exists but could not be read (broken ref). Left the current branch alone — ' +\n \"repointing onto an unreadable ref risks attaching your staged work to history Arcane cannot see. \" +\n \"Check `git for-each-ref refs/heads/main` before committing.\",\n );\n }\n }\n\n if (gitState.status === \"ready\" && gitState.uncommittedChanges > 0) {\n printWarning(\n `This repo has ${gitState.uncommittedChanges} uncommitted change${gitState.uncommittedChanges === 1 ? \"\" : \"s\"}.`,\n );\n printInfo(\"Running spell init will add new files to your working tree.\");\n console.log();\n const continueAnyway = await confirm({\n message: \"Continue anyway?\",\n default: true,\n });\n if (!continueAnyway) {\n console.log(\"\\nCancelled. Commit or stash your changes first.\");\n return;\n }\n console.log();\n }\n\n const rebaseResult = await ensureLocalPullRebase(targetDir);\n if (rebaseResult.action === \"explicit-false-preserved\") {\n printWarning(\n \"This repo's pull.rebase is explicitly set to false, but Arcane's governance mandates rebase-and-fast-forward (see .arcane/governance/git-conventions.md). Left as-is since you set it explicitly.\",\n );\n }\n }\n }\n\n // ── Step 1: Resolve profile (prompt if not provided) ───────────────────\n let profile: Profile = options.profile as Profile;\n if (!profile) {\n const profiles = listProfiles();\n profile = (await select({\n message: \"Select an installation profile:\\n\",\n choices: profiles.map((p) => {\n const profileComponents = getProfile(p.id);\n return {\n value: p.id,\n name: `${PROFILE_ICONS[p.id] ?? \"📋\"} ${p.displayName} — ${p.description}`,\n description: buildProfileDescription(profileComponents, p.id),\n };\n }),\n })) as Profile;\n }\n\n // ── Step 2: Resolve components ─────────────────────────────────────────\n const profileComponents = getProfile(profile);\n\n // ── Step 2b: Spell scope (ARC-045 decision 4 / CS-05) ──────────────────\n // Asked here rather than beside the other manifest questions (Steps 5a-5e)\n // because it changes WHAT gets installed, not just what gets recorded: the\n // preview below and the copy loop both have to reflect it.\n //\n // Asked only when this machine actually has a user tier -- most do not, and\n // a question about a store that does not exist is noise -- and only in an\n // interactive install, the same gate every other manifest question uses.\n // The question is always asked; only which answer is pre-selected changes.\n // No on purpose unless this machine says otherwise (ARC-048): opting out is\n // committed to .arcane.json and inherited by every clone, so a shared or\n // team repository must not stop carrying its spells just because the machine\n // that happened to run `spell init` had a tier installed. An operator who\n // has decided for their own machine sets `default_spell_scope` in the store\n // and gets their answer pre-selected -- in NEW repositories only, since this\n // is read at init time and rewrites no existing manifest.\n let spell_scope: InstallScope | undefined;\n if (!options.profile && !options.dryRun) {\n let storeVersion: string | undefined;\n let preferred: InstallScope = \"repo\";\n try {\n const store = await readManifest(userTierRoot());\n storeVersion = store.version;\n preferred = preferredSpellScope(store);\n } catch {\n // No user tier on this machine (or an unreadable one) -- do not ask.\n }\n if (storeVersion !== undefined) {\n console.log();\n const useTier = await confirm({\n message: `This machine has an Arcane user tier (v${storeVersion}). Use it for this repository's spells instead of installing copies here?`,\n default: preferred === \"user\",\n });\n // Recorded either way, like the hub question: \"asked and declined\" is\n // worth telling apart from \"predates the field\".\n spell_scope = useTier ? \"user\" : \"repo\";\n }\n }\n const components = profileComponents.map((component) =>\n componentForSpellScope(component, spell_scope ?? \"repo\"),\n );\n\n // ── Step 3: Preview what will be installed ─────────────────────────────\n const groups = categorizeComponents(components);\n\n console.log();\n printSection(`📦 Profile: ${profile}`);\n console.log();\n\n // Show agent setup as upcoming interactive step\n const hasAgents = profile === \"full\" || profile === \"lite\";\n\n // Build summary parts (same style as hover description)\n const previewParts = groups.map((g) => `${g.icon} ${g.count} ${g.label}`);\n if (hasAgents) {\n // Insert agent roles before governance docs\n const govIndex = groups.findIndex((g) => g.label === \"Governance Docs\");\n previewParts.splice(govIndex >= 0 ? govIndex : previewParts.length, 0, \"🤖 12 Agent Roles\");\n }\n console.log(` ${previewParts.join(\" · \")}`);\n\n console.log();\n\n // ── Step 4: Confirm (skip in non-interactive mode) ─────────────────────\n if (!options.dryRun && !options.profile) {\n const proceed = await confirm({\n message: \"Proceed with installation?\",\n default: true,\n });\n if (!proceed) {\n console.log(\"\\nCancelled.\");\n return;\n }\n }\n\n // ── Step 5: Copy files ─────────────────────────────────────────────────\n const installedComponents: InstalledComponent[] = [];\n let fileCount = 0;\n\n for (const component of components) {\n const installedFiles: string[] = [];\n const fileHashes: Record<string, string> = {};\n\n for (const file of component.files) {\n const srcPath = join(assetsDir, component.sourceOverrides?.[file] ?? file);\n if (options.dryRun) {\n printDryRun(`Would copy: ${file}`);\n } else {\n try {\n fileHashes[file] = await copyFile(srcPath, targetDir, file, { force: options.force });\n } catch (err) {\n // skipExisting components silently skip files that already exist\n if (component.skipExisting && err instanceof Error && err.message.includes(\"already exists\")) {\n continue;\n }\n throw err;\n }\n }\n installedFiles.push(file);\n fileCount++;\n }\n\n for (const dir of component.directories ?? []) {\n const srcDirPath = join(assetsDir, dir);\n if (options.dryRun) {\n printDryRun(`Would copy directory: ${dir}/`);\n } else {\n const copied = await copyDirectory(srcDirPath, targetDir, dir, { force: options.force });\n for (const { path: copiedPath, hash } of copied) {\n installedFiles.push(copiedPath);\n fileHashes[copiedPath] = hash;\n }\n fileCount += copied.length;\n }\n }\n\n if (!options.dryRun) {\n installedComponents.push({\n name: component.name,\n files: installedFiles,\n installedVersion: packageVersion,\n fileHashes,\n });\n }\n }\n\n // Show compact category summary (not 60 individual file paths)\n if (!options.dryRun) {\n for (const group of groups) {\n printStep(`${group.icon} ${group.count} ${group.label}`);\n }\n }\n\n if (options.dryRun) {\n // Show what agent setup would install\n if (hasAgents) {\n const agentFiles = await import(\"node:fs\").then((fsModule) =>\n fsModule.promises.readdir(join(assetsDir, \"agents\")),\n );\n const yamlFiles = agentFiles.filter((f) => f.endsWith(\".yaml\")).sort();\n printDryRun(\"Would run agent setup (interactive):\");\n printDryRun(\"Would write: .arcane/agents.yaml\");\n for (const f of yamlFiles) {\n printDryRun(`Would copy: .arcane/agents/${f}`);\n }\n }\n printDryRun(`Would initialize with profile \"${profile}\" — ${fileCount} files`);\n return;\n }\n\n // ── Step 5a: Hub question ───────────────────────────────────────────────\n // Explicit opt-in only -- never inferred from repo content (a `ventures/`\n // directory can legitimately exist in a consumer repo too, e.g. one this\n // very init run just created via a governance-only profile). Written\n // explicitly either way so a future `spell update` retrofit wizard can\n // tell \"asked and declined\" apart from \"predates this feature\".\n let role: HubRole | undefined = flags.role;\n if (role === undefined && !options.profile) {\n console.log();\n const isHub = await confirm({\n message: \"Will this repo manage other ventures as a hub? (idea books, spell-manifest promotion, venture registry)\",\n default: false,\n });\n role = isHub ? \"hub\" : \"consumer\";\n }\n\n // ── Step 5b: Tracking-mode question (EF-14) ─────────────────────────────\n // governance-only/methodology profiles have no code-tracking surface --\n // default without asking, even under --profile (deterministic, no prompt\n // to skip). full/lite ask once, interactively only, mirroring the hub\n // question's own gating exactly -- a scripted full/lite install leaves\n // this unset, resolved later by spell update's retrofit wizard.\n let tracking_mode: TrackingMode | undefined;\n let external_provider: ExternalProvider | null | undefined;\n if (flags.tracking_mode !== undefined) {\n tracking_mode = flags.tracking_mode;\n external_provider = flags.external_provider ?? null;\n } else if (profile === \"governance-only\" || profile === \"methodology\" || profile === \"docs\") {\n tracking_mode = \"internal\";\n external_provider = null;\n } else if (!options.profile) {\n console.log();\n ({ tracking_mode, external_provider } = await askTrackingQuestions(targetDir));\n }\n\n // ── Step 5c: Subject shape (EF-07) ──────────────────────────────────────\n // Asked for the docs profile only. Other profiles describe code or a\n // venture portfolio, where \"what is this repo about\" is already answered by\n // business_root or by the code itself; a docs/records repo is the case where\n // the repository IS one subject and nothing currently expresses that.\n // Interactive only, like every other manifest question.\n let subject_root: string | null | undefined = flags.subject_root;\n if (subject_root === undefined && profile === \"docs\" && !options.profile) {\n console.log();\n const shape = await select({\n message: \"What does this repository hold?\",\n choices: [\n {\n value: \"root\",\n name: \"One subject, at the repository root (documents sit alongside Arcane's files)\",\n },\n { value: \"subdir\", name: \"One subject, in its own directory\" },\n { value: \"portfolio\", name: \"Several subjects or ventures (decide per-document later)\" },\n ],\n });\n if (shape === \"root\") {\n // \".\" is deliberate, not a placeholder: it lets an existing archive come\n // under governance without being restructured first (EF-07 / MH-02).\n subject_root = \".\";\n } else if (shape === \"subdir\") {\n const answer = await input({\n message: \"Directory holding the subject's documents:\",\n default: \"docs\",\n validate: (v) =>\n isValidSubjectRoot(v.trim() || \"docs\") ||\n \"Must be a relative path inside the repository (no leading /, drive letter, or ..).\",\n });\n subject_root = answer.trim() || \"docs\";\n }\n else if (shape === \"portfolio\") {\n // Explicit null, not unset: records that the question was asked and\n // answered, so spell update's retrofit doesn't put it again every run.\n subject_root = null;\n }\n }\n\n // ── Step 5d: Content sensitivity (EF-12) ────────────────────────────────\n // Asked on every interactive install, not just docs: a code repo can hold\n // sensitive records too. Declared once for the whole repository -- per-file\n // detection is unreliable for general documents, which is why the docs-mode\n // PRD rejects scanning as a primary mechanism.\n let content_sensitivity: ContentSensitivity | undefined = flags.content_sensitivity;\n if (content_sensitivity === undefined && !options.profile) {\n console.log();\n content_sensitivity = (await select({\n message: \"How should agents treat this repository's contents?\",\n choices: [\n { value: \"standard\", name: \"Standard — agents may quote contents in journals and decisions\" },\n {\n value: \"sensitive\",\n name: \"Sensitive — agents reference documents by path, never transcribe them\",\n },\n ],\n default: \"standard\",\n })) as ContentSensitivity;\n }\n\n // ── Step 5e: Push policy (EF-09) ────────────────────────────────────────\n // Default \"open\" — strictly additive, so every existing repository and every\n // scripted install behaves exactly as before. Asked interactively only.\n let push_policy: PushPolicy | undefined = flags.push_policy;\n if (push_policy === undefined && !options.profile) {\n console.log();\n push_policy = (await select({\n message: \"Should this repository be allowed to push to a remote?\",\n choices: [\n { value: \"open\", name: \"Yes — normal repository\" },\n { value: \"guarded\", name: \"Sensitive, but keep push working — remind me instead\" },\n {\n value: \"blocked\",\n name: \"No — block pushes (a pre-push hook plus a disabled push URL)\",\n },\n ],\n default: \"open\",\n })) as PushPolicy;\n }\n\n // ── Step 6: Write manifest ─────────────────────────────────────────────\n const manifest: ArcaneManifest = {\n version: packageVersion,\n profile,\n installedAt: new Date().toISOString(),\n components: installedComponents,\n ...(spell_scope ? { spell_scope } : {}),\n ...(role ? { role } : {}),\n ...(tracking_mode ? { tracking_mode, external_provider } : {}),\n ...(subject_root !== undefined ? { subject_root } : {}),\n ...(content_sensitivity ? { content_sensitivity } : {}),\n ...(push_policy ? { push_policy } : {}),\n };\n await writeManifest(targetDir, manifest);\n\n printSuccess(`Initialized with profile \"${profile}\" — ${fileCount} files installed`);\n\n // A scripted install leaves unflagged questions unset (PRD D-02): say which,\n // and the exact line that answers them.\n const flagLine = options.profile ? manifestFlagLine(manifest) : undefined;\n if (flagLine) {\n printInfo(\"Some setup questions were left unanswered. Answer them without a prompt with:\");\n for (const line of flagLine) printInfo(` ${line}`);\n }\n\n // ── Push-safety controls (EF-09 R2/R3/R4/R7) ────────────────────────────\n // Wrapped: a failure here must not surface as a raw stack trace after\n // \"Initialized successfully\", leaving a manifest that says \"blocked\" while\n // the operator believes they are protected.\n if (push_policy === \"blocked\" || push_policy === \"guarded\") {\n try {\n if (push_policy === \"blocked\") {\n const controls = await applyBlockedPushControls(targetDir, {\n emit: (m) => (m.level === \"warning\" ? printWarning(m.text) : printInfo(m.text)),\n });\n // A flag-driven, non-interactive init must not read as success while\n // the recorded block is only partly in force (ARC-034 decision 5).\n if (!hookInForce(controls.hook) || controls.urls.some((u) => u.status === \"failed\")) {\n printWarning(\n 'push_policy is recorded as \"blocked\", but the block is not fully in force. Resolve the ' +\n \"cause above, then run `spell block-push`; `spell doctor` shows exactly what is missing.\",\n );\n process.exitCode = 1;\n }\n } else {\n printInfo(\n \"Marked as push-guarded. No technical control was installed — check the remote is the \" +\n \"one you intend before pushing. `spell doctor` will keep reminding you.\",\n );\n }\n } catch (err) {\n printWarning(\n `Could not fully apply push-safety controls: ${err instanceof Error ? err.message : String(err)}. ` +\n `The manifest records push_policy: \"${push_policy}\", but this repository may not actually be ` +\n \"protected. Run `spell doctor` to see exactly what is and isn't in place.\",\n );\n }\n }\n\n // ── Closed-PR push warning (ARC-035 decision 4) ─────────────────────────\n // Every tier except \"blocked\" gets this: a \"blocked\" repo already refuses\n // every push outright and has no use for a warning about which PR it\n // would have gone to (installing here would also silently downgrade that\n // stronger control, since both share the \"pre-push\" hook slot). Wrapped\n // the same way as the blocks above -- a failure here must not surface as\n // a raw stack trace after \"Initialized successfully\".\n if (push_policy !== \"blocked\") {\n try {\n const hook = await installClosedPrWarningHook(targetDir);\n if (hook.status === \"installed\") {\n printInfo(\"Installed a pre-push hook that warns when the current branch's PR is already closed or merged.\");\n }\n // Other outcomes (refused-*, already-ours) mirror the blocking hook's\n // own install path and are silent here on purpose: this hook is a\n // convenience warning, not a security control, so a refusal doesn't\n // leave the repository in a worse-than-before state the way a silently\n // skipped block would -- `spell doctor` still surfaces the refusal\n // reason for the shared hooks-path collision case.\n } catch {\n // Never let this convenience warning's own failure surface as a raw\n // stack trace after \"Initialized successfully\" -- it is strictly\n // additive, so a failure here means the repo is exactly as protected\n // as it was before this block ran, not less.\n }\n }\n\n // ── Secrets pre-commit hook (ARC-037 decision 2) ────────────────────────\n // Deliberately push_policy-independent: every profile gets this, since\n // credential leakage is a risk regardless of whether history may reach a\n // remote. Mandatory, not an interactive question -- see secrets-scan.ts's\n // own reasoning for why. Wrapped the same way as the push-safety block\n // above: a failure here must not surface as a raw stack trace after\n // \"Initialized successfully\".\n try {\n const hook = await installSecretsPrecommitHook(targetDir);\n if (hook.status === \"installed\") {\n printInfo(\"Installed a pre-commit hook that scans staged content for leaked credentials.\");\n } else if (hook.status === \"refused-unreadable-config\") {\n printWarning(\n \"Did not install the secrets pre-commit hook: git could not report the current \" +\n \"core.hooksPath (unreadable config, or a git too old for `--show-scope`).\",\n );\n } else if (hook.status === \"refused-default-hooks\") {\n printWarning(\n `Did not install the secrets pre-commit hook: this repository has hooks in git's default ` +\n `directory (${hook.hooks.join(\", \")}). Setting core.hooksPath would silently stop them ` +\n `running. Move them under \\`${ARCANE_HOOKS_DIR}\\` yourself and re-run.`,\n );\n } else if (hook.status === \"refused-foreign-hooks-path\") {\n printWarning(\n `Did not install the secrets pre-commit hook: core.hooksPath is already \"${hook.existing}\" ` +\n `(${describeConfigScope(hook.scope)}), so another hook manager owns it.`,\n );\n }\n // \"already-ours\": nothing to report, matches the prior install exactly.\n } catch (err) {\n printWarning(\n `Could not install the secrets pre-commit hook: ${err instanceof Error ? err.message : String(err)}. ` +\n \"Run `spell doctor --leaks` directly to scan on demand instead.\",\n );\n }\n\n // ── Step 7: Offer agent setup (for full and lite profiles) ─────────────\n let agentsConfigured = false;\n if (hasAgents && !options.profile) {\n console.log();\n const setupAgents = await confirm({\n message: \"🤖 Set up your agent team? (names, roles, AI tool configs)\",\n default: true,\n });\n\n if (setupAgents) {\n await runAgentsInit({}, targetDir, assetsDir);\n agentsConfigured = true;\n }\n }\n\n // ── Step 8: Print next steps ───────────────────────────────────────────\n console.log();\n printSection(\"🚀 Next steps\");\n console.log();\n let step = 1;\n\n if (!agentsConfigured && profile !== \"governance-only\") {\n printNextStep(step++, \"Run `spell agents init` to set up your agent team\");\n }\n if (agentsConfigured) {\n printNextStep(\n step++,\n \"Edit .arcane/agents.yaml to customize roles → `spell agents sync` to regenerate AI tool configs\",\n );\n }\n if (profile !== \"governance-only\") {\n printNextStep(\n step++,\n \"Review .arcane/governance/ docs and customize for your project\",\n );\n }\n if (repoNotFound) {\n printNextStep(\n step++,\n 'Initialize Git for this project: `git init -b main` (avoids defaulting to \"master\" on systems where that\\'s the system default)',\n );\n }\n const wroteGitattributes = installedComponents.some((c) => c.files.includes(\".gitattributes\"));\n if (wroteGitattributes && repoHasCommits) {\n printWarning(\n \"Installed .gitattributes, which normalizes text to LF. In a repository with history, files \" +\n \"committed with CRLF can show as modified once Git renormalizes them (for example after \" +\n \"`git add --renormalize .`). Review and commit that as its own change.\",\n );\n }\n printNextStep(\n step++,\n `Commit the new files: git add .arcane .github .claude AGENTS.md CLAUDE.md .arcane.json${wroteGitattributes ? \" .gitattributes\" : \"\"}`,\n );\n console.log();\n}\n\n// ─── spell init --user ────────────────────────────────────────────────────────\n\n/**\n * `spell init --user` (ARC-045 decision 3 / CS-04). `storeRoot` is `~/.arcane`,\n * resolved by the CLI entry. Deliberately none of the repository ceremony\n * above: no profile (the tier is spells-only and un-profiled), no git-state\n * checks (the store is not a repository), none of the manifest questions\n * (hub role, tracking mode, sensitivity and push policy describe a\n * repository), no hooks, no agent setup. Copies the canonical spells into the\n * store through the same copier and hash record a repository install uses,\n * fans the user-level shims out to the clients' home directories, and records\n * both in the store manifest so `update`/`status`/`uninstall --user` know\n * exactly what Arcane wrote -- and nothing else.\n */\nasync function runInitUser(\n options: SpellInitOptions,\n storeRoot: string,\n assetsDir: string,\n packageVersion: string,\n): Promise<void> {\n if (options.profile) {\n throw new Error(\n '\"--profile\" does not apply to \"--user\": the user tier always installs every spell and nothing else.',\n );\n }\n if (hasManifestFlags(options.manifestFlags)) {\n throw new Error(\n 'The manifest-question flags do not apply to \"--user\": they describe a repository, and the user tier is not one.',\n );\n }\n\n try {\n await readManifest(storeRoot);\n console.log(chalk.hex(\"#a855f7\")(`\\n ✦ Arcane v${packageVersion}\\n`));\n console.log(\n `Already initialized (user tier at ${storeRoot}). Run \"spell update --user\" to update it.`,\n );\n return;\n } catch (err) {\n if (!(err instanceof ManifestNotFoundError)) throw err;\n }\n\n console.log(chalk.hex(\"#a855f7\")(`\\n ✦ Arcane v${packageVersion}\\n`));\n\n // The store's parent is the home directory by construction (userTierRoot),\n // and the fan-out paths are relative to it.\n const homeDir = dirname(storeRoot);\n const components = USER_TIER_COMPONENTS.map((name) =>\n componentForScope(getComponent(name), \"user\"),\n );\n const spellIds = components\n .flatMap((c) => c.files)\n .map((file) => spellIdFromStorePath(file))\n .filter((id): id is string => id !== undefined);\n\n printSection(`📦 User tier — ${storeRoot}`);\n console.log();\n console.log(\n ` ✨ ${spellIds.length} Spells → ${storeRoot}/spells/ · 🔗 ${spellIds.length * 2} client files → ~/.agents/skills (Codex, Copilot), ~/.claude/commands (Claude Code)`,\n );\n console.log();\n\n if (options.dryRun) {\n for (const component of components) {\n for (const file of component.files) printDryRun(`Would copy: ${file}`);\n }\n // The store does not exist yet in a dry run, so the shims cannot be\n // rendered from it; list the paths and flag the one thing that would\n // change the outcome -- a same-named file already there, which the real\n // run leaves alone rather than claiming.\n for (const id of spellIds) {\n for (const client of [\"codex\", \"claude\"] as const) {\n const relativePath = USER_FANOUT_PATHS[client](id);\n if (await fileExists(join(homeDir, relativePath))) {\n printDryRun(`Would leave alone (exists, not written by Arcane): ~/${relativePath}`);\n } else {\n printDryRun(`Would write: ~/${relativePath}`);\n }\n }\n }\n printDryRun(\n `Would initialize the user tier — ${spellIds.length} spells, ${spellIds.length * 2} client files`,\n );\n return;\n }\n\n const installedComponents: InstalledComponent[] = [];\n let fileCount = 0;\n for (const component of components) {\n const installedFiles: string[] = [];\n const fileHashes: Record<string, string> = {};\n for (const file of component.files) {\n const srcPath = join(assetsDir, component.sourceOverrides?.[file] ?? file);\n fileHashes[file] = await copyFile(srcPath, storeRoot, file, { force: options.force });\n installedFiles.push(file);\n fileCount++;\n }\n installedComponents.push({\n name: component.name,\n files: installedFiles,\n installedVersion: packageVersion,\n fileHashes,\n });\n }\n\n const fanout = await syncUserTierFanout({ homeDir, storeRoot, spellIds, fallbackDir: assetsDir, ownedClients: SPELL_FANOUT_CLIENTS });\n\n const manifest: ArcaneManifest = {\n version: packageVersion,\n // The tier is un-profiled; \"full\" records the profile its component set\n // is drawn from, and every command reads `scope` before `profile`.\n profile: \"full\",\n installedAt: new Date().toISOString(),\n components: installedComponents,\n scope: \"user\",\n fanout: fanout.record,\n };\n await writeManifest(storeRoot, manifest);\n\n printStep(`✨ ${fileCount} Spells installed to ${storeRoot}/spells/`);\n for (const line of describeFanoutOutcomes(fanout.outcomes)) {\n if (line.startsWith(\"!\")) printWarning(line.slice(2));\n else printStep(`🔗 ${line}`);\n }\n printSuccess(\n `Initialized the user tier — ${fileCount} spells, ${Object.keys(fanout.record).length} client files recorded`,\n );\n\n console.log();\n printSection(\"🧭 Clients\");\n console.log();\n for (const line of VSCODE_USER_TIER_NOTE) printInfo(line);\n printInfo(CLAUDE_PRECEDENCE_NOTE);\n\n console.log();\n printSection(\"🚀 Next steps\");\n console.log();\n printNextStep(1, \"Reload VS Code, and start a fresh Codex or Claude Code session, so the pickers see the new files\");\n printNextStep(2, \"`spell status --user` shows the tier; `spell update --user` refreshes it after upgrading the CLI\");\n console.log();\n}\n","import {\n mkdir,\n access,\n readdir,\n readFile,\n rm,\n rmdir,\n writeFile,\n} from \"node:fs/promises\";\nimport { createHash } from \"node:crypto\";\nimport path from \"node:path\";\n\n/**\n * CRLF and lone CR -> LF, at the byte level (no decoding, so any content is\n * handled exactly). Arcane's managed files are text, and git rewrites their\n * line endings on checkout (`text=auto eol=lf`, `core.autocrlf`); a hash that\n * saw those bytes read a file nobody had edited as an operator edit -- found\n * live in the CS-03 migration fixture (TODO.md, \"a recorded file hash is\n * byte-exact\").\n */\nfunction normalizeLineEndings(content: Buffer): Buffer {\n if (!content.includes(0x0d)) return content;\n const out = Buffer.allocUnsafe(content.length);\n let o = 0;\n for (let i = 0; i < content.length; i++) {\n const byte = content[i]!;\n if (byte === 0x0d) {\n out[o++] = 0x0a;\n if (content[i + 1] === 0x0a) i++;\n continue;\n }\n out[o++] = byte;\n }\n return out.subarray(0, o);\n}\n\n/**\n * SHA-256 hex digest of content with line endings normalized to LF -- the\n * hash `fileHashes` and the user tier's `fanout` record carry (ARC-038\n * decision 1). Two files that differ only in CRLF/LF hash the same.\n */\nexport function hashContent(content: Buffer | string): string {\n const bytes = typeof content === \"string\" ? Buffer.from(content, \"utf-8\") : content;\n return createHash(\"sha256\").update(normalizeLineEndings(bytes)).digest(\"hex\");\n}\n\n/** `hashContent` of a file on disk. */\nexport async function hashFile(filePath: string): Promise<string> {\n return hashContent(await readFile(filePath));\n}\n\n/**\n * True when a file still matches a hash Arcane recorded for it -- the\n * line-ending-normalized digest (what `hashFile`/`copyFile` record now), or\n * the raw-bytes digest (what they recorded before normalization existed), so\n * an install that predates this change is read correctly without a\n * migration: a file byte-identical or LF-identical to what was recorded is\n * \"untouched\", and only genuinely different content reads as an edit.\n */\nexport async function fileMatchesHash(filePath: string, recordedHash: string): Promise<boolean> {\n const content = await readFile(filePath);\n if (hashContent(content) === recordedHash) return true;\n return createHash(\"sha256\").update(content).digest(\"hex\") === recordedHash;\n}\n\n/**\n * Validates that relativePath stays within targetDir.\n * Throws if the resolved path escapes the target directory boundary.\n */\nexport function validateTargetPath(\n targetDir: string,\n relativePath: string,\n): void {\n const resolvedBase = path.resolve(targetDir);\n const resolvedFull = path.resolve(targetDir, relativePath);\n\n if (\n resolvedFull !== resolvedBase &&\n !resolvedFull.startsWith(resolvedBase + path.sep)\n ) {\n throw new Error(\n `Path traversal detected: \"${relativePath}\" escapes target directory \"${targetDir}\"`,\n );\n }\n}\n\n/**\n * Creates a directory and all parent directories if they don't exist.\n */\nexport async function ensureDir(dirPath: string): Promise<void> {\n await mkdir(dirPath, { recursive: true });\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Copies srcPath into targetDir at relativePath.\n * Validates path traversal before any I/O.\n * Throws if the destination already exists and opts.force is not set.\n * Returns the copied content's SHA-256 hex digest (ARC-038 decision 1) --\n * read once into memory and reused for both the hash and the write, rather\n * than hashing srcPath and then separately copying it: reading every source\n * file twice measurably slowed a full `spell init` (~90 files) once every\n * file started being hashed, enough to push some CI/test runs over their\n * timeout under contention. One read, one write; no redundant I/O.\n */\nexport async function copyFile(\n srcPath: string,\n targetDir: string,\n relativePath: string,\n opts: { force?: boolean } = {},\n): Promise<string> {\n validateTargetPath(targetDir, relativePath);\n\n const destPath = path.resolve(targetDir, relativePath);\n await ensureDir(path.dirname(destPath));\n\n if (!opts.force && (await fileExists(destPath))) {\n throw new Error(\n `Destination already exists: \"${relativePath}\". Use --force to overwrite.`,\n );\n }\n\n const content = await readFile(srcPath);\n const hash = hashContent(content);\n await writeFile(destPath, content);\n return hash;\n}\n\n/**\n * Deletes `relativePath` under `targetDir`, refusing (with the same named\n * error every write path already raises) any path that resolves outside it.\n * The paths this is called with come from `.arcane.json`, which is repository\n * content, so a manifest edited -- or generated by a hostile tool -- to track\n * `../../something` must never reach `rm` (TODO.md, \"two delete paths apply\n * manifest-controlled paths without the traversal guard\"). Missing files are\n * fine (`force`); nothing is ever removed recursively here.\n */\nexport async function removeWithin(targetDir: string, relativePath: string): Promise<void> {\n validateTargetPath(targetDir, relativePath);\n await rm(path.resolve(targetDir, relativePath), { force: true });\n}\n\n/**\n * Removes the directories a just-deleted file leaves behind, walking up from\n * its parent and stopping at the first non-empty one -- or at `targetDir`,\n * which is never removed. `rmdir` on purpose: it removes exactly an empty\n * directory and refuses a non-empty one, so a file that appears between the\n * emptiness check and the removal is never taken with it.\n *\n * Without this, pruning a component's files leaves its skeleton behind: a\n * repository that opts out of carrying spells (ARC-045 decision 4 / CS-05)\n * would keep 41 empty `.agents/skills/<id>/` directories and the empty\n * client folders above them, which git does not track and nothing else\n * cleans up.\n */\nexport async function removeEmptyAncestors(targetDir: string, relativePath: string): Promise<void> {\n validateTargetPath(targetDir, relativePath);\n const base = path.resolve(targetDir);\n let dir = path.dirname(path.resolve(targetDir, relativePath));\n while (dir !== base && dir.startsWith(base + path.sep)) {\n try {\n if ((await readdir(dir)).length > 0) return;\n await rmdir(dir);\n } catch {\n return; // already gone, unreadable, or no longer empty\n }\n dir = path.dirname(dir);\n }\n}\n\nexport interface CopiedFile {\n path: string;\n hash: string;\n}\n\n/**\n * Recursively copies a directory from srcDir to targetDir/relativeDir.\n * Validates the target path to prevent traversal attacks.\n * Returns each copied file's relative path and content hash (ARC-038\n * decision 1), for manifest tracking.\n */\nexport async function copyDirectory(\n srcDir: string,\n targetDir: string,\n relativeDir: string,\n opts: { force?: boolean } = {},\n): Promise<CopiedFile[]> {\n validateTargetPath(targetDir, relativeDir);\n\n const copiedFiles: CopiedFile[] = [];\n const entries = await readdir(srcDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcEntryPath = path.join(srcDir, entry.name);\n const relEntryPath = `${relativeDir}/${entry.name}`;\n\n if (entry.isDirectory()) {\n const subFiles = await copyDirectory(srcEntryPath, targetDir, relEntryPath, opts);\n copiedFiles.push(...subFiles);\n } else {\n const hash = await copyFile(srcEntryPath, targetDir, relEntryPath, opts);\n copiedFiles.push({ path: relEntryPath, hash });\n }\n }\n\n return copiedFiles;\n}\n\n/**\n * Every file under srcDir, as a path relative to targetDir/relativeDir with\n * forward slashes -- the list `copyDirectory` would write, without writing.\n * Lets a plan name each destination before any of them is touched.\n */\nexport async function listDirectoryFiles(srcDir: string, relativeDir: string): Promise<string[]> {\n const files: string[] = [];\n for (const entry of await readdir(srcDir, { withFileTypes: true })) {\n const rel = `${relativeDir}/${entry.name}`;\n if (entry.isDirectory()) files.push(...(await listDirectoryFiles(path.join(srcDir, entry.name), rel)));\n else files.push(rel);\n }\n return files;\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type {\n ArcaneManifest,\n ContentSensitivity,\n ExternalProvider,\n IdeaTags,\n InstallScope,\n InstalledComponent,\n Profile,\n PushPolicy,\n TrackingMode,\n} from \"../types.js\";\n\nexport class ManifestNotFoundError extends Error {\n constructor(manifestPath: string) {\n super(`Manifest not found at \"${manifestPath}\". Run \"spell init\" first.`);\n this.name = \"ManifestNotFoundError\";\n }\n}\n\nexport class ManifestCorruptError extends Error {\n constructor(manifestPath: string) {\n super(\n `Manifest at \"${manifestPath}\" contains invalid JSON. It may be corrupted.`,\n );\n this.name = \"ManifestCorruptError\";\n }\n}\n\n/**\n * Distinct from ManifestCorruptError: the file IS valid JSON, but a field\n * holds a value outside its supported enum (EF-14 point 2 -- \"reject\n * unsupported values rather than silently treating them as a provider\").\n */\nexport class ManifestInvalidFieldError extends Error {\n readonly field: string;\n constructor(manifestPath: string, field: string, value: unknown) {\n super(\n `Manifest at \"${manifestPath}\" has an unsupported value for \"${field}\": ${JSON.stringify(value)}.`,\n );\n this.name = \"ManifestInvalidFieldError\";\n this.field = field;\n }\n}\n\nconst MANIFEST_FILE = \".arcane.json\";\n\nconst VALID_TRACKING_MODES: TrackingMode[] = [\"internal\", \"external\"];\nconst VALID_EXTERNAL_PROVIDERS: ExternalProvider[] = [\"ado\", \"github\", \"jira\", \"other\"];\nconst VALID_CONTENT_SENSITIVITY: ContentSensitivity[] = [\"standard\", \"sensitive\"];\nconst VALID_PUSH_POLICIES: PushPolicy[] = [\"open\", \"guarded\", \"blocked\"];\n\n/** The enum fields' accepted values, for messages that list them (PRD D-02 flags). */\nexport const MANIFEST_ENUM_VALUES: Readonly<Record<string, readonly string[]>> = {\n tracking_mode: VALID_TRACKING_MODES,\n external_provider: VALID_EXTERNAL_PROVIDERS,\n content_sensitivity: VALID_CONTENT_SENSITIVITY,\n push_policy: VALID_PUSH_POLICIES,\n};\nconst VALID_INSTALL_SCOPES: InstallScope[] = [\"repo\", \"user\"];\n\n/**\n * True when a `fanout` key is a home-relative POSIX path that stays inside\n * the home directory: not absolute, not drive-qualified, no backslashes, no\n * `..` segment, one line. `uninstall --user` and `update --user` delete what\n * the record lists (when its hash still matches), so a key must never be\n * able to point that deletion outside the home directory -- the same\n * reasoning `isValidSubjectRoot` applies to a path handed to spells.\n */\nexport function isValidFanoutPath(file: string): boolean {\n return (\n file.length > 0 &&\n !file.startsWith(\"/\") &&\n !/^[a-zA-Z]:/.test(file) &&\n !file.includes(\"\\\\\") &&\n !file.split(\"/\").includes(\"..\") &&\n !/[\\r\\n\\0]/.test(file)\n );\n}\n\n/**\n * True when a `fanout` value is safe to store: a plain object mapping\n * home-relative paths (see isValidFanoutPath) to hex digests (CS-04). The\n * digest is what `update`/`uninstall --user` compare against before deleting\n * or rewriting a file outside the store, so a malformed entry must be\n * rejected here rather than read as \"no record\" (which would make the file\n * look unclaimed) or as a matching hash (which would make it look untouched).\n */\nexport function isValidFanoutRecord(value: unknown): value is Record<string, string> {\n return (\n isRecord(value) &&\n Object.entries(value).every(\n ([file, hash]) =>\n isValidFanoutPath(file) && typeof hash === \"string\" && /^[0-9a-f]{64}$/.test(hash),\n )\n );\n}\n\n/**\n * `subject_root` is a free-form relative path, so it gets shape rules rather\n * than an enum. It is resolved against the repository root and handed to\n * spells, so a value that escapes the repo (absolute, or containing `..`)\n * would point agents at files outside the project entirely. \".\" is explicitly\n * legal: it means the repository root IS the subject tree (EF-07).\n */\n/**\n * True when a subject_root value is safe to store. Exported so the interactive\n * prompt can reject a bad value in place rather than writing it and failing on\n * every subsequent command until the operator hand-edits .arcane.json.\n */\nexport function isValidSubjectRoot(value: string): boolean {\n const invalid =\n value.trim() === \"\" ||\n path.isAbsolute(value) ||\n // Windows drive-relative (\"C:docs\") and UNC-ish values also escape.\n /^[a-zA-Z]:/.test(value) ||\n value.startsWith(\"\\\\\\\\\") ||\n value.startsWith(\"\\\\\") ||\n value.split(/[\\\\/]/).includes(\"..\") ||\n // A stored subject_root is interpolated into agent prompts downstream, so\n // keep it a single path-shaped token rather than a multi-line payload.\n /[\\r\\n\\0]/.test(value);\n return !invalid;\n}\n\nfunction validateSubjectRoot(value: string, filePath: string): void {\n if (!isValidSubjectRoot(value)) {\n throw new ManifestInvalidFieldError(filePath, \"subject_root\", value);\n }\n}\n\n/**\n * True when a secretsScanExcludePrefixes value is safe to store: an array of\n * non-empty strings. Deliberately looser than `isValidSubjectRoot` -- a scan\n * exclusion is a path *prefix* match, not a path handed to spells, so it has\n * no traversal-escape risk to guard against; it only needs to be a real,\n * matchable string.\n */\nexport function isValidSecretsScanExcludePrefixes(value: unknown): value is string[] {\n return (\n Array.isArray(value) &&\n value.every((entry) => typeof entry === \"string\" && entry.length > 0)\n );\n}\n\nfunction validateSecretsScanExcludePrefixes(value: unknown, filePath: string): void {\n if (!isValidSecretsScanExcludePrefixes(value)) {\n throw new ManifestInvalidFieldError(filePath, \"secretsScanExcludePrefixes\", value);\n }\n}\n\n/**\n * True when an `idea_tags` value is safe to store (PRD D-10): a known mode and\n * an array of non-blank strings. Tags are interpolated into IDEAS.md entries,\n * so anything else is rejected rather than guessed at.\n */\nexport function isValidIdeaTags(value: unknown): value is IdeaTags {\n return (\n isRecord(value) &&\n (value[\"mode\"] === \"extend\" || value[\"mode\"] === \"replace\") &&\n Array.isArray(value[\"tags\"]) &&\n value[\"tags\"].every((tag) => typeof tag === \"string\" && tag.trim() !== \"\")\n );\n}\n\nfunction manifestPath(targetDir: string): string {\n return path.join(targetDir, MANIFEST_FILE);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * The field validation readManifest applies, exported so values arriving by\n * another route (the init/update flags, PRD D-02) are held to exactly the\n * same contract. Throws ManifestInvalidFieldError.\n */\nexport function validateManifestFields(manifest: Partial<ArcaneManifest>, filePath: string): void {\n if (\n manifest.tracking_mode !== undefined &&\n !VALID_TRACKING_MODES.includes(manifest.tracking_mode)\n ) {\n throw new ManifestInvalidFieldError(filePath, \"tracking_mode\", manifest.tracking_mode);\n }\n if (\n manifest.external_provider !== undefined &&\n manifest.external_provider !== null &&\n !VALID_EXTERNAL_PROVIDERS.includes(manifest.external_provider)\n ) {\n throw new ManifestInvalidFieldError(filePath, \"external_provider\", manifest.external_provider);\n }\n if (\n manifest.content_sensitivity !== undefined &&\n !VALID_CONTENT_SENSITIVITY.includes(manifest.content_sensitivity)\n ) {\n throw new ManifestInvalidFieldError(\n filePath,\n \"content_sensitivity\",\n manifest.content_sensitivity,\n );\n }\n if (\n manifest.push_policy !== undefined &&\n !VALID_PUSH_POLICIES.includes(manifest.push_policy)\n ) {\n throw new ManifestInvalidFieldError(filePath, \"push_policy\", manifest.push_policy);\n }\n if (manifest.subject_root !== undefined && manifest.subject_root !== null) {\n if (typeof manifest.subject_root !== \"string\") {\n throw new ManifestInvalidFieldError(filePath, \"subject_root\", manifest.subject_root);\n }\n validateSubjectRoot(manifest.subject_root, filePath);\n }\n if (manifest.secretsScanExcludePrefixes !== undefined) {\n validateSecretsScanExcludePrefixes(manifest.secretsScanExcludePrefixes, filePath);\n }\n if (manifest.scope !== undefined && !VALID_INSTALL_SCOPES.includes(manifest.scope)) {\n throw new ManifestInvalidFieldError(filePath, \"scope\", manifest.scope);\n }\n // ARC-045 decision 4 / CS-05. Validated rather than defaulted on a bad\n // value: a typo here would otherwise read as \"repo\" and silently reinstall\n // spells into a repository that meant to opt out.\n if (manifest.spell_scope !== undefined && !VALID_INSTALL_SCOPES.includes(manifest.spell_scope)) {\n throw new ManifestInvalidFieldError(filePath, \"spell_scope\", manifest.spell_scope);\n }\n if (manifest.fanout !== undefined && !isValidFanoutRecord(manifest.fanout)) {\n throw new ManifestInvalidFieldError(filePath, \"fanout\", manifest.fanout);\n }\n if (manifest.idea_tags !== undefined && !isValidIdeaTags(manifest.idea_tags)) {\n throw new ManifestInvalidFieldError(filePath, \"idea_tags\", manifest.idea_tags);\n }\n}\n\n/**\n * Reads .arcane.json from targetDir.\n * Throws ManifestNotFoundError if the file is missing.\n * Throws ManifestCorruptError if the file contains invalid JSON.\n * Throws ManifestInvalidFieldError if tracking_mode/external_provider hold\n * an unsupported value.\n */\nexport async function readManifest(targetDir: string): Promise<ArcaneManifest> {\n const filePath = manifestPath(targetDir);\n let content: string;\n\n try {\n content = await readFile(filePath, \"utf-8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n throw new ManifestNotFoundError(filePath);\n }\n throw err;\n }\n\n let manifest: ArcaneManifest;\n try {\n manifest = JSON.parse(content) as ArcaneManifest;\n } catch {\n throw new ManifestCorruptError(filePath);\n }\n\n validateManifestFields(manifest, filePath);\n return manifest;\n}\n\n/**\n * Resolves the effective `secretsScanExcludePrefixes` list for a target\n * directory (ARC-037). Self-hosted-aware: falls back to reading\n * src/assets/.arcane.json when there is no root .arcane.json, mirroring\n * doctor.ts's own self-hosted-manifest detection (this repo's own case --\n * it ships no root manifest, only the self-hosted marker file). Shared by\n * the build-time scan (scripts/copy-assets.ts) and `spell doctor --leaks`,\n * so a repository configures its exclusions in exactly one place regardless\n * of which path runs the scan.\n */\nexport async function resolveSecretsScanExcludePrefixes(targetDir: string): Promise<string[]> {\n try {\n const manifest = await readManifest(targetDir);\n return manifest.secretsScanExcludePrefixes ?? [];\n } catch {\n // No root manifest -- fall through to the self-hosted case below.\n }\n\n try {\n const raw = await readFile(path.join(targetDir, \"src\", \"assets\", \".arcane.json\"), \"utf-8\");\n const parsed: unknown = JSON.parse(raw);\n if (isRecord(parsed) && parsed[\"selfHosted\"] === true && parsed[\"tracking_mode\"] === \"internal\") {\n const prefixes = parsed[\"secretsScanExcludePrefixes\"];\n if (isValidSecretsScanExcludePrefixes(prefixes)) return prefixes;\n }\n } catch {\n // No self-hosted manifest either -- scan with no exclusions.\n }\n\n return [];\n}\n\n/**\n * Writes the manifest to .arcane.json in targetDir with 2-space indentation,\n * preserving the existing file's line endings and trailing-newline shape.\n *\n * On a `core.autocrlf=true` checkout the working copy is CRLF. Writing LF over\n * it leaves a file git reports as modified with no content change -- and the\n * clean-tree gate in `spell update` then refuses the next run, so Arcane\n * dirties a tree it just demanded be clean. Same class of problem as the\n * line-ending-normalized hashing in copier.ts (1.1.1), solved at the write\n * rather than at the compare.\n *\n * A manifest that does not exist yet keeps today's exact bytes: LF, no\n * trailing newline. The trailing newline is deliberately NOT added\n * unconditionally -- that would dirty every existing consumer's manifest\n * exactly once, reproducing the symptom this fixes.\n */\nexport async function writeManifest(\n targetDir: string,\n manifest: ArcaneManifest,\n): Promise<void> {\n const filePath = manifestPath(targetDir);\n\n // Advisory read: it only tells us how to format bytes we are about to\n // overwrite. Unlike readManifest above, a failure here must never fail a\n // write that would otherwise succeed, so every error degrades to the\n // new-file defaults rather than rethrowing.\n let existing = \"\";\n try {\n existing = await readFile(filePath, \"utf-8\");\n } catch {\n existing = \"\";\n }\n\n const eol = existing.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n const trailingNewline = /\\r?\\n$/.test(existing) ? eol : \"\";\n const body = JSON.stringify(manifest, null, 2).replaceAll(\"\\n\", eol);\n\n await writeFile(filePath, body + trailingNewline, \"utf-8\");\n}\n\n/**\n * Creates a new .arcane.json in targetDir with an empty components array.\n * Returns the created manifest.\n */\nexport async function createManifest(\n targetDir: string,\n profile: Profile,\n version: string,\n): Promise<ArcaneManifest> {\n const manifest: ArcaneManifest = {\n version,\n profile,\n installedAt: new Date().toISOString(),\n components: [],\n };\n await writeManifest(targetDir, manifest);\n return manifest;\n}\n\n/**\n * Pure function — returns a new manifest with the component appended.\n * Does not write to disk.\n */\nexport function addComponent(\n manifest: ArcaneManifest,\n component: InstalledComponent,\n): ArcaneManifest {\n return {\n ...manifest,\n components: [...manifest.components, component],\n };\n}\n\n/**\n * Pure function — returns a new manifest with the named component removed.\n * Does not write to disk.\n */\nexport function removeComponent(\n manifest: ArcaneManifest,\n name: string,\n): ArcaneManifest {\n return {\n ...manifest,\n components: manifest.components.filter((c) => c.name !== name),\n };\n}\n\n/**\n * Returns true if a component with the given name exists in the manifest.\n */\nexport function hasComponent(manifest: ArcaneManifest, name: string): boolean {\n return manifest.components.some((c) => c.name === name);\n}\n","/**\n * Profile configuration — the canonical source of truth for Arcane profiles.\n *\n * A profile bundles a curated set of components for a common use case.\n * Changing which components an EXISTING profile bundles requires only editing\n * this file. Adding a NEW profile id additionally touches the `Profile` union\n * in types.ts, `VALID_PROFILES` and the presentation/gating branches in\n * commands/init.ts, the --profile help string in index.ts, and the retrofit\n * mirror in modules/hub.ts -- the registry itself needs no change.\n *\n * The special value \"*\" for `components` means \"all registered components\"\n * and is expanded by the registry at runtime.\n */\n\nimport type { Profile } from \"../types.js\";\n\nexport interface ProfileConfig {\n id: Profile;\n displayName: string;\n description: string;\n /**\n * Component names included in this profile.\n * Use the special value \"*\" to mean \"all registered components\" (full install).\n */\n components: string[] | \"*\";\n}\n\nexport const PROFILE_CONFIGS: ProfileConfig[] = [\n {\n id: \"full\",\n displayName: \"Full Suite\",\n description: \"All components — spells, governance, agents, templates\",\n components: \"*\",\n },\n {\n id: \"lite\",\n displayName: \"Essentials\",\n description: \"Spells + core governance — fast start\",\n components: [\n \"spells-session\",\n \"spells-capture\",\n \"spells-delivery\",\n \"spells-review\",\n \"spells-planning\",\n \"spells-build\",\n \"spells-venture\",\n \"spells-meta\",\n \"agent-output-instructions\",\n \"git-conventions\",\n \"testing-standards\",\n \"framework-decisions\",\n \"session-continuity\",\n \"line-ending-baseline\",\n ],\n },\n {\n id: \"methodology\",\n displayName: \"Methodology Suite\",\n description: \"Spells + governance — full methodology without security/infra docs\",\n components: [\n \"spells-session\",\n \"spells-capture\",\n \"spells-delivery\",\n \"spells-review\",\n \"spells-planning\",\n \"spells-build\",\n \"spells-venture\",\n \"spells-meta\",\n \"agent-output-instructions\",\n \"git-conventions\",\n \"testing-standards\",\n \"framework-decisions\",\n \"session-continuity\",\n \"decision-documentation-standard\",\n \"agent-work-queue-model\",\n \"development-methodology\",\n \"spell-authoring-standards\",\n \"line-ending-baseline\",\n ],\n },\n {\n id: \"docs\",\n displayName: \"Docs / Records\",\n description: \"Documentation and records repositories — no code, test, or deployment workflows\",\n components: [\n // Deliberate exclusions, per docs-mode PRD MH-01: \"spells-build\"\n // (implementation, tests, stack experts, release, deployment, PRD\n // enchantment, asset tooling), \"spells-review\" (adversarial CODE review\n // -- its own workflow validates test coverage against source, which a\n // docs repo has neither of), and \"spells-venture\" (hub-only).\n //\n // MH-01 requires every retained spell to complete its core workflow\n // without source code, tests, CI, or an external tracker. Asserted --\n // exclusions included, which MH-01 requires explicitly -- in\n // test/docs-profile-registry-split.test.ts.\n \"spells-session\",\n \"spells-capture\",\n \"spells-delivery\",\n \"spells-planning\",\n \"spells-meta\",\n \"spells-docs\",\n \"docs-baseline\",\n \"line-ending-baseline\",\n \"records-conventions\",\n \"agent-output-instructions\",\n \"git-conventions\",\n // Four retained docs spells link to agent-policies; it carries no code\n // implication, so shipping it closes a dangling reference cheaply.\n \"agent-policies\",\n \"framework-decisions\",\n \"session-continuity\",\n \"decision-documentation-standard\",\n \"agent-work-queue-model\",\n \"development-methodology\",\n \"naming-conventions\",\n \"universal-agent-rules\",\n \"spell-authoring-standards\",\n ],\n },\n {\n id: \"governance-only\",\n displayName: \"Governance Only\",\n description: \"Standards docs only — no spells or agents\",\n components: [\n \"agent-output-instructions\",\n \"git-conventions\",\n \"testing-standards\",\n \"framework-decisions\",\n \"decision-documentation-standard\",\n \"agent-work-queue-model\",\n \"naming-conventions\",\n \"agent-policies\",\n \"threat-model\",\n \"hardening-checklist\",\n \"authentication-strategy\",\n \"new-business-setup\",\n \"agent-approved-paths\",\n \"portable-bootstrap\",\n \"development-methodology\",\n \"cicd-standards\",\n \"poc-management-pattern\",\n \"product-excellence-standards\",\n \"rca-process-standard\",\n \"universal-agent-rules\",\n \"spell-authoring-standards\",\n \"external-verification-standards\",\n \"web-discoverability-standards\",\n \"mobile-release-standards\",\n \"records-conventions\",\n \"compliance-standards\",\n \"line-ending-baseline\",\n ],\n },\n];\n","import type { Profile, RegistryComponent, ProfileDefinition, RegistryRetirement } from \"../types.js\";\nimport { PROFILE_CONFIGS } from \"../config/profiles.js\";\n\nexport class ComponentNotFoundError extends Error {\n constructor(name: string, available: string[]) {\n super(\n `Component \"${name}\" not found. Available components: ${available.join(\", \")}`,\n );\n this.name = \"ComponentNotFoundError\";\n }\n}\n\n// ─── Component definitions ───────────────────────────────────────────────────\n// File paths are relative to src/assets/ (source) / dist/assets/ (published).\n\nconst COMPONENTS: RegistryComponent[] = [\n // Governance\n {\n name: \"git-conventions\",\n description: \"Git branching, commit message, and PR conventions\",\n files: [\".arcane/governance/git-conventions.md\"],\n },\n {\n name: \"testing-standards\",\n description: \"Testing framework selection, coverage thresholds, and CI gates\",\n files: [\".arcane/governance/testing-standards.md\"],\n },\n {\n name: \"framework-decisions\",\n description: \"Offline reference for legacy framework ADR citations\",\n files: [\".arcane/governance/framework-decisions.md\"],\n },\n {\n name: \"decision-documentation-standard\",\n description: \"ADR format and decision-logging conventions\",\n files: [\".arcane/governance/decision-documentation-standard.md\"],\n },\n {\n name: \"agent-work-queue-model\",\n description: \"Work queue model for AI agent task assignment and tracking\",\n files: [\".arcane/governance/agent-work-queue-model.md\"],\n },\n {\n name: \"naming-conventions\",\n description: \"Machine, agent, and service naming rules (three-tier model)\",\n files: [\".arcane/governance/naming-conventions.md\"],\n },\n {\n name: \"agent-policies\",\n description: \"Agent autonomy levels, power levels, and operational constraints\",\n files: [\".arcane/governance/agent-policies.md\"],\n },\n {\n name: \"threat-model\",\n description: \"Security threat model covering STRIDE categories and mitigations\",\n files: [\".arcane/governance/threat-model.md\"],\n },\n {\n name: \"hardening-checklist\",\n description: \"Production hardening checklist for infrastructure and agents\",\n files: [\".arcane/governance/hardening-checklist.md\"],\n },\n {\n name: \"authentication-strategy\",\n description: \"Authentication patterns, token management, and identity strategy\",\n files: [\".arcane/governance/authentication-strategy.md\"],\n },\n {\n name: \"new-business-setup\",\n description: \"Step-by-step playbook for bootstrapping a new business unit\",\n files: [\".arcane/governance/new-business-setup.md\"],\n },\n {\n name: \"agent-approved-paths\",\n description: \"Approved repository paths and access boundaries for agents\",\n files: [\".arcane/governance/agent-approved-paths.md\"],\n },\n {\n name: \"portable-bootstrap\",\n description: \"Portable AI context bootstrap for onboarding new AI clients\",\n files: [\".arcane/governance/portable-bootstrap.md\"],\n },\n // Governance — methodology\n {\n name: \"development-methodology\",\n description: \"Spell Loop development methodology\",\n files: [\".arcane/governance/development-methodology.md\"],\n },\n {\n name: \"cicd-standards\",\n description: \"CI/CD pipeline standards and quality gates\",\n files: [\".arcane/governance/cicd-standards.md\"],\n },\n {\n name: \"poc-management-pattern\",\n description: \"POC lifecycle management pattern\",\n files: [\".arcane/governance/poc-management-pattern.md\"],\n },\n {\n name: \"product-excellence-standards\",\n description: \"Product quality gate standards and spell-enchant requirements\",\n files: [\".arcane/governance/product-excellence-standards.md\"],\n },\n {\n name: \"spell-authoring-standards\",\n description: \"Spell Quality Rubric — the authoring bar for spell prompts (8 dimensions)\",\n files: [\".arcane/governance/spell-authoring-standards.md\"],\n },\n {\n name: \"rca-process-standard\",\n description: \"Root cause analysis process standard\",\n files: [\".arcane/governance/rca-process-standard.md\"],\n },\n {\n name: \"universal-agent-rules\",\n description: \"Universal rules for all AI agents operating in any Arcane-managed repo\",\n files: [\".arcane/governance/universal-agent-rules.md\"],\n },\n // VS Code Copilot instructions — installed to .github/instructions/\n {\n name: \"agent-output-instructions\",\n description: \"Copilot enforcement rules: PR link format, merge strategy, and branch naming\",\n files: [\".github/instructions/agent-output.instructions.md\"],\n },\n // Repository baseline files (EF-10 + EF-17). skipExisting: these are\n // user-owned once written -- a repo with its own .gitattributes has an\n // intentional policy Arcane must not overwrite. Sources are stored under a\n // non-dotfile path and mapped via sourceOverrides; see RegistryComponent.\n {\n name: \"docs-baseline\",\n description: \"Ignore baseline for document repositories (.gitignore)\",\n files: [\".gitignore\"],\n sourceOverrides: {\n \".gitignore\": \"docs-baseline/gitignore\",\n },\n skipExisting: true,\n initOnly: true,\n },\n // Split out of docs-baseline so every profile ships it (#281, PRD D-09);\n // same asset source. See COMPONENT_FILE_MOVES for manifests written before.\n {\n name: \"line-ending-baseline\",\n description: \"Line-ending and binary-format baseline (.gitattributes): text normalizes to LF, binary formats are never rewritten\",\n files: [\".gitattributes\"],\n sourceOverrides: {\n \".gitattributes\": \"docs-baseline/gitattributes\",\n },\n skipExisting: true,\n initOnly: true,\n },\n // NOTE: the `mcp-config-template` component (`.mcp.json`, BC-22/I12) was\n // retired on 2026-10-02 -- see REGISTRY_RETIREMENTS. A root .mcp.json is\n // live MCP config that clients execute, and the `full` profile's \"*\" put\n // its example server in every full-profile repo (#328).\n {\n name: \"records-conventions\",\n description: \"Supersession, tombstone, and retention conventions for records repositories\",\n files: [\".arcane/governance/records-conventions.md\"],\n },\n {\n name: \"external-verification-standards\",\n description: \"Rules for confirming a write took effect in a system you don't control — consoles, pipelines, DNS, deployed endpoints\",\n files: [\".arcane/governance/external-verification-standards.md\"],\n },\n {\n name: \"web-discoverability-standards\",\n description: \"Search-engine and AI-assistant discoverability rules for web properties\",\n files: [\".arcane/governance/web-discoverability-standards.md\"],\n },\n {\n name: \"mobile-release-standards\",\n description: \"Durable App Store / Google Play console and release-track facts consumed by spell-eas-store-deploy\",\n files: [\".arcane/governance/mobile-release-standards.md\"],\n },\n {\n name: \"compliance-standards\",\n description: \"GDPR/CCPA/SOC 2/HIPAA obligations and a tiered applicability guide, consumed by spell-compliance\",\n files: [\".arcane/governance/compliance-standards.md\"],\n },\n // ─── Spells ────────────────────────────────────────────────────────────────\n // Split from the former monolithic `spell-prompts` + `claude-commands` pair\n // so a profile can select spells by capability (see ARC-0NN). Grouping lives\n // here and only here -- every spell file stays flat on disk; the one\n // sanctioned relocation is ARC-045's canonical move (CS-03).\n //\n // Each component carries, per spell, the canonical source and ALL THREE\n // generated client shims (ARC-045 / CS-03): the client-neutral source\n // (.arcane/spells/*.md, always listed first), the Copilot prompt shim\n // (.github/prompts/*.prompt.md), the Claude Code command stub\n // (.claude/commands/*.md), and the Codex skill (.agents/skills/*/SKILL.md,\n // CS-01). They are never independently selectable -- every profile that\n // wanted one always wanted the others -- and pairing them here makes it\n // structurally impossible for a spell's source and its shims to drift\n // apart across profiles.\n {\n name: \"spells-session\",\n description: \"Session lifecycle — open, close, commit, status, version\",\n files: [\n \".arcane/spells/spell-open-session.md\",\n \".github/prompts/spell-open-session.prompt.md\",\n \".claude/commands/spell-open-session.md\",\n \".agents/skills/spell-open-session/SKILL.md\",\n \".arcane/spells/spell-close-session.md\",\n \".github/prompts/spell-close-session.prompt.md\",\n \".claude/commands/spell-close-session.md\",\n \".agents/skills/spell-close-session/SKILL.md\",\n \".arcane/spells/spell-commit-work.md\",\n \".github/prompts/spell-commit-work.prompt.md\",\n \".claude/commands/spell-commit-work.md\",\n \".agents/skills/spell-commit-work/SKILL.md\",\n \".arcane/spells/spell-status.md\",\n \".github/prompts/spell-status.prompt.md\",\n \".claude/commands/spell-status.md\",\n \".agents/skills/spell-status/SKILL.md\",\n \".arcane/spells/spell-arcane-version.md\",\n \".github/prompts/spell-arcane-version.prompt.md\",\n \".claude/commands/spell-arcane-version.md\",\n \".agents/skills/spell-arcane-version/SKILL.md\",\n ],\n },\n {\n name: \"spells-capture\",\n description: \"Idea, todo, feedback and explanation capture\",\n files: [\n \".arcane/spells/spell-save-idea.md\",\n \".github/prompts/spell-save-idea.prompt.md\",\n \".claude/commands/spell-save-idea.md\",\n \".agents/skills/spell-save-idea/SKILL.md\",\n \".arcane/spells/spell-todo.md\",\n \".github/prompts/spell-todo.prompt.md\",\n \".claude/commands/spell-todo.md\",\n \".agents/skills/spell-todo/SKILL.md\",\n \".arcane/spells/spell-feedback.md\",\n \".github/prompts/spell-feedback.prompt.md\",\n \".claude/commands/spell-feedback.md\",\n \".agents/skills/spell-feedback/SKILL.md\",\n \".arcane/spells/spell-suggest-feature.md\",\n \".github/prompts/spell-suggest-feature.prompt.md\",\n \".claude/commands/spell-suggest-feature.md\",\n \".agents/skills/spell-suggest-feature/SKILL.md\",\n \".arcane/spells/spell-document.md\",\n \".github/prompts/spell-document.prompt.md\",\n \".claude/commands/spell-document.md\",\n \".agents/skills/spell-document/SKILL.md\",\n \".arcane/spells/spell-verification-ledger.md\",\n \".github/prompts/spell-verification-ledger.prompt.md\",\n \".claude/commands/spell-verification-ledger.md\",\n \".agents/skills/spell-verification-ledger/SKILL.md\",\n \".arcane/spells/spell-brainstorm.md\",\n \".github/prompts/spell-brainstorm.prompt.md\",\n \".claude/commands/spell-brainstorm.md\",\n \".agents/skills/spell-brainstorm/SKILL.md\",\n \".arcane/spells/spell-explain-concept.md\",\n \".github/prompts/spell-explain-concept.prompt.md\",\n \".claude/commands/spell-explain-concept.md\",\n \".agents/skills/spell-explain-concept/SKILL.md\",\n ],\n },\n {\n name: \"spells-delivery\",\n description: \"Pull-request lifecycle — open a PR, sync a stale one, respond to review feedback\",\n files: [\n \".arcane/spells/spell-create-pull-request.md\",\n \".github/prompts/spell-create-pull-request.prompt.md\",\n \".claude/commands/spell-create-pull-request.md\",\n \".agents/skills/spell-create-pull-request/SKILL.md\",\n \".arcane/spells/spell-sync-pull-request.md\",\n \".github/prompts/spell-sync-pull-request.prompt.md\",\n \".claude/commands/spell-sync-pull-request.md\",\n \".agents/skills/spell-sync-pull-request/SKILL.md\",\n \".arcane/spells/spell-address-review.md\",\n \".github/prompts/spell-address-review.prompt.md\",\n \".claude/commands/spell-address-review.md\",\n \".agents/skills/spell-address-review/SKILL.md\",\n ],\n },\n {\n name: \"spells-review\",\n description: \"Adversarial code review — requires source and tests, so excluded from docs-only profiles\",\n files: [\n \".arcane/spells/spell-review.md\",\n \".github/prompts/spell-review.prompt.md\",\n \".claude/commands/spell-review.md\",\n \".agents/skills/spell-review/SKILL.md\",\n \".arcane/spells/spell-review-batch.md\",\n \".github/prompts/spell-review-batch.prompt.md\",\n \".claude/commands/spell-review-batch.md\",\n \".agents/skills/spell-review-batch/SKILL.md\",\n ],\n },\n {\n name: \"spells-planning\",\n description: \"PRD, architecture, scoping and product review\",\n files: [\n \".arcane/spells/spell-plan.md\",\n \".github/prompts/spell-plan.prompt.md\",\n \".claude/commands/spell-plan.md\",\n \".agents/skills/spell-plan/SKILL.md\",\n \".arcane/spells/spell-architect.md\",\n \".github/prompts/spell-architect.prompt.md\",\n \".claude/commands/spell-architect.md\",\n \".agents/skills/spell-architect/SKILL.md\",\n \".arcane/spells/spell-scope.md\",\n \".github/prompts/spell-scope.prompt.md\",\n \".claude/commands/spell-scope.md\",\n \".agents/skills/spell-scope/SKILL.md\",\n \".arcane/spells/spell-product-review.md\",\n \".github/prompts/spell-product-review.prompt.md\",\n \".claude/commands/spell-product-review.md\",\n \".agents/skills/spell-product-review/SKILL.md\",\n ],\n },\n {\n name: \"spells-build\",\n description: \"Code and product delivery — implementation, tests, stack experts, release, deployment, asset tooling\",\n files: [\n \".arcane/spells/spell-implement.md\",\n \".github/prompts/spell-implement.prompt.md\",\n \".claude/commands/spell-implement.md\",\n \".agents/skills/spell-implement/SKILL.md\",\n \".arcane/spells/spell-test.md\",\n \".github/prompts/spell-test.prompt.md\",\n \".claude/commands/spell-test.md\",\n \".agents/skills/spell-test/SKILL.md\",\n \".arcane/spells/spell-full-cycle.md\",\n \".github/prompts/spell-full-cycle.prompt.md\",\n \".claude/commands/spell-full-cycle.md\",\n \".agents/skills/spell-full-cycle/SKILL.md\",\n \".arcane/spells/spell-bug.md\",\n \".github/prompts/spell-bug.prompt.md\",\n \".claude/commands/spell-bug.md\",\n \".agents/skills/spell-bug/SKILL.md\",\n \".arcane/spells/spell-bump.md\",\n \".github/prompts/spell-bump.prompt.md\",\n \".claude/commands/spell-bump.md\",\n \".agents/skills/spell-bump/SKILL.md\",\n \".arcane/spells/spell-dotnet-expert.md\",\n \".github/prompts/spell-dotnet-expert.prompt.md\",\n \".claude/commands/spell-dotnet-expert.md\",\n \".agents/skills/spell-dotnet-expert/SKILL.md\",\n \".arcane/spells/spell-security-review.md\",\n \".github/prompts/spell-security-review.prompt.md\",\n \".claude/commands/spell-security-review.md\",\n \".agents/skills/spell-security-review/SKILL.md\",\n \".arcane/spells/spell-ship.md\",\n \".github/prompts/spell-ship.prompt.md\",\n \".claude/commands/spell-ship.md\",\n \".agents/skills/spell-ship/SKILL.md\",\n \".arcane/spells/spell-enchant.md\",\n \".github/prompts/spell-enchant.prompt.md\",\n \".claude/commands/spell-enchant.md\",\n \".agents/skills/spell-enchant/SKILL.md\",\n \".arcane/spells/spell-generate-bot-icons.md\",\n \".github/prompts/spell-generate-bot-icons.prompt.md\",\n \".claude/commands/spell-generate-bot-icons.md\",\n \".agents/skills/spell-generate-bot-icons/SKILL.md\",\n \".arcane/spells/spell-make-discoverable.md\",\n \".github/prompts/spell-make-discoverable.prompt.md\",\n \".claude/commands/spell-make-discoverable.md\",\n \".agents/skills/spell-make-discoverable/SKILL.md\",\n \".arcane/spells/spell-scry.md\",\n \".github/prompts/spell-scry.prompt.md\",\n \".claude/commands/spell-scry.md\",\n \".agents/skills/spell-scry/SKILL.md\",\n \".arcane/spells/spell-eas-store-deploy.md\",\n \".github/prompts/spell-eas-store-deploy.prompt.md\",\n \".claude/commands/spell-eas-store-deploy.md\",\n \".agents/skills/spell-eas-store-deploy/SKILL.md\",\n \".arcane/spells/spell-compliance.md\",\n \".github/prompts/spell-compliance.prompt.md\",\n \".claude/commands/spell-compliance.md\",\n \".agents/skills/spell-compliance/SKILL.md\",\n ],\n // spell-make-discoverable cites the first two for every EV-/WD- rationale;\n // spell-eas-store-deploy and spell-compliance consume the last two.\n requires: [\n \"external-verification-standards\",\n \"web-discoverability-standards\",\n \"mobile-release-standards\",\n \"compliance-standards\",\n ],\n },\n {\n name: \"spells-docs\",\n description: \"Documentation and records workflows — adopt an existing document tree\",\n files: [\n \".arcane/spells/spell-adopt-docs.md\",\n \".github/prompts/spell-adopt-docs.prompt.md\",\n \".claude/commands/spell-adopt-docs.md\",\n \".agents/skills/spell-adopt-docs/SKILL.md\",\n ],\n },\n {\n name: \"spells-venture\",\n description: \"Hub-only venture management\",\n files: [\n \".arcane/spells/spell-summon-venture.md\",\n \".github/prompts/spell-summon-venture.prompt.md\",\n \".claude/commands/spell-summon-venture.md\",\n \".agents/skills/spell-summon-venture/SKILL.md\",\n \".arcane/spells/spell-manifest.md\",\n \".github/prompts/spell-manifest.prompt.md\",\n \".claude/commands/spell-manifest.md\",\n \".agents/skills/spell-manifest/SKILL.md\",\n ],\n },\n {\n name: \"spells-meta\",\n description: \"Arcane-about-Arcane — presentation and documentation drift detection\",\n files: [\n \".arcane/spells/spell-present-arcane.md\",\n \".github/prompts/spell-present-arcane.prompt.md\",\n \".claude/commands/spell-present-arcane.md\",\n \".agents/skills/spell-present-arcane/SKILL.md\",\n \".arcane/spells/spell-check-drift.md\",\n \".github/prompts/spell-check-drift.prompt.md\",\n \".claude/commands/spell-check-drift.md\",\n \".agents/skills/spell-check-drift/SKILL.md\",\n ],\n },\n // Templates\n {\n name: \"venture-template\",\n description: \"Starter template for a new business venture folder — overview plus idea/todo books\",\n files: [\n \".arcane/templates/venture-template/overview.md\",\n \".arcane/templates/venture-template/IDEAS.md\",\n \".arcane/templates/venture-template/TODO.md\",\n ],\n },\n // Session continuity — files required for spell-close-session / spell-open-session\n {\n name: \"session-continuity\",\n description: \"Install-once project orientation and session lifecycle files\",\n skipExisting: true,\n files: [\n \"README.md\",\n \"project.md\",\n \"TODO.md\",\n \"DECISIONS.md\",\n \"ai-context/system-prompt-context.md\",\n \"journal/.gitkeep\",\n ],\n },\n // Agent definitions\n // NOTE: the full portable agent system is managed by `spell agents init` / sync / list.\n // This component only installs the policy reference template for governance-only setups.\n {\n name: \"agent-definitions\",\n description: \"Agent policy reference template (for governance-only setups — use `spell agents init` for the full agent system)\",\n files: [\".arcane/agents/agent-policies-template.md\"],\n },\n // NOTE: the static `agent-files` component was retired — `.github/agents/*.agent.md`\n // files are generated per-roster by `spell agents init` / `spell agents sync`, so\n // installing a fixed-name set via `spell init` shipped stale names and collided\n // with the generated output.\n];\n\n// ─── Profile map (derived from config) ───────────────────────────────────────\n\nfunction buildProfileMap(): Record<Profile, string[]> {\n const allNames = COMPONENTS.map((c) => c.name);\n const map = {} as Record<Profile, string[]>;\n for (const config of PROFILE_CONFIGS) {\n map[config.id] = config.components === \"*\" ? allNames : config.components;\n }\n return map;\n}\n\nconst PROFILE_MAP = buildProfileMap();\n\n// ─── API ──────────────────────────────────────────────────────────────────────\n\nconst componentIndex = new Map(COMPONENTS.map((c) => [c.name, c]));\n\n/**\n * Returns a component by name.\n * Throws ComponentNotFoundError if not found.\n */\n/** Names of the capability-scoped spell components, derived not hand-listed. */\nexport const SPELL_COMPONENT_NAMES: string[] = COMPONENTS.map((c) => c.name).filter((n) =>\n n.startsWith(\"spells-\"),\n);\n\n/**\n * The exact set of components that reconstitutes what the retired monolithic\n * `spell-prompts` / `claude-commands` pair used to install.\n *\n * **Deliberately a frozen literal, not derived from `SPELL_COMPONENT_NAMES`.**\n * Deriving it would mean every future `spells-*` group silently joins this\n * list, so a legacy install would be handed brand-new spells it never had —\n * and a test asserting `migration === SPELL_COMPONENT_NAMES` would be\n * self-referential, passing while the behaviour changed underneath it. This\n * list describes history: what the old monolith contained, as of the 0.18.0\n * split. It must not grow.\n */\nconst LEGACY_MONOLITH_REPLACEMENTS = [\n \"spells-session\",\n \"spells-capture\",\n \"spells-delivery\",\n \"spells-review\",\n \"spells-planning\",\n \"spells-build\",\n \"spells-venture\",\n \"spells-meta\",\n] as const;\n\n/**\n * Components that no longer exist under their old name, mapped to the set that\n * replaces them.\n *\n * `spell-prompts` and `claude-commands` were one monolithic pair holding all 34\n * spells in each client format. Both map to the same replacements, because\n * those components now carry every format of every spell (the canonical source and its three client shims) — so a manifest\n * listing either legacy name (or both, which every profile that had one did)\n * converges on the same result. Callers must dedupe.\n *\n * Without this, `spell update` would hit `ComponentNotFoundError` for the\n * legacy names, print \"not in registry — skipping\", preserve the dead entry,\n * and silently never update that repo's spells again.\n *\n * Null-prototype so a manifest component named `constructor` or `toString`\n * can't resolve to an inherited function and blow up the lookup.\n */\nexport const LEGACY_COMPONENT_MIGRATIONS: Readonly<Record<string, readonly string[]>> =\n Object.assign(Object.create(null) as Record<string, readonly string[]>, {\n \"spell-prompts\": LEGACY_MONOLITH_REPLACEMENTS,\n \"claude-commands\": LEGACY_MONOLITH_REPLACEMENTS,\n });\n\n/**\n * Spells and components renamed or retired since they first shipped (#279,\n * PRD D-15). Report-only: `spell update` explains an entry a manifest still\n * tracks, and deletes nothing beyond what its orphan handling already does.\n * Describes history, so entries are only ever added.\n */\nexport const REGISTRY_RETIREMENTS: readonly RegistryRetirement[] = [\n {\n name: \"spell-bootstrap-business\",\n kind: \"spell\",\n successors: [\"spell-summon-venture\"],\n reason:\n \"renamed in 0.16.0 with no alias (ARC-008 precedent) — it became a hub-gated venture creator with per-venture books and a registry entry\",\n },\n {\n name: \"spell-assess\",\n kind: \"spell\",\n successors: [\"spell-scope\"],\n reason: \"renamed to name its outcome, epic scoping and sequencing (ARC-007); no alias (ARC-008)\",\n },\n {\n name: \"agent-files\",\n kind: \"component\",\n successors: [],\n reason:\n \"`.github/agents/*.agent.md` files are rendered per roster by `spell agents init` / `spell agents sync` (ARC-047), which a fixed-path component cannot do\",\n },\n {\n name: \"mcp-config-template\",\n kind: \"component\",\n successors: [],\n userOwned: true,\n reason:\n \"retired 2026-10-02 (#328): a root .mcp.json is live MCP config that clients execute, and the `full` profile installed its example server everywhere; set per-server timeouts in your own .mcp.json, which `spell doctor` checks\",\n },\n {\n name: \"spell-prompts\",\n kind: \"component\",\n successors: LEGACY_MONOLITH_REPLACEMENTS,\n reason: \"split in 0.18.0 into capability-scoped spells-* components; `spell update` migrates the entry itself\",\n },\n {\n name: \"claude-commands\",\n kind: \"component\",\n successors: LEGACY_MONOLITH_REPLACEMENTS,\n reason: \"split in 0.18.0 into capability-scoped spells-* components; `spell update` migrates the entry itself\",\n },\n];\n\n/**\n * Files that moved from one live component to another. A manifest written\n * before the move still lists them under `from`, where the registry no longer\n * ships them -- so `spell update` would call them orphans. `update` re-homes\n * them under `to` before doing anything else, keeping their recorded hashes,\n * so a file is neither orphaned nor copied a second time.\n */\nexport const COMPONENT_FILE_MOVES: readonly { from: string; to: string; files: readonly string[] }[] = [\n { from: \"docs-baseline\", to: \"line-ending-baseline\", files: [\".gitattributes\"] },\n];\n\nexport function getComponent(name: string): RegistryComponent {\n const component = componentIndex.get(name);\n if (!component) {\n throw new ComponentNotFoundError(name, Array.from(componentIndex.keys()));\n }\n return component;\n}\n\n/**\n * Returns all components for the given profile.\n */\nexport function getProfile(profile: Profile): RegistryComponent[] {\n return PROFILE_MAP[profile].map((name) => getComponent(name));\n}\n\n/**\n * Returns all registered components.\n */\nexport function getAllComponents(): RegistryComponent[] {\n return [...COMPONENTS];\n}\n\n/**\n * Returns all profile definitions with their component lists expanded.\n * Profiles are sourced from src/config/profiles.ts.\n */\nexport function listProfiles(): ProfileDefinition[] {\n const allNames = COMPONENTS.map((c) => c.name);\n return PROFILE_CONFIGS.map((config) => ({\n id: config.id,\n displayName: config.displayName,\n description: config.description,\n components:\n config.components === \"*\" ? [...allNames] : [...config.components],\n }));\n}\n","/**\n * User tier (ARC-045 decision 3 / CS-04): the per-user Arcane store at\n * `~/.arcane/` and the fan-out of generated client shims into each client's\n * home-directory discovery root.\n *\n * The store is a repo-shaped Arcane target -- it has its own `.arcane.json`\n * and is written by the same copier/manifest/hash machinery a repository is\n * -- that holds canonical spells only (`~/.arcane/spells/<id>.md`). No client\n * reads a repository-relative shim from the store, so none is installed\n * there; instead `syncUserTierFanout()` renders the two user-level shims per\n * spell with an ABSOLUTE canonical path and writes them where the clients\n * already look:\n *\n * ~/.agents/skills/<id>/SKILL.md Codex CLI/extension AND VS Code Copilot\n * ~/.claude/commands/<id>.md Claude Code (personal commands)\n *\n * Copilot is served by the Codex file on purpose: VS Code discovers agent\n * skills from `~/.agents/skills` by default (and from `~/.claude/skills`,\n * which is exactly why the Claude shim is a command and not a skill -- a\n * skill there would list every spell twice in Copilot's picker), and it has\n * deprecated the `chat.*FilesLocations` settings a printed snippet would\n * have pointed at (vendor documentation fetched 2026-09-09; see\n * features/codex-support/architecture.md, CS-04 findings). Nothing in this\n * module reads or writes VS Code settings; `VSCODE_USER_TIER_NOTE` is what\n * the commands print instead of a snippet.\n *\n * The fan-out files live outside any Arcane target directory, so they are\n * written with node:fs directly rather than through `copier.copyFile()`,\n * whose traversal guard exists precisely to forbid that for repository\n * installs -- the shape `agent-generator.ts` already uses for OpenClaw\n * workspaces under `~/.openclaw`. What was written is recorded in the store\n * manifest's `fanout` map (home-relative POSIX path -> SHA-256), and every\n * later run reconciles against that record under ARC-038's rule: a file whose\n * content no longer matches what Arcane wrote is the operator's and is never\n * overwritten or deleted; a file Arcane never recorded is not Arcane's at all\n * and is never claimed.\n */\n\nimport { lstat, mkdir, readdir, readFile, rm, rmdir, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileMatchesHash, hashContent, hashFile, validateTargetPath } from \"./copier.js\";\nimport { SPELL_COMPONENT_NAMES } from \"./registry.js\";\nimport {\n canonicalSpellPath,\n parsePromptFrontmatter,\n renderClaudeCommandStub,\n renderCodexSkill,\n} from \"./spell-compiler.js\";\nimport type { InstallScope, InstalledComponent, RegistryComponent } from \"../types.js\";\n\n// ─── Locations ────────────────────────────────────────────────────────────────\n\n/** Name of the store directory under the home directory. */\nexport const USER_TIER_DIR_NAME = \".arcane\";\n\n/** Directory inside the store that holds the canonical spells. */\nexport const USER_TIER_SPELLS_DIR = \"spells\";\n\n/**\n * The home directory, resolved on every call -- never at module load. Node's\n * `os.homedir()` follows `USERPROFILE` (Windows) / `HOME` (POSIX) at call\n * time, which is what lets tests point the whole tier at a temp directory by\n * setting the environment instead of mocking this function.\n */\nexport function resolveHomeDir(): string {\n return homedir();\n}\n\n/** The store root: `~/.arcane`. Always a direct child of the home directory. */\nexport function userTierRoot(homeDir: string = resolveHomeDir()): string {\n return join(homeDir, USER_TIER_DIR_NAME);\n}\n\n/**\n * The components the user tier installs: every capability-scoped spell\n * component, un-profiled. The tier carries spell delivery content only\n * (ARC-045 decision 5) -- governance, continuity files and repository\n * configuration stay with the repository.\n */\nexport const USER_TIER_COMPONENTS: readonly string[] = SPELL_COMPONENT_NAMES;\n\nconst CANONICAL_SPELL_PATH_PATTERN = /^\\.arcane\\/spells\\/(spell-[a-z0-9-]+)\\.md$/;\nconst STORE_SPELL_PATH_PATTERN = /^spells\\/(spell-[a-z0-9-]+)\\.md$/;\n\n/** Store-relative path of a canonical spell: `spells/<id>.md`. */\nexport function storeSpellPath(id: string): string {\n return `${USER_TIER_SPELLS_DIR}/${id}.md`;\n}\n\n/** The spell id of a registry-relative canonical path (`.arcane/spells/<id>.md`), or undefined. */\nexport function spellIdFromCanonicalPath(path: string): string | undefined {\n return path.replace(/\\\\/g, \"/\").match(CANONICAL_SPELL_PATH_PATTERN)?.[1];\n}\n\n/** The spell id of a store-relative path (`spells/<id>.md`), or undefined. */\nexport function spellIdFromStorePath(path: string): string | undefined {\n return path.replace(/\\\\/g, \"/\").match(STORE_SPELL_PATH_PATTERN)?.[1];\n}\n\n/**\n * The absolute path of a spell in the store, with forward slashes on every\n * platform -- the form Codex accepted verbatim in the CS-04 probe, valid on\n * Windows, and free of backslash escaping inside Markdown and YAML.\n */\nexport function absoluteStoreSpellPath(storeRoot: string, id: string): string {\n return resolve(storeRoot, storeSpellPath(id)).replace(/\\\\/g, \"/\");\n}\n\n// ─── The scope-aware view of the registry ─────────────────────────────────────\n\n/**\n * A component as the given scope installs it. For `\"repo\"` that is the\n * component itself. For `\"user\"` it keeps only the canonical spell files,\n * installs each at `spells/<id>.md` and maps it back to its asset through\n * `sourceOverrides` -- the mechanism the registry already uses for dotfiles\n * -- so `init`, `update` (including the ARC-038 merge, which fetches the\n * previously published file by that asset path) and `uninstall` run their\n * ordinary code over the store. The three repository-relative shims are\n * dropped: nothing reads them from the store, and the user-level shims are\n * rendered by the fan-out instead. `directories`, `skipExisting` and\n * `initOnly` are not carried because no spell component has them.\n */\nexport function componentForScope(\n component: RegistryComponent,\n scope: InstallScope,\n): RegistryComponent {\n if (scope === \"repo\") return component;\n\n const files: string[] = [];\n const sourceOverrides: Record<string, string> = {};\n for (const file of component.files) {\n const id = spellIdFromCanonicalPath(file);\n if (id === undefined) continue;\n const installed = storeSpellPath(id);\n files.push(installed);\n sourceOverrides[installed] = component.sourceOverrides?.[file] ?? file;\n }\n return { name: component.name, description: component.description, files, sourceOverrides };\n}\n\n/**\n * A component as a REPOSITORY installs it under its `spell_scope`\n * (ARC-045 decision 4 / CS-05). Identity for `\"repo\"`, the default and the\n * meaning of every manifest that omits the field. For `\"user\"` a\n * spell-delivering component installs nothing -- the repository takes its\n * spells from the machine-wide store instead -- and every other component is\n * returned untouched, because governance, instructions, continuity files,\n * hooks and templates never move tier (ARC-045 decision 5).\n *\n * Which components deliver spells is derived, never hand-listed: filtering\n * by path prefix instead would put the client-surface shapes in a fourth\n * place beside CLIENT_SHIM_PATHS, CLIENT_SHIM_PATH_PATTERNS and\n * CANONICAL_SPELLS_DIR, and would silently miss a fifth client the day one\n * is added. A test pins that the two definitions -- components named\n * `spells-*`, and components carrying a canonical or shim path -- coincide.\n *\n * Orthogonal to componentForScope above: that one answers \"what does the\n * STORE install\", this one \"what does a REPOSITORY install\". Composed only\n * in the repo scope; the store itself has no spell_scope.\n */\nexport function componentForSpellScope(\n component: RegistryComponent,\n spellScope: InstallScope,\n): RegistryComponent {\n if (spellScope === \"repo\") return component;\n if (!SPELL_COMPONENT_NAMES.includes(component.name)) return component;\n return { ...component, files: [] };\n}\n\n/**\n * A repository's effective spell scope: the manifest's field, or `\"repo\"`\n * when it is absent. One place, so no caller has to remember that absent\n * means repo.\n */\nexport function effectiveSpellScope(manifest: { spell_scope?: InstallScope }): InstallScope {\n return manifest.spell_scope ?? \"repo\";\n}\n\n/**\n * The answer `spell init` should pre-select for its spell-scope question on\n * this machine (ARC-048): the store's `default_spell_scope` when one is set,\n * `\"repo\"` otherwise.\n *\n * Deliberately narrow. This changes which answer is HIGHLIGHTED to someone who\n * is being asked and can decline; it is not a default and it never rewrites an\n * existing repository. A repository whose manifest carries no `spell_scope`\n * still means `\"repo\"` permanently, on every machine, forever -- flipping THAT\n * would retroactively change every repository that predates the field, on a\n * release chain with no step where a human looks.\n *\n * Absent store, unreadable store, or an unrecognized value all resolve to\n * `\"repo\"`: a preference that cannot be read is not a reason to change what\n * someone is offered.\n */\nexport function preferredSpellScope(manifest: { default_spell_scope?: unknown } | null): InstallScope {\n return manifest?.default_spell_scope === \"user\" ? \"user\" : \"repo\";\n}\n/** The spell ids a store manifest's components track, deduplicated and sorted. */\nexport function spellIdsInStore(components: InstalledComponent[]): string[] {\n const ids = new Set<string>();\n for (const component of components) {\n for (const file of component.files) {\n const id = spellIdFromStorePath(file);\n if (id !== undefined) ids.add(id);\n }\n }\n return [...ids].sort();\n}\n\n// ─── The fan-out ──────────────────────────────────────────────────────────────\n\n/**\n * Home-relative directory holding the user tier's rendered agent files.\n *\n * `~/.claude/agents` is read by BOTH clients: VS Code resolves it as the\n * `claude-personal` row of its custom-agent table, and Claude Code reads it as\n * its own user-scope subagent directory. One file therefore serves both, which\n * is the only arrangement that adds Claude Code without also adding a second\n * VS Code picker entry -- the four locations sit in one table with no\n * deduplication (ARC-047, amended).\n */\nexport const USER_AGENT_FANOUT_DIR = \".claude/agents\";\n\n/**\n * Where the user tier's agent files lived before ARC-047 was amended. Still\n * recognized as this client's own, so a `spell agents sync --user` after the\n * upgrade prunes the old copies instead of stranding them -- an unrecognized\n * path is treated as another command's and left alone forever.\n */\nexport const LEGACY_USER_AGENT_FANOUT_DIR = \".copilot/agents\";\n\nexport type FanoutClient = \"codex\" | \"claude\" | \"copilot-agents\";\n\n/**\n * The clients whose fan-out file is keyed by a spell id. The agent client is\n * keyed by a roster slug instead, so it has no entry in `USER_FANOUT_PATHS`.\n */\nexport type SpellFanoutClient = Exclude<FanoutClient, \"copilot-agents\">;\n\n/** Every spell-keyed client, in the order a run renders them. */\nexport const SPELL_FANOUT_CLIENTS: readonly SpellFanoutClient[] = [\"codex\", \"claude\"];\n\n/** Where each client's user-level shim goes, relative to the home directory. */\nexport const USER_FANOUT_PATHS: Readonly<Record<SpellFanoutClient, (id: string) => string>> = {\n codex: (id) => `.agents/skills/${id}/SKILL.md`,\n claude: (id) => `.claude/commands/${id}.md`,\n};\n\n/**\n * Where a user-tier agent file goes, relative to the home directory\n * (CS-06 / ARC-047 decision 3). `~/.copilot/agents` is one of the four custom\n * agent locations VS Code resolves by default, and the only one of them read\n * by VS Code alone -- `~/.claude/agents`, the other home location, is also\n * Claude Code's own user-scope subagent directory, and filling it would make\n * every rostered persona delegatable in every Claude Code session on the\n * machine. That is a new client surface rather than a tier move, so it is\n * deliberately not written here.\n *\n * A plain `.md` suffix, not `.agent.md`: inside an `agents` directory under\n * `.claude`, VS Code registers every `.md` file as an agent, and Claude Code\n * expects `.md` for a subagent. One name both accept.\n */\nexport function userAgentFanoutPath(slug: string): string {\n return `${USER_AGENT_FANOUT_DIR}/${slug}.md`;\n}\n\n/** Human-readable label per client, for command output. */\nexport const FANOUT_CLIENT_LABELS: Readonly<Record<FanoutClient, string>> = {\n codex: \"Codex/Copilot skills\",\n claude: \"Claude Code commands\",\n \"copilot-agents\": \"agent personas (VS Code + Claude Code)\",\n};\n\n/** The client a recorded fan-out path belongs to, or undefined for an unknown shape. */\nexport function clientOfFanoutPath(relativePath: string): FanoutClient | undefined {\n const normalized = relativePath.replace(/\\\\/g, \"/\");\n if (normalized.startsWith(\".agents/skills/\")) return \"codex\";\n if (normalized.startsWith(\".claude/commands/\")) return \"claude\";\n if (normalized.startsWith(`${USER_AGENT_FANOUT_DIR}/`)) return \"copilot-agents\";\n if (normalized.startsWith(`${LEGACY_USER_AGENT_FANOUT_DIR}/`)) return \"copilot-agents\";\n return undefined;\n}\n\nexport interface FanoutFile {\n /** Home-relative POSIX path. */\n relativePath: string;\n client: FanoutClient;\n content: string;\n}\n\nexport interface RenderedFanout {\n files: FanoutFile[];\n /** Spell ids whose source could be read from neither the store nor the fallback. */\n missing: string[];\n}\n\nasync function readSpellSource(\n storeRoot: string,\n id: string,\n fallbackDir: string | undefined,\n): Promise<string | undefined> {\n for (const candidate of [\n join(storeRoot, storeSpellPath(id)),\n ...(fallbackDir ? [join(fallbackDir, canonicalSpellPath(id))] : []),\n ]) {\n try {\n return await readFile(candidate, \"utf-8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n }\n return undefined;\n}\n\n/**\n * Renders the desired fan-out for the given spells: the Codex skill and the\n * Claude command per spell, each pointing at the spell's absolute path in\n * the store. The frontmatter comes from the store copy, or -- when that file\n * is missing and `fallbackDir` (the CLI's assets root) is given -- from the\n * vendor asset the next restore would write, which is what lets a dry run\n * preview the fan-out before anything has been restored. A spell with\n * neither source is reported in `missing`, never thrown on.\n */\nexport async function renderUserFanout(\n storeRoot: string,\n spellIds: string[],\n fallbackDir?: string,\n): Promise<RenderedFanout> {\n const files: FanoutFile[] = [];\n const missing: string[] = [];\n for (const id of [...spellIds].sort()) {\n const canonical = await readSpellSource(storeRoot, id, fallbackDir);\n if (canonical === undefined) {\n missing.push(id);\n continue;\n }\n const frontmatter = parsePromptFrontmatter(canonical);\n const absolutePath = absoluteStoreSpellPath(storeRoot, id);\n files.push({\n relativePath: USER_FANOUT_PATHS.codex(id),\n client: \"codex\",\n content: renderCodexSkill(id, frontmatter, absolutePath),\n });\n files.push({\n relativePath: USER_FANOUT_PATHS.claude(id),\n client: \"claude\",\n content: renderClaudeCommandStub(id, frontmatter, absolutePath),\n });\n }\n return { files, missing };\n}\n\ntype TargetState = \"absent\" | \"file\" | \"other\";\n\n/**\n * What is at a fan-out path, without following links: nothing, a regular\n * file, or something else (a directory, a symlink -- dangling or not -- a\n * device). Only a regular file is ever hashed, rewritten or deleted; anything\n * else is left exactly as found. `lstat` rather than `access`/`stat` so a\n * symlink an operator planted at the path is seen as the link, never written\n * through to wherever it points.\n */\nasync function classifyTarget(path: string): Promise<TargetState> {\n try {\n const stats = await lstat(path);\n return stats.isFile() ? \"file\" : \"other\";\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return \"absent\";\n throw err;\n }\n}\n\nexport type FanoutStatus =\n /** Created, or rewritten because the rendered content changed. */\n | \"written\"\n /** On disk, hash-matched, content current. */\n | \"unchanged\"\n /** Recorded, but the operator edited it: kept byte-untouched, recorded hash carried forward. */\n | \"customized\"\n /** A same-named file Arcane never recorded, or something that is not a regular file: kept, warned, not claimed. */\n | \"collision\"\n /** No longer desired, hash-matched: deleted (and its per-spell directory, once empty). */\n | \"pruned\"\n /** No longer desired, but edited since Arcane wrote it (or no longer a regular file): kept, still recorded. */\n | \"kept-edited\"\n /** No longer desired and already gone from disk: record dropped. */\n | \"gone\"\n /** Desired, but the spell's source is missing from the store and the fallback: nothing written or deleted, record carried forward. */\n | \"unrenderable\";\n\nexport interface FanoutOutcome {\n relativePath: string;\n status: FanoutStatus;\n}\n\nexport interface FanoutSyncResult {\n /** The record to persist as the manifest's `fanout` field. */\n record: Record<string, string>;\n outcomes: FanoutOutcome[];\n}\n\nexport interface FanoutSyncOptions {\n /** The home directory the fan-out paths are relative to (the store's parent). */\n homeDir: string;\n storeRoot: string;\n /** Spells that should have fan-out files after this run; empty on uninstall. */\n spellIds: string[];\n /** The manifest's current `fanout` record, if any. */\n previous?: Record<string, string>;\n dryRun?: boolean;\n /** The CLI's assets root: renders a spell whose store copy is missing from the vendor asset instead (see renderUserFanout). */\n fallbackDir?: string;\n /**\n * Already-rendered fan-out files to reconcile alongside the spell files --\n * the user tier's agent files, which come from a roster rather than from a\n * spell id (CS-06).\n */\n extraFiles?: readonly FanoutFile[];\n /**\n * The clients this run owns. Recorded paths belonging to any other client\n * are carried forward untouched instead of being pruned, so two commands\n * can share one `fanout` record without each erasing the other's entries:\n * `spell update --user` owns the spell clients, `spell agents sync --user`\n * owns the agent client. Defaults to every client.\n */\n ownedClients?: readonly FanoutClient[];\n}\n\n/**\n * The per-spell directory a recorded path lives in, when its shape has one\n * (`.agents/skills/<id>/SKILL.md` -> `.agents/skills/<id>`). The Claude shape\n * has none: `.claude/commands/` is the client's own directory and is never\n * removed, even when Arcane's file was the last thing in it.\n */\nfunction perSpellDirectory(homeDir: string, relativePath: string): string | undefined {\n if (clientOfFanoutPath(relativePath) !== \"codex\") return undefined;\n return dirname(join(homeDir, relativePath));\n}\n\n/**\n * Removes `dir` only when it exists and is empty; silent otherwise. `rmdir`\n * on purpose, not `rm`: it removes exactly an empty directory and refuses a\n * non-empty one, so a file that appeared between the check and the removal\n * is never taken with it.\n */\nexport async function removeDirectoryIfEmpty(dir: string): Promise<void> {\n try {\n const entries = await readdir(dir);\n if (entries.length === 0) await rmdir(dir);\n } catch {\n // Already gone, not a directory, or no longer empty -- nothing to remove.\n }\n}\n\n/**\n * Reconciles the desired fan-out against what the manifest recorded, writing\n * only what is safe to write and deleting only what is safe to delete:\n *\n * | on disk | recorded | action |\n * |-----------------------------|----------|----------------------------------------------|\n * | absent | – | write, record |\n * | present, hash == recorded | yes | rewrite only if the rendered content changed |\n * | present, hash != recorded | yes | keep, \"customized\", carry the hash forward |\n * | present | no | keep, \"collision\", never record |\n * | recorded, no longer desired | yes | delete only if hash == recorded; else keep |\n *\n * Dry-run performs no I/O beyond reading and reports the same decisions.\n */\nexport async function syncUserTierFanout(options: FanoutSyncOptions): Promise<FanoutSyncResult> {\n const rendered = await renderUserFanout(options.storeRoot, options.spellIds, options.fallbackDir);\n const desired = [...rendered.files, ...(options.extraFiles ?? [])];\n const owned = options.ownedClients;\n const ownsClient = (client: FanoutClient | undefined): boolean =>\n owned === undefined || (client !== undefined && owned.includes(client));\n const previous = options.previous ?? {};\n const dryRun = Boolean(options.dryRun);\n const record: Record<string, string> = {};\n const outcomes: FanoutOutcome[] = [];\n const desiredPaths = new Set(desired.map((f) => f.relativePath));\n\n // A spell whose source is gone from both the store and this CLI's assets\n // (a component the registry no longer ships, whose store file --prune\n // already removed) cannot be rendered. Its client files are neither\n // rewritten nor pruned -- they still point at a spell the operator may\n // restore -- and stay recorded so a later run recognizes them.\n for (const id of rendered.missing) {\n for (const client of SPELL_FANOUT_CLIENTS) {\n const relativePath = USER_FANOUT_PATHS[client](id);\n desiredPaths.add(relativePath);\n if (previous[relativePath] !== undefined) record[relativePath] = previous[relativePath];\n outcomes.push({ relativePath, status: \"unrenderable\" });\n }\n }\n\n for (const file of desired) {\n // Template-derived, so this cannot fail -- kept as the same invariant the\n // prune loop below enforces on recorded keys.\n validateTargetPath(options.homeDir, file.relativePath);\n const target = join(options.homeDir, file.relativePath);\n const recorded = previous[file.relativePath];\n const newHash = hashContent(file.content);\n const state = await classifyTarget(target);\n\n if (state === \"absent\") {\n if (!dryRun) {\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, file.content, \"utf-8\");\n }\n record[file.relativePath] = newHash;\n outcomes.push({ relativePath: file.relativePath, status: \"written\" });\n continue;\n }\n\n if (state === \"other\" || recorded === undefined) {\n // Not ours: an operator's own skill or command that happens to share\n // the spell's name, or a directory or symlink where a file would go.\n // Recording it would claim it, and `uninstall --user` deletes what the\n // record lists; writing through a symlink would land wherever it points.\n outcomes.push({ relativePath: file.relativePath, status: \"collision\" });\n continue;\n }\n\n if (!(await fileMatchesHash(target, recorded))) {\n record[file.relativePath] = recorded;\n outcomes.push({ relativePath: file.relativePath, status: \"customized\" });\n continue;\n }\n const currentHash = await hashFile(target);\n if (currentHash === newHash) {\n record[file.relativePath] = newHash;\n outcomes.push({ relativePath: file.relativePath, status: \"unchanged\" });\n continue;\n }\n if (!dryRun) await writeFile(target, file.content, \"utf-8\");\n record[file.relativePath] = newHash;\n outcomes.push({ relativePath: file.relativePath, status: \"written\" });\n }\n\n for (const [relativePath, recorded] of Object.entries(previous)) {\n if (desiredPaths.has(relativePath)) continue;\n // Another command's client: not this run's to prune, and dropping it from\n // `record` would untrack a file Arcane wrote, which `uninstall --user`\n // would then leak -- the same hazard the orphan path guards in `update`.\n if (!ownsClient(clientOfFanoutPath(relativePath))) {\n record[relativePath] = recorded;\n continue;\n }\n // Recorded keys are validated when the manifest is read\n // (isValidFanoutPath); this is the module's own guard, so a caller that\n // hands over an unvalidated record still cannot delete outside home.\n validateTargetPath(options.homeDir, relativePath);\n const target = join(options.homeDir, relativePath);\n const state = await classifyTarget(target);\n if (state === \"absent\") {\n outcomes.push({ relativePath, status: \"gone\" });\n continue;\n }\n // Not a regular file any more (a directory or a link now sits there), or\n // edited since Arcane wrote it: never deleted, still recorded.\n if (state === \"other\" || !(await fileMatchesHash(target, recorded))) {\n record[relativePath] = recorded;\n outcomes.push({ relativePath, status: \"kept-edited\" });\n continue;\n }\n if (!dryRun) {\n await rm(target, { force: true });\n const spellDir = perSpellDirectory(options.homeDir, relativePath);\n if (spellDir !== undefined) await removeDirectoryIfEmpty(spellDir);\n }\n outcomes.push({ relativePath, status: \"pruned\" });\n }\n\n return { record, outcomes };\n}\n\n/** Counts outcomes by status, for one-line summaries. */\nexport function summarizeFanout(outcomes: FanoutOutcome[]): Record<FanoutStatus, number> {\n const counts: Record<FanoutStatus, number> = {\n written: 0,\n unchanged: 0,\n customized: 0,\n collision: 0,\n pruned: 0,\n \"kept-edited\": 0,\n gone: 0,\n unrenderable: 0,\n };\n for (const outcome of outcomes) counts[outcome.status]++;\n return counts;\n}\n\n/**\n * Lines describing a sync's outcomes for command output -- one summary line\n * for what was written or pruned, then a warning block per state the\n * operator needs to know about (customized, collision, kept-edited). Files\n * that were unchanged or already gone produce no line. Pure: the commands\n * print these.\n */\nexport function describeFanoutOutcomes(outcomes: FanoutOutcome[], dryRun = false): string[] {\n const counts = summarizeFanout(outcomes);\n const lines: string[] = [];\n const prefix = dryRun ? \"[dry-run] Would \" : \"\";\n const byClient = (status: FanoutStatus): Record<FanoutClient, number> => {\n const n: Record<FanoutClient, number> = { codex: 0, claude: 0, \"copilot-agents\": 0 };\n for (const o of outcomes) {\n if (o.status !== status) continue;\n const client = clientOfFanoutPath(o.relativePath);\n if (client !== undefined) n[client]++;\n }\n return n;\n };\n const paths = (status: FanoutStatus): string =>\n outcomes\n .filter((o) => o.status === status)\n .map((o) => ` ~/${o.relativePath}`)\n .join(\"\\n\");\n\n if (counts.written > 0) {\n const n = byClient(\"written\");\n // Only the clients this run actually wrote for: a spell run would\n // otherwise report \"0 Copilot agent modes\" and an agent run \"0 Codex\n // skills, 0 Claude Code commands\", which is how a correct run came to\n // read as a broken one.\n const breakdown = (Object.keys(n) as FanoutClient[])\n .filter((client) => n[client] > 0)\n .map((client) => `${n[client]} ${FANOUT_CLIENT_LABELS[client]}`)\n .join(\", \");\n lines.push(\n `${prefix}${dryRun ? \"write\" : \"Wrote\"} ${counts.written} client file(s): ${breakdown}.`,\n );\n }\n if (counts.pruned > 0) {\n lines.push(`${prefix}${dryRun ? \"remove\" : \"Removed\"} ${counts.pruned} client file(s) no longer needed.`);\n }\n if (counts.customized > 0) {\n lines.push(\n `! ${dryRun ? \"Would keep\" : \"Kept\"} ${counts.customized} customized client file(s) untouched (edited since Arcane wrote them):\\n${paths(\"customized\")}\\n Delete one to have it regenerated by the next \\`spell update --user\\`.`,\n );\n }\n if (counts.collision > 0) {\n lines.push(\n `! ${dryRun ? \"Would leave\" : \"Left\"} ${counts.collision} existing path(s) alone (not written by Arcane — a skill or command of the same name, a directory or a link already exists there):\\n${paths(\"collision\")}\\n Move or rename it and run \\`spell update --user\\` to install Arcane's.`,\n );\n }\n if (counts.unrenderable > 0) {\n lines.push(\n `! ${dryRun ? \"Would keep\" : \"Kept\"} ${counts.unrenderable} client file(s) whose spell source is missing from the store and from this CLI (left as they are):\\n${paths(\"unrenderable\")}\\n Restore the spell with \\`spell update --user\\`, or delete these files if the spell is no longer shipped.`,\n );\n }\n if (counts[\"kept-edited\"] > 0) {\n lines.push(\n `! ${dryRun ? \"Would keep\" : \"Kept\"} ${counts[\"kept-edited\"]} edited client file(s) that Arcane no longer needs (delete them yourself if unwanted):\\n${paths(\"kept-edited\")}`,\n );\n }\n return lines;\n}\n\nexport interface FanoutHealth {\n /** Recorded files. */\n total: number;\n byClient: Record<FanoutClient, number>;\n /** Recorded files no longer on disk. */\n missing: string[];\n /** Recorded files whose content no longer matches what Arcane wrote. */\n customized: string[];\n}\n\n/**\n * Read-only view of the recorded fan-out for `status` and `doctor`: how many\n * files each client has, and which are missing or edited. Never writes.\n */\nexport async function inspectUserTierFanout(\n homeDir: string,\n record: Record<string, string> | undefined,\n): Promise<FanoutHealth> {\n const health: FanoutHealth = {\n total: 0,\n byClient: { codex: 0, claude: 0, \"copilot-agents\": 0 },\n missing: [],\n customized: [],\n };\n for (const [relativePath, recorded] of Object.entries(record ?? {})) {\n health.total++;\n const client = clientOfFanoutPath(relativePath);\n if (client !== undefined) health.byClient[client]++;\n const target = join(homeDir, relativePath);\n const state = await classifyTarget(target);\n if (state === \"absent\") {\n health.missing.push(relativePath);\n continue;\n }\n // A directory or link where Arcane's file was is \"not what Arcane wrote\",\n // the same as an edit -- and never something to hash or touch.\n if (state === \"other\" || !(await fileMatchesHash(target, recorded))) health.customized.push(relativePath);\n }\n return health;\n}\n\n/**\n * Whether `targetDir` is this machine's store, `~/.arcane`. A manifest with\n * `scope: \"user\"` found anywhere else -- a backup copy, a moved directory --\n * must not be operated on as the user tier without `--user`: the fan-out is\n * anchored at the store's parent, and that parent would be the wrong home.\n */\nexport function isUserTierStore(targetDir: string): boolean {\n return resolve(targetDir) === resolve(userTierRoot());\n}\n\n/**\n * Resolves which tier a command is operating on. `--user` is authoritative\n * (the CLI entry resolved `targetDir` to the store). Without it, a manifest\n * declaring `scope: \"user\"` is honored only when `targetDir` IS the store;\n * anywhere else the caller must refuse rather than fan out relative to the\n * wrong directory.\n */\nexport function resolveInstallScope(\n targetDir: string,\n manifestScope: InstallScope | undefined,\n userFlag: boolean | undefined,\n): { scope: InstallScope; misplacedUserManifest: boolean } {\n if (userFlag) return { scope: \"user\", misplacedUserManifest: false };\n if (manifestScope !== \"user\") return { scope: \"repo\", misplacedUserManifest: false };\n return isUserTierStore(targetDir)\n ? { scope: \"user\", misplacedUserManifest: false }\n : { scope: \"repo\", misplacedUserManifest: true };\n}\n\n/** The message a command prints before exiting when it finds a user-tier manifest outside the store. */\nexport function misplacedUserManifestMessage(targetDir: string): string {\n return (\n `This .arcane.json belongs to the user tier (scope: \"user\"), but ${targetDir} is not the store (${userTierRoot()}). ` +\n \"Run the command with --user to operate on the store, or from the store directory itself.\"\n );\n}\n\n// ─── What the commands print about VS Code ────────────────────────────────────\n\n/**\n * Printed by `init --user` and `status --user`; never applied. ARC-045\n * decision 3 asked for printed, never auto-applied VS Code guidance; the\n * guidance that turned out to be true is that no setting is needed.\n */\nexport const VSCODE_USER_TIER_NOTE: readonly string[] = [\n \"VS Code Copilot: no settings change is needed — Copilot discovers ~/.agents/skills by default\",\n \"(chat.useAgentSkills, on by default) and lists each spell under /. Reload the window after installing.\",\n \"VS Code has deprecated chat.promptFilesLocations; the user tier does not use it.\",\n];\n\n/**\n * Printed with the note: Claude Code's documented precedence rule, which is\n * the one behavior change an operator sees in repositories that still carry\n * their own shims until CS-05's repo opt-out.\n */\nexport const CLAUDE_PRECEDENCE_NOTE =\n \"Claude Code runs a personal command over a project command of the same name, so /spell-* in a repository that still carries its own shims now runs the user tier's copy.\";\n","/**\n * Spell compiler — build-time generation for spell client shims and shared\n * prose fragments (ARC-039 / BC-32, extended by ARC-045 / CS-03).\n *\n * `.arcane/spells/<id>.md` is each spell's sole authored source: client-neutral,\n * carrying every client's hint fields in one frontmatter block. Three\n * renderers produce the client surfaces from it, and none of them carries\n * prose of its own -- `renderCopilotPromptShim()` (`.github/prompts/\n * <id>.prompt.md`), `renderClaudeCommandStub()` (`.claude/commands/<id>.md`)\n * and `renderCodexSkill()` (`.agents/skills/<id>/SKILL.md`) -- the same\n * one-source/multiple-render() shape `agent-generator.ts` established,\n * applied to a second content class. `expandFragment()` inlines a named,\n * shared prose fragment into a marked span of a consuming spell, so genuinely\n * duplicated text (today: the tracking-mode declaration lines) has one edited\n * home instead of N independently-drifting copies.\n */\n\nimport { parse as parseYaml } from \"yaml\";\n\nexport interface PromptFrontmatter {\n name: string;\n description: string;\n claudeDescription?: string;\n}\n\nexport class MissingFrontmatterError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"MissingFrontmatterError\";\n }\n}\n\nexport class MalformedFragmentMarkersError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"MalformedFragmentMarkersError\";\n }\n}\n\nexport class InvalidSkillNameError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"InvalidSkillNameError\";\n }\n}\n\n// ─── The path contract (ARC-045 decisions 1-2) ────────────────────────────────\n// Everything that needs to know where a spell's authored content lives, or\n// where its generated client shims go, resolves it here -- the parity script,\n// the registry tests, and `spell update`'s migration logic alike. The shim\n// path GENERATORS (CLIENT_SHIM_PATHS) and the shim path MATCHER\n// (CLIENT_SHIM_PATH_PATTERNS) live side by side and are bound to each other\n// by test, so a renamed shape cannot drift out of the matcher unnoticed.\n\n/** Directory (relative to the assets root / a consumer's repo root) holding the canonical spell sources. */\nexport const CANONICAL_SPELLS_DIR = \".arcane/spells\";\n\n/** The one authored file for a spell: `.arcane/spells/<id>.md`. */\nexport function canonicalSpellPath(id: string): string {\n return `${CANONICAL_SPELLS_DIR}/${id}.md`;\n}\n\n/** Where each client's generated shim for a spell lives, one generator per client surface. */\nexport const CLIENT_SHIM_PATHS = {\n copilot: (id: string): string => `.github/prompts/${id}.prompt.md`,\n claude: (id: string): string => `.claude/commands/${id}.md`,\n codex: (id: string): string => `.agents/skills/${id}/SKILL.md`,\n} as const;\n\n/**\n * The three generated client-shim shapes, one per client surface -- the\n * matcher counterpart of CLIENT_SHIM_PATHS. A path matching any of these\n * carries no authored prose -- it is `render()` output over the canonical\n * file -- which is exactly why `spell update` must never three-way-merge an\n * operator's edit *into* one (see update.ts).\n */\nexport const CLIENT_SHIM_PATH_PATTERNS: readonly RegExp[] = [\n /^\\.github\\/prompts\\/spell-[a-z0-9-]+\\.prompt\\.md$/,\n /^\\.claude\\/commands\\/spell-[a-z0-9-]+\\.md$/,\n /^\\.agents\\/skills\\/spell-[a-z0-9-]+\\/SKILL\\.md$/,\n];\n\n/** True when `path` (registry-relative, either slash style) is a generated client shim. */\nexport function isClientShimPath(path: string): boolean {\n const normalized = path.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\");\n return CLIENT_SHIM_PATH_PATTERNS.some((pattern) => pattern.test(normalized));\n}\n\nconst FRONTMATTER_PATTERN = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/;\n\n/**\n * Parses a spell's YAML frontmatter block. Requires `name` and\n * `description`; `claude_description` (the Claude Code stub's own\n * proactive-invocation hint -- distinct from `description`, see\n * renderClaudeCommandStub) is optional so a spell authored before that\n * field existed still parses, falling back to `description` at render time.\n */\nexport function parsePromptFrontmatter(content: string): PromptFrontmatter {\n const match = content.match(FRONTMATTER_PATTERN);\n if (!match) {\n throw new MissingFrontmatterError(\n \"Prompt file has no YAML frontmatter block (expected a leading --- ... --- block).\",\n );\n }\n\n const raw = parseYaml(match[1]) as Record<string, unknown> | null;\n const name = raw?.[\"name\"];\n const description = raw?.[\"description\"];\n\n if (typeof name !== \"string\" || name.trim() === \"\") {\n throw new MissingFrontmatterError('Prompt frontmatter is missing a \"name\" field.');\n }\n if (typeof description !== \"string\" || description.trim() === \"\") {\n throw new MissingFrontmatterError('Prompt frontmatter is missing a \"description\" field.');\n }\n\n const claudeDescription = raw?.[\"claude_description\"];\n\n return {\n name,\n description,\n claudeDescription: typeof claudeDescription === \"string\" ? claudeDescription : undefined,\n };\n}\n\n/**\n * Returns a spell's complete frontmatter block -- the opening `---`, every\n * field line, the closing `---` and its newline -- normalized to LF. The\n * Copilot shim copies this block verbatim (see renderCopilotPromptShim), so\n * it is extracted as text rather than re-serialized from the parsed object:\n * YAML round-tripping could reorder or requote fields, and the point is that\n * Copilot sees exactly the block it saw before the canonical move.\n */\nexport function extractFrontmatterBlock(content: string): string {\n const match = content.match(FRONTMATTER_PATTERN);\n if (!match) {\n throw new MissingFrontmatterError(\n \"Prompt file has no YAML frontmatter block (expected a leading --- ... --- block).\",\n );\n }\n const block = match[0].replace(/\\r\\n/g, \"\\n\");\n return block.endsWith(\"\\n\") ? block : `${block}\\n`;\n}\n\nconst SPELL_NAME_PREFIX = /^Spell\\s*—\\s*/;\n\n/** Derives a `.claude/commands/` stub title from a prompt's `name` field (\"Spell — X\" -> \"X\"). */\nexport function deriveStubTitle(name: string): string {\n return name.replace(SPELL_NAME_PREFIX, \"\").trim();\n}\n\n/**\n * Renders the `.github/prompts/{id}.prompt.md` shim for VS Code Copilot\n * (ARC-045 decision 2 / CS-03). The frontmatter is the canonical file's own\n * block, byte-for-byte: VS Code reads `description`, `agent`, `argument-hint`\n * (and `name`, `model`, `tools`) from it, and copying the block whole means\n * the picker sees exactly the fields the full prompt carried before the\n * move. The body is one paragraph carrying both reference mechanisms VS\n * Code's prompt-file documentation admits: a relative Markdown link (resolved\n * from the prompt file's own location -- `.github/prompts/` is two levels\n * below the repo root, hence `../../`) for a client that attaches linked\n * files as context, and the explicit read-and-follow sentence proven live\n * against Codex for one that does not. Every spell runs `agent: agent`, so\n * the model always has a file-read tool for the second path.\n */\nexport function renderCopilotPromptShim(id: string, canonicalContent: string): string {\n // Validate before rendering: a canonical file the parser rejects must fail\n // the build here, not ship an unparseable shim.\n parsePromptFrontmatter(canonicalContent);\n const frontmatter = extractFrontmatterBlock(canonicalContent);\n const path = canonicalSpellPath(id);\n\n return `${frontmatter}\nThis prompt is the Arcane \\`${id}\\` spell. Read [\\`${path}\\`](../../${path}) and follow it as the complete workflow.\n`;\n}\n\n/**\n * Renders a `.claude/commands/{id}.md` thin-shim stub from its canonical\n * source's frontmatter. The stub's own `description` is a Claude\n * Code-specific proactive-invocation hint -- prefer `claudeDescription`\n * when the source provides one; only a spell with no such field yet falls\n * back to the plainer `description` (which reads correctly, just without\n * the \"use PROACTIVELY\" framing that encourages unprompted invocation).\n * The `@` include is Claude Code's own mechanism for splicing a repo-relative\n * file into the command, unchanged by CS-03 -- only its target moved.\n * `canonicalRef` defaults to the repository-relative canonical path; the\n * user tier (CS-04) passes the ABSOLUTE path of the spell in `~/.arcane`,\n * since a personal command in `~/.claude/commands/` runs from any working\n * directory and a relative path would resolve against the wrong root.\n */\nexport function renderClaudeCommandStub(\n id: string,\n frontmatter: PromptFrontmatter,\n canonicalRef: string = canonicalSpellPath(id),\n): string {\n const title = deriveStubTitle(frontmatter.name);\n const description = frontmatter.claudeDescription ?? frontmatter.description;\n const promptPath = canonicalRef;\n // Claude Code's documented form for a path with whitespace is\n // `@\"path with spaces.md\"`; the repository-relative canonical path never\n // has any, so the common case stays byte-identical to what shipped in 1.0.0.\n const includeRef = /\\s/.test(promptPath) ? `\"${promptPath}\"` : promptPath;\n\n return `---\ndescription: ${description}\n---\n\n# ${title}\n\nInvoke the Arcane \\`${id}\\` spell workflow.\n\nSee the full prompt at \\`${promptPath}\\` for the complete workflow definition.\n\n---\n\n@${includeRef}\n`;\n}\n\nconst SKILL_NAME_PATTERN = /^[a-z0-9-]+$/;\n\n/**\n * Renders a `.agents/skills/{id}/SKILL.md` for OpenAI Codex (CS-01 / ARC-039\n * extension). Unlike the Claude stub, this is not an `@include` -- Codex has\n * no such directive -- so the body is a plain-language instruction to read\n * and follow the canonical file. Live-tested against installed\n * `codex-cli 0.153.4` (2026-09-09, `docs/research/skill-discovery-smoke-tests.md`):\n * a skill body naming a bare repository-relative path is read via a real\n * shell command and its instructions followed verbatim, so this is a proven\n * mechanism, not an assumption. `canonicalPath` is `canonicalSpellPath(id)`\n * since CS-03 (it was the Copilot prompt's path under CS-01); the parameter\n * stays explicit so the rendered text and the path contract are checked\n * against each other in tests rather than assumed to agree.\n *\n * No description-length guard: Codex already degrades gracefully on an\n * overlong description (observed truncating it under a \"skills context\n * budget\" rather than erroring), so throwing our own error for something the\n * consumer already tolerates would be enforcing a limit nobody has actually\n * hit or documented.\n */\nexport function renderCodexSkill(\n id: string,\n frontmatter: PromptFrontmatter,\n canonicalPath: string,\n): string {\n if (!SKILL_NAME_PATTERN.test(id)) {\n throw new InvalidSkillNameError(\n `Skill id \"${id}\" must match ${SKILL_NAME_PATTERN} (lowercase letters, digits, hyphens only).`,\n );\n }\n const description = frontmatter.claudeDescription ?? frontmatter.description;\n\n return `---\nname: ${id}\ndescription: ${description}\n---\n\nThis skill is the Arcane \\`${id}\\` spell. Read \\`${canonicalPath}\\` and follow it as the complete workflow.\n`;\n}\n\nfunction fragmentMarkers(name: string): { start: string; end: string } {\n return {\n start: `<!-- fragment:${name}:start -->`,\n end: `<!-- fragment:${name}:end -->`,\n };\n}\n\n/**\n * Expands a named fragment into `content`'s marked span, replacing\n * whatever currently sits between `<!-- fragment:{name}:start -->` and\n * `<!-- fragment:{name}:end -->` with `fragmentContent` (the markers\n * themselves are always preserved, so the span stays re-expandable --\n * the same idempotent, always-owned-content model `merger.ts` uses for\n * CLAUDE.md's routing-table section, generalized to a *named* marker so\n * one file can host more than one distinct fragment).\n *\n * The injected lines are re-indented to match the start marker's own\n * leading whitespace, since consuming prompts nest the marker at\n * different list depths (a bare-column-0 splice would dedent a fragment\n * out of its surrounding bullet list). Author both markers at the same\n * indentation; this function only reads the start marker's.\n *\n * A file that does not reference this fragment at all is returned\n * unchanged (most consuming files reference exactly one of several\n * fragments in the library). Mismatched, reversed, or inconsistently\n * indented markers all throw -- fixing a malformed span is a decision\n * only a person should make. (LH-06b: the indentation check was added\n * after finding the START/END pair had actually drifted out of sync\n * in a real consuming file, silently re-indenting the fragment to the\n * START's column while leaving a differently-indented END marker\n * sitting in the output -- exactly the kind of malformed span this\n * function already refuses to silently paper over for the other two\n * cases, just not yet for this one.)\n */\nexport function expandFragment(content: string, name: string, fragmentContent: string): string {\n const { start, end } = fragmentMarkers(name);\n const startIdx = content.indexOf(start);\n const endIdx = content.indexOf(end);\n\n if (startIdx === -1 && endIdx === -1) return content;\n if (startIdx === -1 || endIdx === -1) {\n throw new MalformedFragmentMarkersError(\n `Mismatched fragment markers for \"${name}\": ` +\n `${startIdx === -1 ? \"start missing\" : \"start found\"}, ` +\n `${endIdx === -1 ? \"end missing\" : \"end found\"}.`,\n );\n }\n if (startIdx > endIdx) {\n throw new MalformedFragmentMarkersError(\n `Fragment markers for \"${name}\" are in the wrong order (end before start).`,\n );\n }\n\n const lineStart = content.lastIndexOf(\"\\n\", startIdx) + 1;\n const indent = content.slice(lineStart, startIdx);\n // The end marker's own indentation is part of `after`, not generated here --\n // slicing from endIdx (the marker text) rather than its line start would\n // silently drop it and dedent the closing marker to column 0.\n const endLineStart = content.lastIndexOf(\"\\n\", endIdx) + 1;\n const endIndent = content.slice(endLineStart, endIdx);\n if (endIndent !== indent) {\n throw new MalformedFragmentMarkersError(\n `Fragment markers for \"${name}\" have inconsistent indentation: ` +\n `start is indented ${JSON.stringify(indent)}, end is indented ${JSON.stringify(endIndent)}. ` +\n `Author both markers at the same indentation before re-running.`,\n );\n }\n\n const before = content.slice(0, startIdx + start.length);\n const after = content.slice(endLineStart);\n const indented = fragmentContent\n .trim()\n .split(\"\\n\")\n .map((line) => (line.length > 0 ? `${indent}${line}` : line))\n .join(\"\\n\");\n return `${before}\\n${indented}\\n${after}`;\n}\n\n/** True when `content` references the named fragment's marker span at all. */\nexport function referencesFragment(content: string, name: string): boolean {\n return content.includes(fragmentMarkers(name).start);\n}\n\n/**\n * True when `content` HOSTS the named fragment: its start marker stands on a\n * line of its own (leading whitespace allowed), the way every consuming span\n * is authored. A file that merely mentions a marker in prose -- the authoring\n * standard quotes `<!-- fragment:needs-you:start -->` inside backticks to\n * explain the mechanism -- references the fragment without hosting it, and\n * must not be expanded. The parity script asks this, not `referencesFragment`,\n * now that every Markdown asset is a candidate consumer, not only spells.\n */\nexport function hostsFragmentSpan(content: string, name: string): boolean {\n const { start } = fragmentMarkers(name);\n const escaped = start.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return new RegExp(`^[ \\\\t]*${escaped}[ \\\\t]*\\\\r?$`, \"m\").test(content);\n}\n","import chalk from \"chalk\";\nimport gradient from \"gradient-string\";\nimport { readManifest } from \"./manifest.js\";\n\nconst TAGLINE = \"Cast spells to ship software\";\n\n// ─── Brand palette — steel blue (#6F9FD8) ────────────────────────────────────\nconst STEEL = \"#6f9fd8\";\nconst STEEL_DEEP = \"#3a6ea5\";\nconst STEEL_DIM = \"#5d6f93\";\nconst INK_LABEL = \"#c7d2ee\";\nconst FAINT = \"#2c3658\";\n\nconst STOPS = [\"#2a4d7d\", \"#4a7bb5\", \"#6f9fd8\", \"#a9c8ec\"];\nconst arcaneGradient = gradient(STOPS);\n\n// ─── ARCANE wordmark ─────────────────────────────────────────────────────────\nconst WORDMARK = [\n \" █████╗ ██████╗ ██████╗ █████╗ ███╗ ██╗███████╗\",\n \"██╔══██╗██╔══██╗██╔════╝██╔══██╗████╗ ██║██╔════╝\",\n \"███████║██████╔╝██║ ███████║██╔██╗ ██║█████╗\",\n \"██╔══██║██╔══██╗██║ ██╔══██║██║╚██╗██║██╔══╝\",\n \"██║ ██║██║ ██║╚██████╗██║ ██║██║ ╚████║███████╗\",\n \"╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝\",\n];\nconst INDENT = \" \";\nconst LOCKUP_WIDTH = INDENT.length + Math.max(...WORDMARK.map((l) => l.length));\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** True when the terminal can render the fade-in safely. */\nfunction isAnimatable(): boolean {\n return (\n !!process.stdout.isTTY &&\n process.env[\"TERM\"] !== \"dumb\" &&\n process.env[\"CI\"] !== \"true\" &&\n process.env[\"ARCANE_DISABLE_ANIMATION\"] !== \"true\" &&\n (process.stdout.columns ?? 80) >= LOCKUP_WIDTH\n );\n}\n\n/** Scale a hex color toward black by factor f (0..1) — used for the fade-in. */\nfunction scale(hex: string, f: number): string {\n const v = (i: number): number =>\n Math.max(0, Math.min(255, Math.round(parseInt(hex.slice(i, i + 2), 16) * f)));\n return \"#\" + [v(1), v(3), v(5)].map((x) => x.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/** Build the ARCANE wordmark lines at brightness f (1 = full color). */\nfunction lockupLines(f = 1): string[] {\n const g = f >= 1 ? arcaneGradient : gradient(STOPS.map((c) => scale(c, f)));\n return WORDMARK.map((line) => INDENT + g(line));\n}\n\nfunction printFooter(version: string): void {\n const taglineText = ` ${TAGLINE} · agent-native SDLC · v${version}`;\n const width = Math.max(LOCKUP_WIDTH, taglineText.length);\n const border = chalk.hex(STEEL_DEEP)(\"─\".repeat(width));\n console.log(border);\n console.log(\n chalk.hex(STEEL)(` ${TAGLINE}`),\n chalk.hex(STEEL_DIM)(`· agent-native SDLC · v${version}`),\n );\n console.log(border);\n}\n\n/**\n * Prints the ARCANE wordmark static, with tagline and version.\n * Used by subcommands (init, etc.).\n */\nexport function printBanner(version: string): void {\n console.log();\n for (const line of lockupLines()) console.log(line);\n printFooter(version);\n console.log();\n}\n\n/**\n * Headline counts for the welcome — turns the installed component file list into\n * \"N spells · N agents · N standards\" (the manifest stores registry-relative,\n * forward-slash paths). Returns [] when nothing categorizable is installed, so\n * the caller can fall back to a plain component count.\n */\nfunction installSummary(components: { files: string[] }[]): string[] {\n const files = components.flatMap((c) =>\n c.files.map((f) => f.replace(/\\\\/g, \"/\")),\n );\n const spells = new Set(\n files\n .filter((f) => /\\/(prompts|commands)\\/spell-.+\\.md$/.test(f))\n .map((f) => f.split(\"/\").pop()!.replace(/\\.prompt\\.md$|\\.md$/, \"\")),\n ).size;\n const agents = files.filter((f) => /\\/agents\\/[^/]+\\.agent\\.md$/.test(f)).length;\n const standards = files.filter((f) =>\n /\\/governance\\/[^/]+\\.md$/.test(f),\n ).length;\n const parts: string[] = [];\n if (spells) parts.push(`${spells} spell${spells === 1 ? \"\" : \"s\"}`);\n if (agents) parts.push(`${agents} agent${agents === 1 ? \"\" : \"s\"}`);\n if (standards) parts.push(`${standards} standard${standards === 1 ? \"\" : \"s\"}`);\n return parts;\n}\n\n/**\n * The bare-`spell` welcome screen: a soft fade-in of the lockup followed by a\n * single gentle pulse (strobe-safe — monotonic brightness ramps, no color\n * cycling), then real current state: whether Arcane is installed in this repo\n * (profile, components, version) and the Spell Loop. Falls back to a static\n * lockup on non-TTY / CI / narrow terminals.\n */\nexport async function printWelcome(version: string, cwd: string): Promise<void> {\n console.log();\n if (isAnimatable()) {\n const draw = (lines: string[], redraw: boolean): void => {\n if (redraw) process.stdout.write(`\\x1b[${lines.length}A`);\n for (const line of lines) process.stdout.write(`\\r\\x1b[2K${line}\\n`);\n };\n // 1. Fade in from dark — the mark materializes.\n const fade = [0.1, 0.22, 0.36, 0.52, 0.68, 0.84, 1];\n for (let i = 0; i < fade.length; i++) {\n draw(lockupLines(fade[i]!), i > 0);\n await sleep(90);\n }\n // 2. One gentle settling pulse — a single slow breath of brightness.\n const pulse = [1, 0.9, 0.82, 0.9, 1];\n for (let i = 0; i < pulse.length; i++) {\n draw(lockupLines(pulse[i]!), true);\n await sleep(140);\n }\n } else {\n for (const line of lockupLines()) console.log(line);\n }\n printFooter(version);\n console.log();\n\n const label = (s: string): string => chalk.hex(STEEL_DIM)(s.padEnd(11));\n const dot = chalk.hex(FAINT)(\"·\");\n\n let repoLine: string;\n try {\n const manifest = await readManifest(cwd);\n const parts = installSummary(manifest.components);\n const summary = parts.length\n ? parts\n : [`${manifest.components.length} components`];\n repoLine =\n `${chalk.hex(STEEL)(\"✓\")} Arcane ${chalk.hex(INK_LABEL)(`«${manifest.profile}»`)} ${dot} ` +\n summary.map((p) => chalk.hex(INK_LABEL)(p)).join(` ${dot} `) +\n ` ${dot} ${chalk.hex(STEEL_DIM)(`v${manifest.version}`)}`;\n } catch {\n repoLine =\n `${chalk.hex(STEEL_DIM)(\"○\")} not initialized ${chalk.hex(FAINT)(\"—\")} run ` +\n `${chalk.hex(STEEL).inverse(\" spell init \")} ${chalk.hex(STEEL_DIM)(\"to scaffold\")}`;\n }\n console.log(` ${label(\"This repo\")}${repoLine}`);\n\n const phases = [\"plan\", \"architect\", \"implement\", \"test\", \"review\", \"ship\"].join(\n chalk.hex(STEEL_DEEP)(\" → \"),\n );\n console.log(` ${label(\"Spell Loop\")}${chalk.hex(\"#dbe3f7\")(phases)}`);\n\n // loop-back hint under the implement → test → review cycle\n const flat = [\"plan\", \"architect\", \"implement\", \"test\", \"review\", \"ship\"].join(\" → \");\n const loopStart = 13 + flat.indexOf(\"implement\"); // col under \"implement\"\n const loopEnd = 13 + flat.indexOf(\"review\") + 5; // col under last char of \"review\"\n const inner = loopEnd - loopStart - 1;\n const lp = Math.floor((inner - 19) / 2); // 19 = visible width of \" ↺ until it passes \"\n const rp = inner - 19 - lp;\n const dash = (n: number): string => chalk.hex(STEEL_DEEP)(\"─\".repeat(Math.max(0, n)));\n console.log(\n \" \".repeat(loopStart) +\n chalk.hex(STEEL_DEEP)(\"╰\") +\n dash(lp) +\n chalk.hex(STEEL)(\" ↺ \") +\n chalk.hex(STEEL_DIM)(\"until it passes\") +\n \" \" +\n dash(rp) +\n chalk.hex(STEEL_DEEP)(\"╯\"),\n );\n console.log();\n}\n\nexport function printSuccess(message: string): void {\n console.log(`\\n${chalk.hex(STEEL)(\"✦\")} ${chalk.bold(message)}`);\n}\n\nexport function printStep(message: string): void {\n console.log(` ${chalk.hex(STEEL_DEEP)(\"▸\")} ${message}`);\n}\n\nexport function printDryRun(message: string): void {\n console.log(` ${chalk.yellow(\"◌\")} ${chalk.dim(message)}`);\n}\n\nexport function printSection(title: string): void {\n console.log(`\\n${chalk.hex(STEEL)(\"─\")} ${chalk.bold(title)}`);\n}\n\nexport function printInfo(message: string): void {\n console.log(` ${chalk.dim(message)}`);\n}\n\nexport function printNextStep(step: number, message: string): void {\n console.log(` ${chalk.hex(STEEL_DEEP)(`${step}.`)} ${message}`);\n}\n\nexport function printWarning(message: string): void {\n console.log(` ${chalk.yellow(\"⚠\")} ${chalk.yellow(message)}`);\n}\n","/**\n * `spell agents` subcommand group.\n *\n * Subcommands:\n * spell agents init — Interactive roster setup (naming strategy + role selection)\n * spell agents sync — Regenerate all client instruction files from canonical defs\n * spell agents list — Show current roster with assigned names and roles\n */\n\nimport { select, checkbox, Separator } from \"@inquirer/prompts\";\nimport { mkdir, writeFile, copyFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { stringify } from \"yaml\";\nimport type {\n AgentInitOptions,\n AgentRoster,\n AgentRosterEntry,\n AgentSyncOptions,\n NamingStrategy,\n AgentProfileId,\n} from \"../types.js\";\nimport { AGENT_ROLES } from \"../config/agent-roles.js\";\nimport {\n AGENT_PROFILE_CONFIGS,\n getAgentProfileConfig,\n} from \"../config/agent-profiles.js\";\nimport { applyNamingStrategy } from \"./naming.js\";\nimport {\n agentsBaseDir,\n loadRoster,\n projectAgentsDir,\n rosterExists,\n AgentConfigValidationError,\n AgentRosterNotFoundError,\n} from \"./agent-loader.js\";\nimport { syncAgents } from \"./agent-generator.js\";\nimport { printStep, printSuccess } from \"./banner.js\";\nimport { readManifest, writeManifest } from \"./manifest.js\";\nimport {\n effectiveSpellScope,\n describeFanoutOutcomes,\n syncUserTierFanout,\n userTierRoot,\n} from \"./user-tier.js\";\nimport type { ArcaneManifest, InstallScope } from \"../types.js\";\n\n/**\n * Where a run of `spell agents <sub>` operates: this repository, or the user\n * tier's store at `~/.arcane` (CS-06 / ARC-047 decision 2). The store is the\n * same one `spell init --user` creates -- the roster and its definition files\n * sit beside the spells, in the same two paths a repository uses.\n */\nexport function agentsTargetDir(scope: InstallScope | undefined, cwd: string): string {\n return scope === \"user\" ? userTierRoot() : cwd;\n}\n\n/**\n * The user tier's store manifest, or a message saying how to create one.\n * `spell agents --user` writes into the store `spell init --user` owns and\n * records its fan-out in that manifest, so the store has to exist first --\n * there is deliberately no second, agents-only store shape to reason about.\n */\nasync function requireUserStore(storeRoot: string): Promise<ArcaneManifest> {\n try {\n return await readManifest(storeRoot);\n } catch {\n console.error(\n `\\n ✗ No user tier at ${storeRoot}.\\n` +\n \" Run `spell init --user` once to create it, then re-run this command.\\n\",\n );\n process.exit(1);\n throw new Error(\"unreachable\");\n }\n}\n\n/**\n * The roster a run should use, and where it came from.\n *\n * A repository that has opted into the user tier and has no roster of its own\n * takes the tier's -- ARC-045 decision 6, \"the user roster is authoritative\n * for UI-visible agents when `spell_scope: \"user\"`\". Without this a repository\n * that never ran `spell agents init` gets no roster tables at all after opting\n * in, even with a perfectly good roster sitting in the store: the half that\n * removes the repository's files shipped, and the half that falls back for the\n * tables did not. A repository's own roster always wins when it has one.\n */\nexport interface ResolvedRoster {\n roster: AgentRoster;\n /** The directory its per-role definition files live in, preferred over the bundled assets. */\n definitionsDir: string;\n /** True when it came from the user tier rather than this repository. */\n fromUserTier: boolean;\n}\n\nexport async function resolveRoster(\n targetDir: string,\n scope: InstallScope,\n /** The home the store sits under. Defaults to the real one; a test points it elsewhere. */\n homeDir?: string,\n): Promise<ResolvedRoster> {\n try {\n return {\n roster: await loadRoster(targetDir, scope),\n definitionsDir: projectAgentsDir(targetDir, scope),\n fromUserTier: scope === \"user\",\n };\n } catch (err) {\n const missing = err instanceof AgentRosterNotFoundError;\n if (!missing || scope !== \"repo\" || (await repoSpellScope(targetDir)) !== \"user\") throw err;\n // Opted in, and nothing local to read: the store is the authority.\n const storeRoot = homeDir === undefined ? userTierRoot() : userTierRoot(homeDir);\n return {\n roster: await loadRoster(storeRoot, \"user\"),\n definitionsDir: projectAgentsDir(storeRoot, \"user\"),\n fromUserTier: true,\n };\n }\n}\n\n/**\n * This repository's `spell_scope`, for a repo-tier run. A repository with no\n * manifest at all is a plain directory as far as agents are concerned, and\n * takes the default.\n */\nasync function repoSpellScope(targetDir: string): Promise<InstallScope> {\n try {\n return effectiveSpellScope(await readManifest(targetDir));\n } catch {\n return \"repo\";\n }\n}\n\n/**\n * Runs `syncAgents` for either tier and finishes what the tier needs.\n *\n * A repository run is unchanged. A user-tier run gets its rendered agent\n * files reconciled through the same fan-out machinery CS-04 built for spells\n * -- hash-recorded in the store manifest, so an operator's edit to one of\n * them is recognized and never overwritten or deleted -- scoped to the agent\n * client so `spell update --user` and this command can share one record\n * without either erasing the other's entries.\n */\nasync function performAgentSync(\n targetDir: string,\n assetsDir: string,\n roster: AgentRoster,\n options: AgentSyncOptions,\n): Promise<{ synced: string[]; skipped: string[]; hasUnresolvedRoles: boolean }> {\n const scope: InstallScope = options.scope ?? \"repo\";\n const spellScope = scope === \"user\" ? undefined : await repoSpellScope(targetDir);\n const result = await syncAgents(targetDir, assetsDir, roster, {\n ...options,\n scope,\n ...(spellScope === undefined ? {} : { spellScope }),\n });\n\n if (scope === \"user\") {\n const manifest = await requireUserStore(targetDir);\n // The store root IS `<home>/.arcane` (userTierRoot), so the home the\n // fan-out paths are relative to is its parent. Derived rather than read\n // from the environment a second time: one source of truth per run, and a\n // test can point the whole thing at a temporary home by passing a store\n // root inside it.\n const fanout = await syncUserTierFanout({\n homeDir: dirname(targetDir),\n storeRoot: targetDir,\n spellIds: [],\n extraFiles: result.userAgentFiles,\n ownedClients: [\"copilot-agents\"],\n previous: manifest.fanout,\n dryRun: options.dryRun,\n fallbackDir: assetsDir,\n });\n for (const line of describeFanoutOutcomes(fanout.outcomes, options.dryRun)) {\n console.log(` ${line}`);\n }\n if (!options.dryRun) {\n await writeManifest(targetDir, { ...manifest, fanout: fanout.record });\n }\n }\n\n if (result.unmanagedAgentFiles.length > 0) {\n console.log(\n `\\n ! This repository takes its spells from the user tier (spell_scope: \"user\"), so it no\\n` +\n ` longer receives agent files of its own. ${result.unmanagedAgentFiles.length} are still on disk\\n` +\n ` from before the opt-out. Nothing here deletes them -- no component ever tracked\\n` +\n ` them, so there is no recorded hash to prove one untouched. Remove them yourself:\\n`,\n );\n for (const file of result.unmanagedAgentFiles) console.log(` ${file}`);\n console.log(`\\n git rm ${result.unmanagedAgentFiles.join(\" \")}\\n`);\n }\n\n return result;\n}\n\n\n// ─── spell agents init ────────────────────────────────────────────────────────\n\n/**\n * Interactive agent roster setup.\n *\n * 1. Prompts for agent profile (base | full | custom)\n * 2. Prompts for naming strategy (generic | random | custom)\n * 3. Applies naming and writes .arcane/agents.yaml + copies definition files\n */\nexport async function runAgentsInit(\n options: AgentInitOptions,\n targetDir: string,\n assetsDir: string,\n): Promise<void> {\n // Check for existing roster\n const scope: InstallScope = options.scope ?? \"repo\";\n // Before anything is written: the user tier's roster lives in the store\n // `spell init --user` owns, and its fan-out is recorded in that store's\n // manifest. Checking after the roster is written would leave a half-built\n // tier behind on the way to the same error.\n if (scope === \"user\") await requireUserStore(targetDir);\n const baseDir = agentsBaseDir(targetDir, scope);\n const label = scope === \"user\" ? \"~/.arcane\" : \".arcane\";\n if (!options.force && (await rosterExists(targetDir, scope))) {\n console.log(\n 'Agent roster already exists at .arcane/agents.yaml. ' +\n 'Run \"spell agents sync\" to regenerate outputs, or use --force to reinitialize.',\n );\n return;\n }\n\n console.log();\n\n // ── Step 1: Agent profile ────────────────────────────────────────────────\n let profileId: AgentProfileId = options.profile ?? \"full\";\n if (!options.profile) {\n const baseRoles = getAgentProfileConfig(\"base\").roles;\n const baseRoleNames = baseRoles.map((id) => {\n const role = AGENT_ROLES.find((r) => r.id === id);\n return role ? role.role : id;\n });\n const allCategories = [...new Set(AGENT_ROLES.map((r) => r.category))];\n\n profileId = (await select({\n message: \"Select an agent profile:\",\n choices: AGENT_PROFILE_CONFIGS.map((p) => ({\n value: p.id,\n name: `${p.id === \"base\" ? \"🎯\" : p.id === \"full\" ? \"🧙\" : \"🎨\"} ${p.displayName}`,\n description:\n p.id === \"base\"\n ? `\\n4 core roles: ${baseRoleNames.join(\", \")}`\n : p.id === \"full\"\n ? `\\nAll 12 roles across ${allCategories.join(\", \")}`\n : \"\\nPick individual roles from the full roster\",\n })),\n })) as AgentProfileId;\n }\n\n // Resolve role IDs for selected profile\n let selectedRoleIds: string[];\n if (profileId === \"custom\") {\n // Group roles by category with separators\n const categories = [...new Set(AGENT_ROLES.map((r) => r.category))];\n const choicesWithSeparators: Array<\n | { value: string; name: string; checked: boolean }\n | Separator\n > = [];\n for (const cat of categories) {\n choicesWithSeparators.push(new Separator(`── ${cat.charAt(0).toUpperCase() + cat.slice(1)} ──`));\n for (const r of AGENT_ROLES.filter((r) => r.category === cat)) {\n choicesWithSeparators.push({\n value: r.id,\n name: `${r.id} — ${r.role}`,\n checked: true,\n });\n }\n }\n\n console.log();\n selectedRoleIds = await checkbox({\n message: \"Select roles to install:\",\n choices: choicesWithSeparators,\n validate: (choices) =>\n choices.length > 0 ? true : \"Select at least one role\",\n });\n } else {\n selectedRoleIds = getAgentProfileConfig(profileId).roles;\n }\n\n // ── Step 2: Naming strategy ──────────────────────────────────────────────\n let namingStrategy: NamingStrategy = options.naming ?? \"arcanos\";\n if (!options.naming) {\n console.log();\n namingStrategy = (await select({\n message: \"Choose a naming strategy for your agents:\",\n choices: [\n {\n value: \"arcanos\" as NamingStrategy,\n name: \"🃏 Arcanos\",\n description: \"\\nPersona names: Kellar, Merlin, Lafayette, Lince, Prospero...\",\n },\n {\n value: \"generic\" as NamingStrategy,\n name: \"🏷️ Generic\",\n description: \"\\nRole-derived labels: Orchestrator, Architect, Developer, QA Lead...\",\n },\n {\n value: \"random\" as NamingStrategy,\n name: \"🎲 Random\",\n description: \"\\nCommon first names shuffled randomly: Blake, Morgan, Casey...\",\n },\n {\n value: \"custom\" as NamingStrategy,\n name: \"✏️ Custom\",\n description: \"\\nType a unique name for each role interactively\",\n },\n ],\n })) as NamingStrategy;\n }\n\n // ── Step 3: Apply naming ─────────────────────────────────────────────────\n printStep(\"Assigning names...\");\n const named = await applyNamingStrategy(namingStrategy, selectedRoleIds);\n\n // ── Step 4: Build roster ─────────────────────────────────────────────────\n const rosterEntries: AgentRosterEntry[] = named.map(({ definition, name, epithet }) => {\n // Orchestrator gets the \"main\" OpenClaw ID by convention\n const id = definition === \"orchestrator\" ? \"main\" : name.toLowerCase().replace(/\\s+/g, \"-\");\n return epithet ? { definition, name, id, epithet } : { definition, name, id };\n });\n\n const roster: AgentRoster = {\n schema_version: 2,\n naming_strategy: namingStrategy,\n agent_profile: profileId,\n openclaw: {\n enabled: true,\n workspace_root: \"~/.openclaw\",\n },\n roster: rosterEntries,\n };\n\n // ── Step 5: Write .arcane/agents.yaml ────────────────────────────────────\n const arcaneDir = join(baseDir, \"agents\");\n if (!options.dryRun) {\n await mkdir(arcaneDir, { recursive: true });\n await writeFile(join(baseDir, \"agents.yaml\"), stringify(roster), \"utf8\");\n } else {\n console.log(` [dry-run] Would write: ${label}/agents.yaml`);\n }\n\n // ── Step 6: Copy bundled definition files ─────────────────────────────────\n for (const roleId of selectedRoleIds) {\n const src = join(assetsDir, \"agents\", `${roleId}.yaml`);\n const dest = join(arcaneDir, `${roleId}.yaml`);\n if (!options.dryRun) {\n try {\n await copyFile(src, dest);\n } catch {\n console.warn(` ! Could not copy definition for \"${roleId}\" — skipping`);\n }\n } else {\n console.log(` [dry-run] Would copy: ${label}/agents/${roleId}.yaml`);\n }\n }\n\n if (options.dryRun) {\n console.log(\n `\\n [dry-run] Would initialize roster with ${selectedRoleIds.length} agents`,\n );\n return;\n }\n\n // ── Step 7: Auto-sync all clients ────────────────────────────────────────\n printStep(\"Syncing agent definitions to all clients...\");\n const { synced, skipped, hasUnresolvedRoles } = await performAgentSync(\n targetDir,\n assetsDir,\n roster,\n { dryRun: false, scope },\n );\n\n printSuccess(\"Agent roster initialized\");\n console.log(` 📋 Profile: ${profileId} · Naming: ${namingStrategy} · Agents: ${rosterEntries.length}`);\n console.log(\n ` 🔄 Synced: ${synced.length} outputs (${scope === \"user\" ? \"agent personas in ~/.claude/agents\" : \"Copilot, Claude, Codex, OpenClaw\"})`,\n );\n if (skipped.length > 0) {\n console.log(` ⚠️ Skipped: ${skipped.length} (${skipped.join(\", \")})`);\n }\n console.log();\n console.log(\n ` 💡 Customize: edit ${label}/agents.yaml → spell agents sync${scope === \"user\" ? \" --user\" : \"\"}`,\n );\n console.log();\n if (hasUnresolvedRoles) {\n console.error(\n \" ✗ One or more rostered roles could not be resolved (see \\\"Skipped\\\" above) — \" +\n \"a malformed or missing agent template must not pass silently.\",\n );\n process.exitCode = 1;\n }\n}\n\n// ─── spell agents sync ────────────────────────────────────────────────────────\n\n/** Regenerates all client instruction files from the current roster + definitions. */\nexport async function runAgentsSync(\n options: AgentSyncOptions,\n targetDir: string,\n assetsDir: string,\n): Promise<void> {\n const scope: InstallScope = options.scope ?? \"repo\";\n let roster: AgentRoster;\n let definitionsDir: string | undefined;\n let fromUserTier = false;\n try {\n ({ roster, definitionsDir, fromUserTier } = await resolveRoster(targetDir, scope));\n } catch (err) {\n if (\n err instanceof AgentRosterNotFoundError\n || err instanceof AgentConfigValidationError\n ) {\n console.error(err.message);\n process.exit(1);\n return;\n }\n throw err;\n }\n\n if (options.dryRun) {\n console.log(\"\\n [dry-run] Would sync agents from roster:\\n\");\n } else {\n console.log(\"\\n Syncing agents...\\n\");\n }\n\n if (fromUserTier && scope === \"repo\") {\n console.log(\n \" This repository has no roster of its own and takes its spells from the user tier,\" +\n \"\\n so its roster tables are built from ~/.arcane/agents.yaml.\\n\",\n );\n }\n\n const { synced, skipped, hasUnresolvedRoles } = await performAgentSync(\n targetDir,\n assetsDir,\n roster,\n { ...options, scope, ...(definitionsDir === undefined ? {} : { definitionsDir }) },\n );\n\n if (options.dryRun) {\n console.log(`\\n [dry-run] ${synced.length} outputs would be generated`);\n return;\n }\n\n console.log(`\\n ✓ Synced ${synced.length} outputs`);\n for (const item of synced) {\n console.log(` + ${item}`);\n }\n if (skipped.length > 0) {\n console.log(`\\n ! Skipped ${skipped.length}:`);\n for (const item of skipped) {\n console.log(` - ${item}`);\n }\n }\n console.log();\n if (hasUnresolvedRoles) {\n console.error(\n \" ✗ One or more rostered roles could not be resolved (see \\\"Skipped\\\" above) — \" +\n \"a malformed or missing agent template must not pass silently.\",\n );\n process.exitCode = 1;\n }\n}\n\n// ─── spell agents list ────────────────────────────────────────────────────────\n\n/** Displays the current agent roster in a table. */\nexport async function runAgentsList(\n targetDir: string,\n scope: InstallScope = \"repo\",\n): Promise<void> {\n let roster: AgentRoster;\n try {\n roster = await loadRoster(targetDir, scope);\n } catch (err) {\n if (\n err instanceof AgentRosterNotFoundError\n || err instanceof AgentConfigValidationError\n ) {\n console.error(err.message);\n process.exit(1);\n return;\n }\n throw err;\n }\n\n const nameWidth = Math.max(\n \"Name\".length,\n ...roster.roster.map((e) => (e.name ?? e.definition).length),\n );\n const defWidth = Math.max(\n \"Role ID\".length,\n ...roster.roster.map((e) => e.definition.length),\n );\n const idWidth = Math.max(\n \"OpenClaw ID\".length,\n ...roster.roster.map((e) => e.id.length),\n );\n\n const pad = (s: string, n: number) => s.padEnd(n);\n\n console.log(\n `\\n Profile: ${roster.agent_profile} | Naming: ${roster.naming_strategy} | Agents: ${roster.roster.length}\\n`,\n );\n console.log(\n ` ${pad(\"Name\", nameWidth)} ${pad(\"Role ID\", defWidth)} ${pad(\"OpenClaw ID\", idWidth)}`,\n );\n console.log(\n ` ${\"-\".repeat(nameWidth)} ${\"-\".repeat(defWidth)} ${\"-\".repeat(idWidth)}`,\n );\n for (const entry of roster.roster) {\n console.log(\n ` ${pad(entry.name ?? \"(unnamed)\", nameWidth)} ${pad(entry.definition, defWidth)} ${pad(entry.id, idWidth)}`,\n );\n }\n console.log();\n}\n","/**\n * Canonical agent role definitions.\n * Each entry maps to a YAML template file at src/assets/agents/{id}.yaml.\n * These are the 12 standard roles that ship with Arcane.\n */\n\nimport type { AgentCategory } from \"../types.js\";\n\nexport interface AgentRoleConfig {\n id: string;\n role: string;\n category: AgentCategory;\n}\n\nexport const AGENT_ROLES: AgentRoleConfig[] = [\n {\n id: \"orchestrator\",\n role: \"Product Operations Manager\",\n category: \"operations\",\n },\n {\n id: \"architecture-lead\",\n role: \"CTO / Architecture Lead\",\n category: \"engineering\",\n },\n {\n id: \"fullstack-dev\",\n role: \"Full-Stack Developer\",\n category: \"engineering\",\n },\n {\n id: \"qa-lead\",\n role: \"QA Lead\",\n category: \"quality\",\n },\n {\n id: \"devops\",\n role: \"DevOps / CI/CD Engineer\",\n category: \"engineering\",\n },\n {\n id: \"frontend-dev\",\n role: \"Frontend Developer\",\n category: \"engineering\",\n },\n {\n id: \"mobile-dev\",\n role: \"Mobile Developer\",\n category: \"engineering\",\n },\n {\n id: \"research-analyst\",\n role: \"Research & Backlog Analyst\",\n category: \"research\",\n },\n {\n id: \"marketing-strategist\",\n role: \"Marketing Strategist\",\n category: \"operations\",\n },\n {\n id: \"operations-comms\",\n role: \"Operations Communications\",\n category: \"operations\",\n },\n {\n id: \"collaborator\",\n role: \"External Collaboration Lead\",\n category: \"operations\",\n },\n {\n id: \"security-ops\",\n role: \"Security Operations\",\n category: \"quality\",\n },\n];\n\n/** Lookup by role ID. */\nexport function getAgentRole(id: string): AgentRoleConfig | undefined {\n return AGENT_ROLES.find((r) => r.id === id);\n}\n","/**\n * Agent profile configurations.\n * Controls which roles are included when running `spell agents init`.\n * Separate from src/config/profiles.ts (which governs governance/prompts components).\n */\n\nimport type { AgentProfileId } from \"../types.js\";\n\nexport interface AgentProfileConfig {\n id: AgentProfileId;\n displayName: string;\n description: string;\n /** Role IDs included in this profile. Empty means user selects interactively. */\n roles: string[];\n}\n\nexport const AGENT_PROFILE_CONFIGS: AgentProfileConfig[] = [\n {\n id: \"base\",\n displayName: \"Base\",\n description: \"Core team: orchestrator, architecture-lead, fullstack-dev, qa-lead\",\n roles: [\"orchestrator\", \"architecture-lead\", \"fullstack-dev\", \"qa-lead\"],\n },\n {\n id: \"full\",\n displayName: \"Full\",\n description: \"Complete roster — all 12 roles\",\n roles: [\n \"orchestrator\",\n \"architecture-lead\",\n \"fullstack-dev\",\n \"qa-lead\",\n \"devops\",\n \"frontend-dev\",\n \"mobile-dev\",\n \"research-analyst\",\n \"marketing-strategist\",\n \"operations-comms\",\n \"collaborator\",\n \"security-ops\",\n ],\n },\n {\n id: \"custom\",\n displayName: \"Custom\",\n description: \"Select roles interactively\",\n roles: [], // populated at runtime via checkbox prompt\n },\n];\n\n/** Lookup by profile ID. */\nexport function getAgentProfileConfig(id: AgentProfileId): AgentProfileConfig {\n const config = AGENT_PROFILE_CONFIGS.find((p) => p.id === id);\n if (!config) throw new Error(`Unknown agent profile: \"${id}\"`);\n return config;\n}\n","/**\n * Naming strategy logic for agent roster setup.\n *\n * Four strategies:\n * arcanos — the built-in persona roster (default): twelve legendary agents\n * generic — maps each role to a short, memorable generic label\n * random — assigns random common names from a curated pool\n * custom — prompts the user to type a name for each role\n */\n\nimport { input } from \"@inquirer/prompts\";\nimport type { NamingStrategy } from \"../types.js\";\nimport { getAgentRole } from \"../config/agent-roles.js\";\n\n// ─── Generic names ────────────────────────────────────────────────────────────\n\n/** Short, role-derived display names for the generic strategy. */\nconst GENERIC_NAME_MAP: Record<string, string> = {\n \"orchestrator\": \"Orchestrator\",\n \"architecture-lead\": \"Architect\",\n \"fullstack-dev\": \"Developer\",\n \"qa-lead\": \"QA Lead\",\n \"devops\": \"DevOps\",\n \"frontend-dev\": \"Frontend\",\n \"mobile-dev\": \"Mobile\",\n \"research-analyst\": \"Researcher\",\n \"marketing-strategist\": \"Marketer\",\n \"operations-comms\": \"Comms\",\n \"collaborator\": \"Collaborator\",\n \"security-ops\": \"Security\",\n};\n\n// ─── Arcanos preset names ─────────────────────────────────────────────────────\n\n/** Persona names from the Arcanos roster — twelve legendary agents. */\nconst ARCANOS_NAME_MAP: Record<string, string> = {\n \"orchestrator\": \"Kellar\",\n \"architecture-lead\": \"Merlin\",\n \"fullstack-dev\": \"Lafayette\",\n \"qa-lead\": \"Lince\",\n \"devops\": \"Prospero\",\n \"frontend-dev\": \"Adelaide\",\n \"mobile-dev\": \"Mercurio\",\n \"research-analyst\": \"Alexander\",\n \"marketing-strategist\": \"Circe\",\n \"operations-comms\": \"Bess\",\n \"collaborator\": \"Iris\",\n \"security-ops\": \"Custodio\",\n};\n\n/**\n * Arcanos epithets (naming-conventions.md's roster table) — bound to the\n * PERSONA NAME, not the role archetype: \"the Archmage\" belongs to Merlin,\n * not to \"architecture-lead\" itself. Deliberately not a field on the role\n * YAML (src/assets/agents/*.yaml), which is naming-strategy-agnostic and\n * reused by generic/random/custom naming — putting it there would force\n * Arcanos-flavored text onto strategies designed to be epithet-less (the\n * \"generic role labels\" option naming-conventions.md documents).\n */\nconst ARCANOS_EPITHET_MAP: Record<string, string> = {\n \"orchestrator\": \"the Maestro\",\n \"architecture-lead\": \"the Archmage\",\n \"fullstack-dev\": \"the Conjuror\",\n \"qa-lead\": \"the Unmasker\",\n \"devops\": \"the Stormcaller\",\n \"frontend-dev\": \"the Illusionist\",\n \"mobile-dev\": \"the Swift\",\n \"research-analyst\": \"the Man Who Knows\",\n \"marketing-strategist\": \"the Charmweaver\",\n \"operations-comms\": \"the Herald\",\n \"collaborator\": \"the Emissary\",\n \"security-ops\": \"the Warden\",\n};\n\n// ─── Random name pool ─────────────────────────────────────────────────────────\n\n/**\n * Curated pool of common single-word names.\n * Gender-neutral where possible to avoid unintentional character typecasting.\n */\nconst RANDOM_NAME_POOL: string[] = [\n \"Aiden\", \"Blake\", \"Casey\", \"Dana\", \"Ellis\", \"Finley\", \"Gray\", \"Harper\",\n \"Indigo\", \"Jordan\", \"Kai\", \"Lane\", \"Morgan\", \"Nova\", \"Owen\", \"Parker\",\n \"Quinn\", \"River\", \"Sage\", \"Taylor\", \"Uma\", \"Vale\", \"Wren\", \"Xander\",\n \"Yael\", \"Zara\", \"Avery\", \"Brett\", \"Charlie\", \"Devon\",\n];\n\n// ─── Strategy implementations ────────────────────────────────────────────────\n\nexport interface NamingResult {\n definition: string;\n name: string;\n /** Present only for the arcanos strategy — other strategies are deliberately epithet-less. */\n epithet?: string;\n}\n\n/** Assigns short generic labels derived from each role. */\nexport function applyGenericNames(roleIds: string[]): NamingResult[] {\n return roleIds.map((id) => ({\n definition: id,\n name: GENERIC_NAME_MAP[id] ?? id,\n }));\n}\n\n/** Assigns Arcanos persona names (Kellar, Merlin, Lafayette, etc.). */\nexport function applyArcanosNames(roleIds: string[]): NamingResult[] {\n return roleIds.map((id) => {\n const name = ARCANOS_NAME_MAP[id] ?? GENERIC_NAME_MAP[id] ?? id;\n const epithet = ARCANOS_NAME_MAP[id] ? ARCANOS_EPITHET_MAP[id] : undefined;\n return epithet ? { definition: id, name, epithet } : { definition: id, name };\n });\n}\n\n/**\n * Assigns random names from the pool, shuffled per call.\n * If more roles than pool entries, wraps around.\n */\nexport function applyRandomNames(roleIds: string[]): NamingResult[] {\n const shuffled = [...RANDOM_NAME_POOL].sort(() => Math.random() - 0.5);\n return roleIds.map((id, i) => ({\n definition: id,\n name: shuffled[i % shuffled.length]!,\n }));\n}\n\n/**\n * Prompts the user to type a name for each role interactively.\n * Defaults to the generic name to speed up entry.\n */\nexport async function promptCustomNames(roleIds: string[]): Promise<NamingResult[]> {\n const results: NamingResult[] = [];\n for (const id of roleIds) {\n const role = getAgentRole(id);\n const defaultName = GENERIC_NAME_MAP[id] ?? id;\n const name = await input({\n message: `Name for ${role?.role ?? id}:`,\n default: defaultName,\n validate: (v) =>\n v.trim().length > 0 ? true : \"Name cannot be empty\",\n });\n results.push({ definition: id, name: name.trim() });\n }\n return results;\n}\n\n/**\n * Applies the selected naming strategy to a list of role IDs.\n * Returns an array of definition→name pairs.\n */\nexport async function applyNamingStrategy(\n strategy: NamingStrategy,\n roleIds: string[],\n): Promise<NamingResult[]> {\n switch (strategy) {\n case \"arcanos\":\n return applyArcanosNames(roleIds);\n case \"generic\":\n return applyGenericNames(roleIds);\n case \"random\":\n return applyRandomNames(roleIds);\n case \"custom\":\n return promptCustomNames(roleIds);\n }\n}\n","/**\n * Agent definition and roster loader.\n *\n * Reads canonical agent YAML files from two locations:\n * - assetsDir/agents/ — bundled templates (used during init/add)\n * - targetDir/.arcane/agents/ — project's installed/customized definitions (used during sync)\n */\n\nimport { readFile, readdir, access } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { parse } from \"yaml\";\nimport type { AgentDefinition, AgentRoster, InstallScope } from \"../types.js\";\nimport {\n AgentConfigValidationError,\n validateAgentDefinition,\n validateAgentRoster,\n} from \"./agent-schema.js\";\n\nexport { AgentConfigValidationError } from \"./agent-schema.js\";\n\n// ─── Error types ──────────────────────────────────────────────────────────────\n\nexport class AgentDefinitionNotFoundError extends Error {\n constructor(id: string, searchDir: string) {\n super(\n `Agent definition \"${id}\" not found in \"${searchDir}\". ` +\n `Check that ${id}.yaml exists or run \"spell agents add ${id}\".`,\n );\n this.name = \"AgentDefinitionNotFoundError\";\n }\n}\n\nexport class AgentRosterNotFoundError extends Error {\n /**\n * Names the tier the caller actually asked for. A `--user` run that reports\n * the repository's path sends the operator to the wrong file and the wrong\n * command, which is worse than no message.\n */\n constructor(scope: InstallScope = \"repo\") {\n const user = scope === \"user\";\n super(\n `No agent roster found at ${user ? \"~/.arcane/agents.yaml\" : \".arcane/agents.yaml\"}. ` +\n `Run \"spell agents init${user ? \" --user\" : \"\"}\" to create one.`,\n );\n this.name = \"AgentRosterNotFoundError\";\n }\n}\n\nfunction parseYaml(content: string, source: string): unknown {\n try {\n return parse(content);\n } catch (err) {\n const detail = err instanceof Error ? err.message : \"unknown YAML parse error\";\n throw new AgentConfigValidationError(source, [`malformed YAML: ${detail}`]);\n }\n}\n\n// ─── Definition loaders ───────────────────────────────────────────────────────\n\n/**\n * Loads a single agent definition from a directory.\n * Used by `spell agents sync` (reads from .arcane/agents/).\n */\nexport async function loadAgentDefinition(\n agentsDir: string,\n id: string,\n): Promise<AgentDefinition> {\n const filePath = join(agentsDir, `${id}.yaml`);\n let content: string;\n try {\n content = await readFile(filePath, \"utf8\");\n } catch {\n throw new AgentDefinitionNotFoundError(id, agentsDir);\n }\n return validateAgentDefinition(parseYaml(content, filePath), filePath);\n}\n\n/**\n * Loads all agent definition files from a directory.\n * Skips non-YAML files and the agents.yaml roster file.\n */\nexport async function loadAllAgentDefinitions(\n agentsDir: string,\n): Promise<AgentDefinition[]> {\n let entries: string[];\n try {\n entries = await readdir(agentsDir);\n } catch {\n return [];\n }\n\n const definitions: AgentDefinition[] = [];\n for (const file of entries) {\n if (!file.endsWith(\".yaml\") || file === \"agents.yaml\") continue;\n const filePath = join(agentsDir, file);\n const content = await readFile(filePath, \"utf8\");\n definitions.push(validateAgentDefinition(parseYaml(content, filePath), filePath));\n }\n return definitions;\n}\n\n// ─── Roster loaders ───────────────────────────────────────────────────────────\n\n/**\n * Loads the agent roster from .arcane/agents.yaml in the target directory.\n * Throws AgentRosterNotFoundError if not initialized.\n */\n/**\n * The directory holding `agents.yaml` and the per-role definition files.\n *\n * A repository keeps them under its framework layer, `<repo>/.arcane`. The\n * user tier's store IS that layer -- `~/.arcane` already plays the role\n * `<repo>/.arcane` plays in a repository, which is why its spells live at\n * `~/.arcane/spells/<id>.md` and not `~/.arcane/.arcane/spells/` (CS-04's\n * store view, `componentForScope`). Agents follow the same rule.\n */\nexport function agentsBaseDir(targetDir: string, scope: InstallScope = \"repo\"): string {\n return scope === \"user\" ? targetDir : join(targetDir, \".arcane\");\n}\n\nexport async function loadRoster(\n targetDir: string,\n scope: InstallScope = \"repo\",\n): Promise<AgentRoster> {\n const rosterPath = join(agentsBaseDir(targetDir, scope), \"agents.yaml\");\n let content: string;\n try {\n content = await readFile(rosterPath, \"utf8\");\n } catch {\n throw new AgentRosterNotFoundError(scope);\n }\n return validateAgentRoster(parseYaml(content, rosterPath), rosterPath);\n}\n\n/**\n * Returns true if a roster file exists in the target directory.\n */\nexport async function rosterExists(\n targetDir: string,\n scope: InstallScope = \"repo\",\n): Promise<boolean> {\n try {\n await access(join(agentsBaseDir(targetDir, scope), \"agents.yaml\"));\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Returns the path to the project's agent definitions directory.\n */\nexport function projectAgentsDir(\n targetDir: string,\n scope: InstallScope = \"repo\",\n): string {\n return join(agentsBaseDir(targetDir, scope), \"agents\");\n}\n","import type { AgentDefinition, AgentRoster } from \"../types.js\";\n\nconst CURRENT_ROSTER_SCHEMA_VERSION = 2;\nconst POWER_LEVELS = new Set([\n \"Spectator\",\n \"Apprentice\",\n \"Wizard\",\n \"Sorcerer\",\n \"Magus\",\n \"Archmage\",\n]);\nconst AGENT_CATEGORIES = new Set([\n \"engineering\",\n \"operations\",\n \"quality\",\n \"research\",\n]);\nconst NAMING_STRATEGIES = new Set([\"arcanos\", \"generic\", \"random\", \"custom\"]);\nconst AGENT_PROFILES = new Set([\"base\", \"full\", \"custom\"]);\n\ntype RecordValue = Record<string, unknown>;\n\nexport class AgentConfigValidationError extends Error {\n constructor(source: string, issues: string[]) {\n super(`Invalid agent configuration in ${source}:\\n- ${issues.join(\"\\n- \")}`);\n this.name = \"AgentConfigValidationError\";\n }\n}\n\nfunction isRecord(value: unknown): value is RecordValue {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction requireRecord(\n value: unknown,\n path: string,\n issues: string[],\n): RecordValue | undefined {\n if (!isRecord(value)) {\n issues.push(`${path} must be an object`);\n return undefined;\n }\n return value;\n}\n\nfunction requireString(value: unknown, path: string, issues: string[]) {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n issues.push(`${path} must be a non-empty string`);\n }\n}\n\nfunction requireBoolean(value: unknown, path: string, issues: string[]) {\n if (typeof value !== \"boolean\") issues.push(`${path} must be a boolean`);\n}\n\nfunction requireStringArray(value: unknown, path: string, issues: string[]) {\n if (!Array.isArray(value) || value.some((entry) => typeof entry !== \"string\")) {\n issues.push(`${path} must be an array of strings`);\n }\n}\n\nfunction requireEnum(\n value: unknown,\n allowed: Set<string>,\n path: string,\n issues: string[],\n) {\n if (typeof value !== \"string\" || !allowed.has(value)) {\n issues.push(`${path} must be one of: ${[...allowed].join(\", \")}`);\n }\n}\n\nfunction validateOptionalClient(\n clients: RecordValue,\n key: string,\n property: string,\n expected: \"string\" | \"boolean\",\n issues: string[],\n) {\n if (clients[key] === undefined) return;\n const client = requireRecord(clients[key], `clients.${key}`, issues);\n if (!client) return;\n if (expected === \"string\") {\n requireString(client[property], `clients.${key}.${property}`, issues);\n } else {\n requireBoolean(client[property], `clients.${key}.${property}`, issues);\n }\n}\n\nexport function validateAgentDefinition(\n value: unknown,\n source: string,\n): AgentDefinition {\n const issues: string[] = [];\n const definition = requireRecord(value, \"definition\", issues);\n if (!definition) throw new AgentConfigValidationError(source, issues);\n\n requireString(definition.id, \"id\", issues);\n requireString(definition.role, \"role\", issues);\n requireEnum(definition.category, AGENT_CATEGORIES, \"category\", issues);\n\n const persona = requireRecord(definition.persona, \"persona\", issues);\n if (persona) {\n requireString(persona.description, \"persona.description\", issues);\n requireStringArray(persona.behavioral_rules, \"persona.behavioral_rules\", issues);\n for (const key of [\"personality\", \"voice\", \"visual_description\"] as const) {\n if (persona[key] !== undefined) requireString(persona[key], `persona.${key}`, issues);\n }\n if (persona.catchphrases !== undefined) {\n requireStringArray(persona.catchphrases, \"persona.catchphrases\", issues);\n }\n }\n\n const model = requireRecord(definition.model, \"model\", issues);\n if (model) {\n requireString(model.primary, \"model.primary\", issues);\n requireStringArray(model.fallback, \"model.fallback\", issues);\n }\n\n const tools = requireRecord(definition.tools, \"tools\", issues);\n if (tools) {\n requireStringArray(tools.allowed, \"tools.allowed\", issues);\n requireStringArray(tools.denied, \"tools.denied\", issues);\n }\n\n const autonomy = requireRecord(definition.autonomy, \"autonomy\", issues);\n if (autonomy) {\n requireEnum(\n autonomy.default_power_level,\n POWER_LEVELS,\n \"autonomy.default_power_level\",\n issues,\n );\n requireBoolean(autonomy.exec_allowed, \"autonomy.exec_allowed\", issues);\n }\n\n const spawn = requireRecord(definition.spawn, \"spawn\", issues);\n if (spawn) {\n requireBoolean(spawn.can_spawn, \"spawn.can_spawn\", issues);\n requireStringArray(spawn.spawnable_by, \"spawn.spawnable_by\", issues);\n }\n\n const clients = requireRecord(definition.clients, \"clients\", issues);\n if (clients) {\n validateOptionalClient(clients, \"openclaw\", \"workspace_prefix\", \"string\", issues);\n validateOptionalClient(clients, \"copilot\", \"agent_file\", \"boolean\", issues);\n validateOptionalClient(clients, \"claude\", \"include_in_instructions\", \"boolean\", issues);\n validateOptionalClient(clients, \"codex\", \"include_in_agents_md\", \"boolean\", issues);\n }\n\n if (issues.length > 0) throw new AgentConfigValidationError(source, issues);\n return definition as unknown as AgentDefinition;\n}\n\nexport function validateAgentRoster(value: unknown, source: string): AgentRoster {\n const issues: string[] = [];\n const parsed = requireRecord(value, \"roster\", issues);\n if (!parsed) throw new AgentConfigValidationError(source, issues);\n\n const version = parsed.schema_version;\n if (!Number.isInteger(version)) {\n throw new AgentConfigValidationError(source, [\n \"schema_version must be an integer; run \\\"spell agents init\\\" with a compatible Arcane CLI\",\n ]);\n }\n if ((version as number) > CURRENT_ROSTER_SCHEMA_VERSION) {\n throw new AgentConfigValidationError(source, [\n `schema_version ${version} is newer than supported version ${CURRENT_ROSTER_SCHEMA_VERSION}; upgrade Arcane before reading this roster`,\n ]);\n }\n if ((version as number) < 1) {\n throw new AgentConfigValidationError(source, [\n `schema_version ${version} is unsupported; supported versions are 1 and ${CURRENT_ROSTER_SCHEMA_VERSION}`,\n ]);\n }\n\n const roster = version === 1\n ? { ...parsed, schema_version: CURRENT_ROSTER_SCHEMA_VERSION }\n : parsed;\n requireEnum(roster.naming_strategy, NAMING_STRATEGIES, \"naming_strategy\", issues);\n requireEnum(roster.agent_profile, AGENT_PROFILES, \"agent_profile\", issues);\n\n const openclaw = requireRecord(roster.openclaw, \"openclaw\", issues);\n if (openclaw) {\n requireBoolean(openclaw.enabled, \"openclaw.enabled\", issues);\n requireString(openclaw.workspace_root, \"openclaw.workspace_root\", issues);\n }\n\n if (!Array.isArray(roster.roster)) {\n issues.push(\"roster must be an array\");\n } else {\n roster.roster.forEach((entry, index) => {\n const item = requireRecord(entry, `roster[${index}]`, issues);\n if (!item) return;\n requireString(item.definition, `roster[${index}].definition`, issues);\n if (item.name !== null) requireString(item.name, `roster[${index}].name`, issues);\n requireString(item.id, `roster[${index}].id`, issues);\n if (item.epithet !== undefined) requireString(item.epithet, `roster[${index}].epithet`, issues);\n });\n }\n\n if (issues.length > 0) throw new AgentConfigValidationError(source, issues);\n return roster as unknown as AgentRoster;\n}","/**\n * Agent generator — multi-client fan-out engine.\n *\n * Reads canonical agent definitions and an agent roster, then generates\n * client-specific instruction files for each enabled AI client:\n * - OpenClaw: IDENTITY.md, SOUL.md, TOOLS.md per agent workspace\n * - Copilot: .github/agents/{name}.agent.md per agent\n * - Claude: CLAUDE.md ← arcane marker merge\n * - Codex: AGENTS.md ← arcane marker merge\n *\n * Each L1 merge (CLAUDE.md, AGENTS.md, .github/copilot-instructions.md) also\n * injects a static intent→spell routing table ahead of the roster table,\n * independent of whether any roster entries resolved — the routing guidance\n * (\"if a spell exists for this workflow, invoke it\") applies with or without\n * agent personas configured.\n *\n * NOTE: openclaw.json is NEVER modified directly — a patch file is generated\n * at .arcane/generated/openclaw-roster.json for the user to apply manually.\n */\n\nimport { writeFile, mkdir, readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type {\n AgentDefinition,\n AgentRoster,\n AgentRosterEntry,\n AgentSyncOptions,\n} from \"../types.js\";\nimport { mergeIntoFile } from \"./merger.js\";\nimport { loadAgentDefinition, projectAgentsDir } from \"./agent-loader.js\";\nimport { userAgentFanoutPath, type FanoutFile } from \"./user-tier.js\";\n\n// ─── Resolved entry (definition + roster entry joined) ───────────────────────\n\ninterface ResolvedEntry {\n entry: AgentRosterEntry;\n def: AgentDefinition;\n displayName: string;\n}\n\n// ─── Content renderers ───────────────────────────────────────────────────────\n\nfunction renderIdentity(def: AgentDefinition, name: string): string {\n const mottosLine =\n def.persona.catchphrases && def.persona.catchphrases.length > 0\n ? `\\n## Mottos\\n\\n${def.persona.catchphrases.map((c) => `- \"${c}\"`).join(\"\\n\")}\\n`\n : \"\";\n return `# ${name} — ${def.role}\n\n## Role\n\n${def.persona.description.trim()}\n\n## Category\n\n${def.category}\n${mottosLine}`;\n}\n\nfunction renderSoul(def: AgentDefinition, name: string): string {\n const rules = def.persona.behavioral_rules.map((r) => `- ${r}`).join(\"\\n\");\n return `# ${name} — Behavioral Directives\n\n## Rules\n\n${rules}\n\n## Autonomy\n\n- **Default Power Level:** ${def.autonomy.default_power_level}\n- **Exec Allowed:** ${def.autonomy.exec_allowed ? \"Yes\" : \"No\"}\n`;\n}\n\nfunction renderTools(def: AgentDefinition, name: string): string {\n const allowed = def.tools.allowed.map((t) => `- \\`${t}\\``).join(\"\\n\");\n const denied = def.tools.denied.map((t) => `- \\`${t}\\``).join(\"\\n\");\n return `# ${name} — Tool Capabilities\n\n## Allowed\n\n${allowed}\n\n## Denied\n\n${denied}\n`;\n}\n\n/**\n * The user tier's agent file. Same body as the repository tier's, one field\n * different: `description`.\n *\n * That field does two jobs at once here. VS Code shows it as the picker's\n * subtitle; Claude Code READS it to decide whether to hand work to this\n * subagent on its own. Twelve personas installed once per machine become\n * visible in every project on that machine, including ones with nothing to do\n * with Arcane, so the wording says plainly that the persona is something you\n * ask for by name rather than something to route work to (ARC-047, amended --\n * an operator who wants automatic delegation changes this one line).\n */\nexport function renderUserTierAgent(def: AgentDefinition, name: string): string {\n // The persona description is hard-wrapped YAML prose. Taking its first\n // LINE cuts mid-sentence (\"...and system design. Reviews\"); collapse the\n // wrapping first, then take the first whole sentence.\n const collapsed = def.persona.description.replace(/\\s+/g, \" \").trim();\n const firstSentence = /^.*?[.!?](?=\\s|$)/.exec(collapsed)?.[0] ?? def.role;\n // Everything after the rendered frontmatter block, kept verbatim: only\n // the description differs between the two tiers.\n const repoFile = renderCopilotAgent(def, name);\n const body = repoFile.slice(repoFile.indexOf(\"---\", 3) + 3);\n return `---\nname: ${name}\ndescription: ${def.role}. ${firstSentence} Use only when explicitly asked for ${name} by name; do not delegate to this persona automatically.\n---${body}`;\n}\n\nexport function renderCopilotAgent(def: AgentDefinition, name: string): string {\n const firstLine = def.persona.description.split(\"\\n\")[0]?.trim() ?? def.role;\n const rules = def.persona.behavioral_rules.map((r) => `- ${r}`).join(\"\\n\");\n const personalityBlock = def.persona.personality\n ? `\\n## Personality\\n\\n${def.persona.personality.trim()}\\n`\n : \"\";\n const voiceBlock = def.persona.voice\n ? `\\n## Voice\\n\\n${def.persona.voice.trim()}\\n`\n : \"\";\n const mottosBlock =\n def.persona.catchphrases && def.persona.catchphrases.length > 0\n ? `\\n## Mottos\\n\\n${def.persona.catchphrases.map((c) => `- \"${c}\"`).join(\"\\n\")}\\n`\n : \"\";\n return `---\nname: ${name}\ndescription: ${def.role} — ${firstLine}\n---\n${mottosBlock}\n## Role\n\n${def.persona.description.trim()}\n${personalityBlock}${voiceBlock}\n## Behavioral Rules\n\n${rules}\n\n## Tools\n\n**Allowed:** ${def.tools.allowed.join(\", \")}\n**Denied:** ${def.tools.denied.join(\", \")}\n`;\n}\n\nfunction renderSpellRoutingSection(): string {\n return `## Spell Routing\n\n| When you're about to... | Invoke |\n|---|---|\n| Commit work | \\`spell-commit-work\\` |\n| Open a session | \\`spell-open-session\\` |\n| Close a session | \\`spell-close-session\\` |\n| Open a pull request | \\`spell-create-pull-request\\` |\n| Ship a feature end-to-end | \\`spell-full-cycle\\` |\n| Fix a bug | \\`spell-bug\\` |\n| Review code or a PR | \\`spell-review\\` |\n\nIf a spell exists for the workflow you are about to perform, invoke it — do not improvise the workflow from general knowledge, even when the user doesn't name the spell.\n`;\n}\n\n/**\n * The branch-naming rule, read from its one source: the ARC-039 fragment\n * `.arcane/spells/_fragments/branch-naming.md` in the bundled assets. Every\n * client instruction file carries it so an assistant names the branch of a\n * worktree or a parallel agent by the rule instead of accepting a generated\n * one (TODO.md \"Claude Code worktree branches bypass Arcane's branch-naming\n * standard\"). A bundle without the fragment (older assets) gets no section,\n * never a throw.\n */\nasync function loadBranchNamingFragment(assetsDir: string): Promise<string | null> {\n try {\n const fragment = await readFile(\n join(assetsDir, \".arcane\", \"spells\", \"_fragments\", \"branch-naming.md\"),\n \"utf8\",\n );\n return fragment.trim();\n } catch {\n return null;\n }\n}\n\nfunction renderBranchNamingSection(fragment: string | null): string {\n if (fragment === null) return \"\";\n return `## Branch Naming\n\n${fragment}\n\n`;\n}\n\nfunction renderAgentRosterSection(entries: ResolvedEntry[]): string {\n const rows = entries\n .map(({ def, displayName }) =>\n `| **${displayName}** | ${def.role} | ${def.category} |`,\n )\n .join(\"\\n\");\n\n return `## Agent Roster\n\n| Agent | Role | Category |\n|-------|------|----------|\n${rows}\n`;\n}\n\n// ─── Sync result ─────────────────────────────────────────────────────────────\n\nexport interface SyncResult {\n synced: string[];\n skipped: string[];\n /**\n * True when a rostered role's definition could not be loaded from either\n * the project or bundled source (the mobile-dev bug class: a malformed\n * template silently dropped from the roster with a zero exit code).\n * Distinct from an openclaw-workspace write failure, which is an I/O\n * problem, not a role-resolution failure, and does not set this.\n */\n hasUnresolvedRoles: boolean;\n /**\n * User-tier runs only: the agent files rendered for the home fan-out,\n * unwritten. The caller reconciles them against the store manifest's\n * `fanout` record so an operator's edit survives (CS-06 / ARC-047).\n */\n userAgentFiles: FanoutFile[];\n /**\n * Opted-out repositories only: `.github/agents` files still on disk that\n * this run did not write and will not delete. They are not manifest-tracked\n * by any component, so there is no recorded hash to prove one untouched --\n * they are named for the operator to remove, never removed here.\n */\n unmanagedAgentFiles: string[];\n}\n\n// ─── Main sync function ───────────────────────────────────────────────────────\n\n/**\n * Generates all client-specific output files from the agent roster and definitions.\n *\n * @param targetDir Root of the consuming repo (where .arcane/ lives)\n * @param assetsDir Path to bundled assets (used as fallback for definitions not yet in project)\n * @param roster Parsed agent roster (from .arcane/agents.yaml)\n * @param options Sync options (dry-run, client filters)\n */\n/**\n * The `.agent.md` files already sitting in a repository's agent directory.\n * Used only to report what an opted-out repository still carries; a missing\n * or unreadable directory is simply \"none\".\n */\nasync function existingAgentFiles(agentsDir: string): Promise<string[]> {\n try {\n const entries = await readdir(agentsDir);\n return entries\n .filter((name) => name.endsWith(\".agent.md\"))\n .sort()\n .map((name) => `.github/agents/${name}`);\n } catch {\n return [];\n }\n}\n\nexport async function syncAgents(\n targetDir: string,\n assetsDir: string,\n roster: AgentRoster,\n options: AgentSyncOptions = {},\n): Promise<SyncResult> {\n const synced: string[] = [];\n const skipped: string[] = [];\n let hasUnresolvedRoles = false;\n const branchNaming = renderBranchNamingSection(await loadBranchNamingFragment(assetsDir));\n\n // CS-06 / ARC-047. Two independent axes, deliberately not one flag:\n // `scope` -- is THIS run writing the user tier's own agent files?\n // `spellScope` -- does this REPOSITORY take its client files from the\n // user tier, and therefore write no `.github/agents`?\n const userTier = options.scope === \"user\";\n const repoOptedOut = !userTier && options.spellScope === \"user\";\n const userAgentFiles: FanoutFile[] = [];\n const unmanagedAgentFiles: string[] = [];\n\n // ── Load all definitions ──────────────────────────────────────────────────\n // When the roster came from the user tier, its definition files come with\n // it: an operator who customized a role in the store should see that\n // customization, not the vendor default (CS-06 follow-up / ARC-045 d6).\n const projectDefs = options.definitionsDir ?? projectAgentsDir(targetDir, userTier ? \"user\" : \"repo\");\n const bundledDefs = join(assetsDir, \"agents\");\n\n const resolved: ResolvedEntry[] = [];\n for (const entry of roster.roster) {\n let def: AgentDefinition;\n try {\n // Prefer the project's customized version\n def = await loadAgentDefinition(projectDefs, entry.definition);\n } catch {\n try {\n // Fall back to bundled template\n def = await loadAgentDefinition(bundledDefs, entry.definition);\n } catch {\n skipped.push(`${entry.definition} (definition not found)`);\n hasUnresolvedRoles = true;\n continue;\n }\n }\n resolved.push({\n entry,\n def,\n displayName: entry.name ?? entry.definition,\n });\n }\n\n // ── OpenClaw output ──────────────────────────────────────────────────────\n // Never at the user tier: OpenClaw workspaces already live under\n // `~/.openclaw` and are home-scoped by construction, so there is no\n // per-folder duplication for a tier to solve and nothing to move.\n if (!userTier && roster.openclaw.enabled && options.openclaw !== false) {\n const openclawRoot = roster.openclaw.workspace_root.replace(\n /^~/,\n homedir(),\n );\n\n for (const { entry, def, displayName } of resolved) {\n if (!def.clients.openclaw) continue;\n\n const workspaceName =\n entry.id === \"main\" ? \"workspace\" : `workspace-${entry.id}`;\n const workspaceDir = join(openclawRoot, workspaceName);\n\n try {\n if (!options.dryRun) {\n await mkdir(workspaceDir, { recursive: true });\n await writeFile(\n join(workspaceDir, \"IDENTITY.md\"),\n renderIdentity(def, displayName),\n \"utf8\",\n );\n await writeFile(\n join(workspaceDir, \"SOUL.md\"),\n renderSoul(def, displayName),\n \"utf8\",\n );\n await writeFile(\n join(workspaceDir, \"TOOLS.md\"),\n renderTools(def, displayName),\n \"utf8\",\n );\n } else {\n console.log(` [dry-run] Would write: ${workspaceDir}/IDENTITY.md`);\n console.log(` [dry-run] Would write: ${workspaceDir}/SOUL.md`);\n console.log(` [dry-run] Would write: ${workspaceDir}/TOOLS.md`);\n }\n synced.push(`openclaw:${entry.id}:IDENTITY+SOUL+TOOLS`);\n } catch {\n skipped.push(`openclaw:${entry.id} (workspace not accessible)`);\n }\n }\n\n // Generate openclaw-roster.json patch — never modifies openclaw.json directly\n const patch = {\n $comment:\n \"Apply this to your openclaw.json agents section. \" +\n \"Do not edit manually — regenerated by `spell agents sync`.\",\n agents: resolved.map(({ entry, def }) => ({\n id: entry.id,\n identity: {\n name: entry.name ?? entry.definition,\n ...(entry.epithet ? { epithet: entry.epithet } : {}),\n role: def.role,\n },\n })),\n };\n\n const generatedDir = join(targetDir, \".arcane\", \"generated\");\n if (!options.dryRun) {\n await mkdir(generatedDir, { recursive: true });\n await writeFile(\n join(generatedDir, \"openclaw-roster.json\"),\n JSON.stringify(patch, null, 2) + \"\\n\",\n \"utf8\",\n );\n } else {\n console.log(\n \" [dry-run] Would write: .arcane/generated/openclaw-roster.json\",\n );\n }\n synced.push(\"openclaw-roster.json\");\n }\n\n // ── Copilot output ───────────────────────────────────────────────────────\n if (options.copilot !== false) {\n const agentsDir = join(targetDir, \".github\", \"agents\");\n\n // An opted-out repository still needs `.github` to exist, because the\n // copilot-instructions merge below writes into it and used to rely on\n // this mkdir creating the parent as a side effect.\n if (!options.dryRun && !userTier) {\n await mkdir(repoOptedOut ? join(targetDir, \".github\") : agentsDir, { recursive: true });\n }\n\n for (const { def, displayName } of resolved) {\n if (!def.clients.copilot?.agent_file) continue;\n // Slugify: lowercase, spaces → hyphens (e.g. \"QA Lead\" → \"qa-lead.agent.md\")\n const slug = displayName.toLowerCase().replace(/\\s+/g, \"-\");\n const fileName = `${slug}.agent.md`;\n const content = userTier\n ? renderUserTierAgent(def, displayName)\n : renderCopilotAgent(def, displayName);\n\n // The user tier renders the same bytes to a home-relative path and\n // hands them back rather than writing them here: the write goes through\n // the fan-out reconcile, so an operator's edit to one of these files is\n // recognized and left alone (ARC-038, as applied by CS-04).\n if (userTier) {\n const relativePath = userAgentFanoutPath(slug);\n userAgentFiles.push({ relativePath, client: \"copilot-agents\", content });\n synced.push(`~/${relativePath}`);\n continue;\n }\n\n // This repository takes its client files from the user tier, so it\n // gains no agent file of its own -- writing one would add a second\n // picker entry beside the tier's, not replace it.\n if (repoOptedOut) continue;\n\n if (!options.dryRun) {\n await writeFile(join(agentsDir, fileName), content, \"utf8\");\n } else {\n console.log(` [dry-run] Would write: .github/agents/${fileName}`);\n }\n synced.push(`.github/agents/${fileName}`);\n }\n\n if (repoOptedOut) {\n unmanagedAgentFiles.push(...(await existingAgentFiles(agentsDir)));\n }\n\n // Merge spell routing + roster into copilot-instructions.md. Repository\n // continuity content, not a client discovery surface: it stays in an\n // opted-out repository (ARC-045 decision 5) and is never written at the\n // user tier, which has no repository to describe.\n if (!userTier) {\n const copilotSection = `${renderSpellRoutingSection()}\\n${branchNaming}${renderAgentRosterSection(resolved)}`;\n const copilotMerged = await mergeIntoFile(\n targetDir,\n \".github/copilot-instructions.md\",\n copilotSection,\n { force: true, dryRun: options.dryRun },\n );\n if (copilotMerged)\n synced.push(\".github/copilot-instructions.md (agents section)\");\n }\n }\n\n if (userTier) {\n return { synced, skipped, hasUnresolvedRoles, userAgentFiles, unmanagedAgentFiles };\n }\n\n // ── Claude output ────────────────────────────────────────────────────────\n if (options.claude !== false) {\n const claudeSection = `${renderSpellRoutingSection()}\\n${branchNaming}${renderAgentRosterSection(resolved)}`;\n const claudeMerged = await mergeIntoFile(\n targetDir,\n \"CLAUDE.md\",\n claudeSection,\n { force: true, dryRun: options.dryRun },\n );\n if (claudeMerged) synced.push(\"CLAUDE.md (agents section)\");\n }\n\n // ── Codex output ─────────────────────────────────────────────────────────\n if (options.codex !== false) {\n const codexSection = `${renderSpellRoutingSection()}\\n${branchNaming}${renderAgentRosterSection(resolved)}`;\n const codexMerged = await mergeIntoFile(\n targetDir,\n \"AGENTS.md\",\n codexSection,\n { force: true, dryRun: options.dryRun },\n );\n if (codexMerged) synced.push(\"AGENTS.md (agents section)\");\n }\n\n return { synced, skipped, hasUnresolvedRoles, userAgentFiles, unmanagedAgentFiles };\n}\n","import { readFile, writeFile, rm } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { validateTargetPath } from \"./copier.js\";\n\nexport const MARKER_START = \"<!-- arcane:start -->\";\nexport const MARKER_END = \"<!-- arcane:end -->\";\n\nexport class MalformedMarkersError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"MalformedMarkersError\";\n }\n}\n\n/**\n * Merges `content` into `targetDir/relativePath` using arcane marker comments.\n *\n * Behaviour:\n * - If both markers exist: replaces everything between them with `content`.\n * - If neither marker exists and `force` is true: appends markers + content.\n * - If neither marker exists and `force` is false: no-op, returns false.\n * - If only one marker is present: throws MalformedMarkersError.\n * - If file doesn't exist and `force` is true: creates the file with markers.\n * - If file doesn't exist and `force` is false: no-op, returns false.\n *\n * @returns true if the file was modified (or would be on dry-run), false if untouched.\n */\nexport async function mergeIntoFile(\n targetDir: string,\n relativePath: string,\n content: string,\n opts: { force?: boolean; dryRun?: boolean } = {},\n): Promise<boolean> {\n validateTargetPath(targetDir, relativePath);\n\n const destPath = path.resolve(targetDir, relativePath);\n\n let existing: string | null;\n try {\n existing = await readFile(destPath, \"utf8\");\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\") {\n existing = null;\n } else {\n throw err;\n }\n }\n\n if (existing === null) {\n // File doesn't exist — create with markers only if --force\n if (!opts.force) return false;\n const newContent = `${MARKER_START}\\n${content}\\n${MARKER_END}\\n`;\n if (!opts.dryRun) {\n await writeFile(destPath, newContent, \"utf8\");\n }\n return true;\n }\n\n const startIdx = existing.indexOf(MARKER_START);\n const endIdx = existing.indexOf(MARKER_END);\n\n const hasStart = startIdx !== -1;\n const hasEnd = endIdx !== -1;\n\n if (hasStart !== hasEnd) {\n throw new MalformedMarkersError(\n `File \"${relativePath}\" has mismatched arcane markers: ` +\n `${hasStart ? \"start found\" : \"start missing\"}, ` +\n `${hasEnd ? \"end found\" : \"end missing\"}.`,\n );\n }\n\n if (hasStart && hasEnd) {\n if (startIdx > endIdx) {\n throw new MalformedMarkersError(\n `File \"${relativePath}\" has arcane markers in wrong order (end before start).`,\n );\n }\n // Replace content between the markers (keeps marker lines themselves intact)\n const beforeMarker = existing.slice(0, startIdx + MARKER_START.length);\n const afterMarker = existing.slice(endIdx);\n const updated = `${beforeMarker}\\n${content}\\n${afterMarker}`;\n if (!opts.dryRun) {\n await writeFile(destPath, updated, \"utf8\");\n }\n return true;\n }\n\n // No markers — append only if --force\n if (!opts.force) return false;\n\n const appended =\n `${existing}${existing.endsWith(\"\\n\") ? \"\" : \"\\n\"}` +\n `${MARKER_START}\\n${content}\\n${MARKER_END}\\n`;\n if (!opts.dryRun) {\n await writeFile(destPath, appended, \"utf8\");\n }\n return true;\n}\n\n/**\n * Removes the `<!-- arcane:start -->…<!-- arcane:end -->` block from a file.\n *\n * - If the file has no markers, returns false (no-op).\n * - If the file is empty (or whitespace-only) after stripping, deletes the file.\n * - Otherwise rewrites the file without the marker block.\n *\n * @returns true if the file was modified or deleted, false if untouched.\n */\nexport async function stripMarkerSection(\n targetDir: string,\n relativePath: string,\n opts: { dryRun?: boolean } = {},\n): Promise<boolean> {\n const destPath = path.resolve(targetDir, relativePath);\n\n let existing: string;\n try {\n existing = await readFile(destPath, \"utf8\");\n } catch {\n return false; // file doesn't exist\n }\n\n const startIdx = existing.indexOf(MARKER_START);\n const endIdx = existing.indexOf(MARKER_END);\n if (startIdx === -1 || endIdx === -1) return false;\n\n const before = existing.slice(0, startIdx);\n const after = existing.slice(endIdx + MARKER_END.length);\n const stripped = (before + after).replace(/\\n{3,}/g, \"\\n\\n\").trim();\n\n if (!opts.dryRun) {\n if (stripped.length === 0) {\n await rm(destPath, { force: true });\n } else {\n await writeFile(destPath, stripped + \"\\n\", \"utf8\");\n }\n }\n return true;\n}\n","import { confirm, select, input } from \"@inquirer/prompts\";\nimport { readdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { fileExists } from \"./copier.js\";\nimport { detectAdoContext } from \"./ado-context.js\";\nimport {\n isValidSubjectRoot,\n validateManifestFields,\n ManifestInvalidFieldError,\n MANIFEST_ENUM_VALUES,\n} from \"./manifest.js\";\nimport type {\n ArcaneManifest,\n ContentSensitivity,\n ExternalProvider,\n HubRole,\n ManifestFlags,\n PushPolicy,\n TrackingMode,\n} from \"../types.js\";\n\n/**\n * A manifest-field retrofit: a schema question `spell update` asks exactly\n * once, for an installed manifest that predates the field. This is a general\n * migration mechanism, not a one-off for `role` -- future manifest fields\n * register a retrofit here instead of leaving older installs permanently\n * unset.\n */\nexport interface ManifestRetrofit {\n /** Field this retrofit introduces on ArcaneManifest, for logging only. */\n field: string;\n /** True if the installed manifest predates this field and should be asked. */\n needsRetrofit(manifest: ArcaneManifest): boolean;\n /**\n * Ask the operator (or apply a deterministic default); return the partial\n * patch to merge into the manifest. Receives the manifest so a retrofit\n * can branch on already-known fields (e.g. profile) the way the\n * tracking_mode retrofit below does -- entries that don't need it (role)\n * simply ignore the parameter.\n */\n ask(manifest: ArcaneManifest, targetDir?: string): Promise<Partial<ArcaneManifest>>;\n /** The flag that answers this question without a prompt (PRD D-02). */\n flag: string;\n}\n\n/**\n * The tracking-mode and provider questions, shared by init's Step 5b and the\n * tracking_mode retrofit. With an Azure DevOps remote (PRD D-03), external\n * and then ado are pre-selected; otherwise nothing is. Each option names its\n * downstream effect in one line.\n */\nexport async function askTrackingQuestions(\n targetDir: string | undefined,\n): Promise<{ tracking_mode: TrackingMode; external_provider: ExternalProvider | null }> {\n const ado = targetDir !== undefined && (await detectAdoContext(targetDir));\n const tracking_mode = (await select({\n message: \"How will work be tracked in this repo?\",\n choices: [\n {\n value: \"internal\",\n name: \"Track work in this repo (TODO.md / PRDs)\",\n description: \"Spells keep TODOs, PRDs and stories in this repository's own files.\",\n },\n {\n value: \"external\",\n name: \"Track work in an external tracker (Azure DevOps / GitHub / Jira / other)\",\n description: \"Spells tie PRDs, commits and PRs to items in the tracker you pick next.\",\n },\n ],\n ...(ado ? { default: \"external\" } : {}),\n })) as TrackingMode;\n if (tracking_mode !== \"external\") return { tracking_mode, external_provider: null };\n const external_provider = (await select({\n message: \"Which external tracker?\",\n choices: [\n {\n value: \"ado\",\n name: \"Azure DevOps\",\n description: \"Each PRD needs an ADO work item ID; plans follow your process template's item types.\",\n },\n {\n value: \"github\",\n name: \"GitHub Issues\",\n description: \"Uses the gh CLI; an issue number is optional and linked from commits and PRs when known.\",\n },\n {\n value: \"jira\",\n name: \"Jira\",\n description: \"No Jira automation yet: spells note linking as a TODO and keep planning in this repo.\",\n },\n {\n value: \"other\",\n name: \"Other\",\n description: \"No automation: spells note linking as a TODO and keep planning in this repo.\",\n },\n ],\n ...(ado ? { default: \"ado\" } : {}),\n })) as ExternalProvider;\n return { tracking_mode, external_provider };\n}\n\nexport const MANIFEST_RETROFITS: ManifestRetrofit[] = [\n {\n field: \"role\",\n flag: \"--role <hub|consumer>\",\n needsRetrofit: (m) => m.role === undefined,\n ask: async () => {\n const isHub = await confirm({\n message:\n \"Will this repo manage other ventures as a hub? (idea books, spell-manifest promotion, venture registry)\",\n default: false,\n });\n return { role: (isHub ? \"hub\" : \"consumer\") as HubRole };\n },\n },\n {\n field: \"tracking_mode\",\n flag: \"--tracking-mode <internal|external>\",\n needsRetrofit: (m) => m.tracking_mode === undefined,\n // Mirrors init.ts's Step 5b branching exactly (EF-14 D5): docs-only\n // profiles get a silent default, full/lite get asked -- through the same\n // askTrackingQuestions init uses, so the two cannot drift.\n ask: async (manifest, targetDir) => {\n if (\n manifest.profile === \"governance-only\" ||\n manifest.profile === \"methodology\" ||\n manifest.profile === \"docs\"\n ) {\n return { tracking_mode: \"internal\" as TrackingMode, external_provider: null };\n }\n return askTrackingQuestions(targetDir);\n },\n },\n {\n field: \"content_sensitivity\",\n flag: \"--content-sensitivity <standard|sensitive>\",\n needsRetrofit: (m) => m.content_sensitivity === undefined,\n // EF-12. Asked for every profile -- a code repo can hold sensitive records\n // too -- and defaults to \"standard\", so an operator who just presses enter\n // keeps today's behaviour exactly.\n ask: async () => {\n const content_sensitivity = (await select({\n message: \"How should agents treat this repository's contents?\",\n choices: [\n {\n value: \"standard\",\n name: \"Standard — agents may quote contents in journals and decisions\",\n },\n {\n value: \"sensitive\",\n name: \"Sensitive — agents reference documents by path, never transcribe them\",\n },\n ],\n default: \"standard\",\n })) as ContentSensitivity;\n return { content_sensitivity };\n },\n },\n {\n field: \"subject_root\",\n flag: \"--subject-root <path>\",\n // EF-07. Only the docs profile is asked: other profiles describe code or a\n // venture portfolio, where \"what is this repo about\" is already answered.\n // A docs install that legitimately holds several subjects answers\n // \"portfolio\" and stays unset -- so this retrofit deliberately does NOT\n // re-ask on every update. It is gated on the field being absent AND the\n // profile being docs, and once answered (either way) it never fires again,\n // because \"portfolio\" writes an explicit empty marker.\n needsRetrofit: (m) => m.profile === \"docs\" && m.subject_root === undefined,\n ask: async () => {\n const shape = await select({\n message: \"What does this repository hold?\",\n choices: [\n {\n value: \"root\",\n name: \"One subject, at the repository root (documents sit alongside Arcane's files)\",\n },\n { value: \"subdir\", name: \"One subject, in its own directory\" },\n { value: \"portfolio\", name: \"Several subjects or ventures\" },\n ],\n });\n if (shape === \"root\") return { subject_root: \".\" };\n if (shape === \"subdir\") {\n const answer = await input({\n message: \"Directory holding the subject's documents:\",\n default: \"docs\",\n validate: (v) =>\n isValidSubjectRoot(v.trim() || \"docs\") ||\n \"Must be a relative path inside the repository (no leading /, drive letter, or ..).\",\n });\n return { subject_root: answer.trim() || \"docs\" };\n }\n // Portfolio: business_root covers this shape. Recorded as an explicit\n // null rather than left unset, so the question isn't re-asked on every\n // future update.\n return { subject_root: null };\n },\n },\n {\n field: \"push_policy\",\n flag: \"--push-policy <open|guarded|blocked>\",\n needsRetrofit: (m) => m.push_policy === undefined,\n // EF-09. Defaults to \"open\", so an operator who just presses enter keeps\n // today's behaviour exactly. NOTE: this only records the choice -- unlike\n // init, the retrofit does not install the hook or disable the push URL,\n // because an update should not silently start blocking pushes in a repo\n // someone is mid-workflow in (ARC-034 decision 7). ARC-049 decision 2:\n // update prints pushPolicyNotice() instead, naming `spell block-push`.\n ask: async () => {\n const push_policy = (await select({\n message: \"Should this repository be allowed to push to a remote?\",\n choices: [\n { value: \"open\", name: \"Yes — normal repository\" },\n { value: \"guarded\", name: \"Sensitive, but keep push working — remind me instead\" },\n { value: \"blocked\", name: \"No — block pushes\" },\n ],\n default: \"open\",\n })) as PushPolicy;\n return { push_policy };\n },\n },\n];\n\n/**\n * What `spell update` prints after it records a push_policy (ARC-049\n * decision 2). Update never installs controls (ARC-034 decision 7), so a\n * recorded \"blocked\" is not yet in force and must say so.\n */\nexport function pushPolicyNotice(policy: PushPolicy | undefined): string[] {\n if (policy === \"blocked\") {\n return [\n \"\",\n ' ! Required action: push_policy is now recorded as \"blocked\", but it is NOT enforced yet.',\n \" `spell update` never installs push controls. Pushes from this repository still work\",\n \" until you run:\",\n \"\",\n \" spell block-push\",\n \"\",\n ];\n }\n if (policy === \"guarded\") {\n return [\n \"\",\n ' push_policy is now recorded as \"guarded\". Nothing is installed for it: `spell doctor`',\n \" reports it, and push-performing spells state the policy and ask before pushing.\",\n \"\",\n ];\n }\n return [];\n}\n\n/**\n * Runs every retrofit question the installed manifest predates, in order.\n * Returns the partial patch to merge into the manifest being written.\n * Returns {} immediately (no questions, no console output) if nothing\n * applies -- callers should only invoke this outside dry-run and\n * non-interactive modes.\n */\nexport async function runManifestRetrofits(\n manifest: ArcaneManifest,\n targetDir?: string,\n): Promise<Partial<ArcaneManifest>> {\n const applicable = MANIFEST_RETROFITS.filter((r) => r.needsRetrofit(manifest));\n if (applicable.length === 0) return {};\n\n console.log();\n console.log(\n `This install predates ${applicable.length} manifest field${applicable.length === 1 ? \"\" : \"s\"} -- a couple of quick questions:`,\n );\n\n let patch: Partial<ArcaneManifest> = {};\n for (const retrofit of applicable) {\n const answer = await retrofit.ask({ ...manifest, ...patch }, targetDir);\n patch = { ...patch, ...answer };\n }\n return patch;\n}\n\n/**\n * The command line that answers every still-pending question by flags, for a\n * run that could not ask them (PRD D-02). Undefined when nothing is pending.\n */\nexport function manifestFlagLine(manifest: ArcaneManifest): string[] | undefined {\n const pending = MANIFEST_RETROFITS.filter((r) => r.needsRetrofit(manifest));\n if (pending.length === 0) return undefined;\n const lines = [`spell update ${pending.map((r) => r.flag).join(\" \")}`];\n if (pending.some((r) => r.field === \"tracking_mode\")) {\n lines.push(\"(--tracking-mode external also needs --external-provider <ado|github|jira|other>)\");\n }\n return lines;\n}\n\n/** An operator-facing refusal of a manifest flag; the CLI prints it and exits 1. */\nexport class ManifestFlagError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ManifestFlagError\";\n }\n}\n\nconst FLAG_NAMES: Record<string, string> = {\n role: \"--role\",\n tracking_mode: \"--tracking-mode\",\n external_provider: \"--external-provider\",\n content_sensitivity: \"--content-sensitivity\",\n subject_root: \"--subject-root\",\n push_policy: \"--push-policy\",\n};\n\nconst PUSH_POLICY_STRICTNESS: Record<PushPolicy, number> = { open: 0, guarded: 1, blocked: 2 };\n\nexport function hasManifestFlags(flags: ManifestFlags | undefined): flags is ManifestFlags {\n return flags !== undefined && Object.values(flags).some((v) => v !== undefined);\n}\n\n/**\n * Validates the manifest-question flags and returns the patch they make to\n * `current` (an empty object for a fresh init). Throws ManifestFlagError.\n *\n * A flag may answer an unset question or TIGHTEN push_policy. It never\n * changes a recorded answer, and it never loosens push_policy: ARC-034\n * decision 6 keeps that interactive-only, in `spell unblock-push`.\n */\nexport function resolveManifestFlags(\n flags: ManifestFlags | undefined,\n current: Partial<ArcaneManifest>,\n manifestPath: string,\n): Partial<ArcaneManifest> {\n if (!hasManifestFlags(flags)) return {};\n\n const requested: Partial<Record<keyof ArcaneManifest, unknown>> = {};\n if (flags.role !== undefined) requested.role = flags.role;\n if (flags.trackingMode !== undefined) requested.tracking_mode = flags.trackingMode;\n if (flags.externalProvider !== undefined) requested.external_provider = flags.externalProvider;\n if (flags.contentSensitivity !== undefined) requested.content_sensitivity = flags.contentSensitivity;\n if (flags.subjectRoot !== undefined) requested.subject_root = flags.subjectRoot;\n if (flags.pushPolicy !== undefined) requested.push_policy = flags.pushPolicy;\n\n const invalid = (error: ManifestInvalidFieldError): ManifestFlagError => {\n const allowed = error.field === \"role\" ? [\"hub\", \"consumer\"] : MANIFEST_ENUM_VALUES[error.field];\n const hint = allowed ? ` Valid values: ${allowed.join(\", \")}.` : \"\";\n return new ManifestFlagError(`Invalid ${FLAG_NAMES[error.field] ?? error.field}: ${error.message}${hint}`);\n };\n // role has no manifest validator of its own; same error class, same wording.\n if (requested.role !== undefined && requested.role !== \"hub\" && requested.role !== \"consumer\") {\n throw invalid(new ManifestInvalidFieldError(manifestPath, \"role\", requested.role));\n }\n try {\n validateManifestFields(requested as Partial<ArcaneManifest>, manifestPath);\n } catch (error) {\n if (error instanceof ManifestInvalidFieldError) throw invalid(error);\n throw error;\n }\n\n if (requested.external_provider !== undefined && requested.tracking_mode !== \"external\") {\n throw new ManifestFlagError(\"--external-provider applies only together with --tracking-mode external.\");\n }\n if (requested.tracking_mode === \"external\" && requested.external_provider === undefined) {\n throw new ManifestFlagError(\n \"--tracking-mode external needs --external-provider <ado|github|jira|other> as well.\",\n );\n }\n if (requested.tracking_mode === \"internal\") requested.external_provider = null;\n\n const patch: Partial<Record<keyof ArcaneManifest, unknown>> = {};\n for (const field of Object.keys(FLAG_NAMES) as (keyof ArcaneManifest)[]) {\n if (!(field in requested)) continue;\n const value = requested[field];\n const recorded = current[field];\n if (recorded === undefined) {\n patch[field] = value;\n continue;\n }\n if (field === \"push_policy\") {\n const from = PUSH_POLICY_STRICTNESS[recorded as PushPolicy];\n const to = PUSH_POLICY_STRICTNESS[value as PushPolicy];\n if (to < from) {\n throw new ManifestFlagError(\n `--push-policy ${String(value)} would loosen this repository's recorded push_policy ` +\n `\"${String(recorded)}\". No flag can loosen it: run \\`spell unblock-push\\` from an ` +\n \"interactive terminal.\",\n );\n }\n if (to > from) patch[field] = value;\n continue;\n }\n if (recorded === value) continue;\n // external_provider is implied by --tracking-mode internal; name the flag the operator typed.\n const flag =\n field === \"external_provider\" && flags.externalProvider === undefined ? \"--tracking-mode\" : FLAG_NAMES[field];\n throw new ManifestFlagError(\n `${flag} conflicts with the recorded ${field}: ${JSON.stringify(recorded)}. A flag only answers ` +\n \"a question that has no recorded answer; change .arcane.json deliberately if the recorded \" +\n \"value is wrong.\",\n );\n }\n return patch as Partial<ArcaneManifest>;\n}\n\n/** One line per field a flag recorded, for the run's output. */\nexport function describeFlagPatch(patch: Partial<ArcaneManifest>): string[] {\n return Object.entries(patch)\n .filter(([field]) => field in FLAG_NAMES)\n .map(([field, value]) => `${field}: ${JSON.stringify(value)}`);\n}\n\n/**\n * Offers to scaffold `{business_root}/registry.json` from existing venture\n * folders, once `role` has just become \"hub\" (via init or retrofit). Never\n * overwrites an existing registry. No-ops silently (no prompt, no output) if\n * the business root doesn't exist or holds no venture folders -- a brand new\n * hub with nothing under `ventures/` yet has nothing to scaffold.\n */\nexport async function offerRegistryScaffold(\n targetDir: string,\n businessRoot: string,\n): Promise<void> {\n const registryPath = join(targetDir, businessRoot, \"registry.json\");\n if (await fileExists(registryPath)) return;\n\n const businessRootPath = join(targetDir, businessRoot);\n let ventureDirs: string[];\n try {\n const entries = await readdir(businessRootPath, { withFileTypes: true });\n ventureDirs = entries\n .filter((e) => e.isDirectory() && e.name !== \"_template\")\n .map((e) => e.name);\n } catch {\n return;\n }\n if (ventureDirs.length === 0) return;\n\n console.log();\n const scaffold = await confirm({\n message: `Found ${ventureDirs.length} folder${ventureDirs.length === 1 ? \"\" : \"s\"} under ${businessRoot}/ -- scaffold ${businessRoot}/registry.json from them?`,\n default: true,\n });\n if (!scaffold) return;\n\n const ventures: Record<string, unknown> = {};\n for (const slug of ventureDirs) {\n const include = await confirm({ message: ` Include \"${slug}\"?`, default: true });\n if (!include) continue;\n ventures[slug] = {\n name: slug,\n aliases: [],\n status: \"active\",\n visibility: \"private\",\n ownership: \"llc\",\n tracking: \"none\",\n repos: [],\n };\n }\n if (Object.keys(ventures).length === 0) {\n console.log(\" No ventures selected -- registry not created.\");\n return;\n }\n\n const registry = {\n _comment:\n \"Hub-owned venture registry. Private. Never installed or modified by spell update. \" +\n \"Seeded by the retrofit wizard -- review and enrich (aliases, ownership, tracking, \" +\n \"clones) before relying on it.\",\n updated: new Date().toISOString().slice(0, 10),\n ventures,\n };\n await writeFile(registryPath, JSON.stringify(registry, null, 2) + \"\\n\", \"utf-8\");\n console.log(\n ` ✓ Scaffolded ${businessRoot}/registry.json with ${Object.keys(ventures).length} venture(s).`,\n );\n}\n","import { execFile } from \"node:child_process\";\n\nexport type GitRepositoryState =\n | { status: \"ready\"; uncommittedChanges: number }\n | { status: \"not-repository\" }\n | { status: \"no-commits\" };\n\nexport type GitCommandClass = \"read\" | \"write\" | \"network\";\n\nexport interface RunGitOptions {\n /** Override auto-classification from the subcommand. */\n commandClass?: GitCommandClass;\n /** Override the command class's default timeout, in milliseconds. */\n timeoutMs?: number;\n}\n\nexport interface GitResult {\n stdout: string;\n stderr: string;\n}\n\n/**\n * EF-20: thrown when a `git` subprocess exceeds its runGit-assigned timeout\n * and Node reports the resulting kill via `error.killed`. Note this reflects\n * \"the child was killed by a signal Node sent it,\" which today only ever\n * happens through runGit's own timeout, since no caller retains the\n * underlying ChildProcess to kill it independently -- if a future caller\n * gains that ability, `error.killed` alone would no longer distinguish\n * runGit's timeout from an external kill of the same handle.\n */\nexport class GitTimeoutError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"GitTimeoutError\";\n }\n}\n\nconst NETWORK_SUBCOMMANDS = new Set([\"fetch\", \"pull\", \"push\", \"clone\", \"ls-remote\", \"send-email\"]);\n\nconst WRITE_SUBCOMMANDS = new Set([\n \"init\",\n \"commit\",\n \"add\",\n \"checkout\",\n \"switch\",\n \"merge\",\n \"rebase\",\n \"reset\",\n \"tag\",\n \"mv\",\n \"rm\",\n \"cherry-pick\",\n \"revert\",\n \"stash\",\n \"apply\",\n \"am\",\n \"restore\",\n \"submodule\",\n \"sparse-checkout\",\n \"maintenance\",\n \"notes\",\n]);\n\n// These subcommands can be read-only (`branch --list`, `config --get`) or\n// mutating (`branch -m`, `config user.name x`) depending on flags. Classified\n// as \"write\" -- the safer over-estimate: a read given write's longer timeout\n// just waits a little longer, while a write given read's shorter timeout\n// risks being killed mid-operation.\nconst AMBIGUOUS_AS_WRITE = new Set([\"branch\", \"config\", \"remote\", \"worktree\"]);\n\n// Known read-only subcommands get the fast 15s budget explicitly. Anything\n// NOT in this list, NOT in WRITE_SUBCOMMANDS/AMBIGUOUS_AS_WRITE, and NOT in\n// NETWORK_SUBCOMMANDS is a subcommand this module doesn't recognize at all --\n// that case defaults to \"write\" (see classifyGitCommand), the safer\n// over-estimate for a genuinely unknown operation.\nconst READ_SUBCOMMANDS = new Set([\n \"status\",\n \"rev-parse\",\n \"log\",\n \"diff\",\n \"show\",\n \"describe\",\n \"cat-file\",\n \"ls-files\",\n \"ls-tree\",\n \"symbolic-ref\",\n \"show-ref\",\n \"for-each-ref\",\n \"merge-base\",\n \"blame\",\n \"shortlog\",\n \"grep\",\n \"reflog\",\n \"diff-tree\",\n \"rev-list\",\n \"name-rev\",\n \"count-objects\",\n \"verify-commit\",\n \"verify-tag\",\n \"help\",\n \"version\",\n]);\n\n// Global options that take their value as a SEPARATE following token (not\n// `--opt=value`). Without skipping the value too, `args.find(a =>\n// !a.startsWith(\"-\"))` would grab the value itself (e.g. \"user.name=x\" from\n// `-c user.name=x commit`) and misclassify the real subcommand that follows.\nconst GLOBAL_FLAGS_WITH_SEPARATE_VALUE = new Set([\"-c\", \"-C\"]);\n\nconst DEFAULT_TIMEOUTS_MS: Record<GitCommandClass, number> = {\n read: 15_000,\n write: 30_000,\n network: 120_000,\n};\n\n/**\n * Classify a git invocation by its first real subcommand token, skipping any\n * leading global flags (and, for `-c`/`-C`, their separate-token values) to\n * find it. Exported for tests.\n */\nexport function classifyGitCommand(args: string[]): GitCommandClass {\n let subcommand: string | undefined;\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (GLOBAL_FLAGS_WITH_SEPARATE_VALUE.has(arg)) {\n i++; // skip this flag's value token too\n continue;\n }\n if (arg.startsWith(\"-\")) continue;\n subcommand = arg;\n break;\n }\n if (!subcommand) return \"read\";\n if (NETWORK_SUBCOMMANDS.has(subcommand)) return \"network\";\n if (WRITE_SUBCOMMANDS.has(subcommand) || AMBIGUOUS_AS_WRITE.has(subcommand)) return \"write\";\n if (READ_SUBCOMMANDS.has(subcommand)) return \"read\";\n // A subcommand this module doesn't recognize at all: default to \"write\"\n // (the safer over-estimate) rather than \"read\" -- see READ_SUBCOMMANDS.\n return \"write\";\n}\n\n/**\n * EF-20/EF-13: the non-interactive execution environment applied to every\n * git invocation. GIT_TERMINAL_PROMPT and GCM_INTERACTIVE suppress credential\n * and terminal prompts; GIT_OPTIONAL_LOCKS disables git's speculative index\n * lock during `status` (git-config(1): \"core.optionalLocks\" affects only\n * status/update-index, so this is safe to apply blanket rather than per\n * command class) -- the mitigation for EF-13's stranded-lock report on\n * filesystems that reject unlink. Exported for tests.\n */\nexport function buildGitEnv(): NodeJS.ProcessEnv {\n return {\n ...process.env,\n GIT_TERMINAL_PROMPT: \"0\",\n GCM_INTERACTIVE: \"Never\",\n GIT_OPTIONAL_LOCKS: \"0\",\n };\n}\n\n/**\n * Single chokepoint for every production `git` invocation. Closes stdin\n * immediately (EF-20: an interactive prompt reading from stdin gets instant\n * EOF instead of hanging on an open, unwritten pipe) and enforces a\n * command-class-scoped timeout (EF-20: no git operation can block an\n * autonomous session indefinitely with no completion signal).\n */\nexport function runGit(cwd: string, args: string[], options: RunGitOptions = {}): Promise<GitResult> {\n const commandClass = options.commandClass ?? classifyGitCommand(args);\n // Falls back to the \"write\" budget if commandClass ever reaches here as\n // something outside the GitCommandClass union (only reachable from an\n // untyped caller) -- an undefined timeoutMs would mean NO timeout at all,\n // silently defeating the one guarantee this module exists to provide.\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUTS_MS[commandClass] ?? DEFAULT_TIMEOUTS_MS.write;\n\n return new Promise((resolve, reject) => {\n const child = execFile(\n \"git\",\n args,\n {\n cwd,\n env: buildGitEnv(),\n timeout: timeoutMs,\n // SIGTERM (Node's default) rather than SIGKILL: gives git a chance\n // to run its own cleanup on POSIX, relevant to EF-20's own concern\n // about a killed operation leaving refs/locks in an uncertain state.\n // Moot on Windows -- Node docs: both map to an abrupt TerminateProcess\n // there -- but this module isn't Windows-gated.\n killSignal: \"SIGTERM\",\n windowsHide: true,\n encoding: \"utf8\",\n },\n (error, stdout, stderr) => {\n if (error) {\n if (error.killed) {\n reject(\n new GitTimeoutError(\n // NOTE: embeds the raw args, including any secrets a future\n // network-class caller might pass (e.g. a credentialed\n // clone URL). No current caller does; redact before one does.\n `git ${args.join(\" \")} timed out after ${timeoutMs}ms (class: ${commandClass})`,\n { cause: error },\n ),\n );\n return;\n }\n reject(error);\n return;\n }\n resolve({ stdout, stderr });\n },\n );\n // EF-20: never leave stdin as an open, unread pipe -- see module doc.\n child.stdin?.end();\n });\n}\n\n/**\n * Returns the repository state required before a destructive managed update.\n */\nexport async function inspectGitRepository(cwd: string): Promise<GitRepositoryState> {\n try {\n const { stdout } = await runGit(cwd, [\"rev-parse\", \"--is-inside-work-tree\"]);\n if (stdout.trim() !== \"true\") {\n return { status: \"not-repository\" };\n }\n } catch {\n return { status: \"not-repository\" };\n }\n\n try {\n await runGit(cwd, [\"rev-parse\", \"--verify\", \"HEAD\"]);\n } catch {\n return { status: \"no-commits\" };\n }\n\n const { stdout } = await runGit(cwd, [\"status\", \"--porcelain\", \"-uall\"]);\n const lines = stdout.trim();\n return {\n status: \"ready\",\n uncommittedChanges: lines.length === 0 ? 0 : lines.split(\"\\n\").length,\n };\n}\n\n/**\n * Returns the number of uncommitted changes in the working tree, or 0\n * if the directory is not a git repo or git is not installed.\n */\nexport async function countUncommittedChanges(cwd: string): Promise<number> {\n try {\n const { stdout } = await runGit(cwd, [\"status\", \"--porcelain\", \"-uall\"]);\n const lines = stdout.trim();\n return lines.length === 0 ? 0 : lines.split(\"\\n\").length;\n } catch {\n return 0;\n }\n}\n\nexport interface UnbornBranchCorrection {\n corrected: boolean;\n from?: string;\n to: \"main\";\n /**\n * Set when the correction was declined because the target ref could not be\n * read (a broken/unreadable `refs/heads/main`) rather than because it was\n * absent or already healthy. Callers should surface this: the repo has a\n * real integrity problem the operator will want to know about, but it is\n * not severe enough to abort an install over a cosmetic branch rename.\n */\n blockedReason?: \"target-unreadable\";\n}\n\n/**\n * EF-05: on an unborn repository (git init has run, no commit yet) whose\n * HEAD currently targets exactly \"master\" -- the reported Git for Windows\n * system-default leak (init.defaultBranch=master at the system config\n * level) -- repoint it to \"main\" via `symbolic-ref`. Safe because no commit\n * objects exist yet to conflict with; equivalent to what `git init -b main`\n * does internally. Any OTHER branch name, including one an operator\n * deliberately chose, is left untouched -- this corrects the specific\n * reported default-leak, not a general \"Arcane requires main\" policy.\n * No-op (not corrected) if `cwd` isn't an unborn repo at all.\n */\nexport async function correctUnbornMasterDefault(cwd: string): Promise<UnbornBranchCorrection> {\n let current: string;\n try {\n const { stdout } = await runGit(cwd, [\"symbolic-ref\", \"--short\", \"HEAD\"]);\n current = stdout.trim();\n } catch {\n return { corrected: false, to: \"main\" };\n }\n\n if (current !== \"master\") {\n return { corrected: false, to: \"main\" };\n }\n\n // Verify unborn-ness internally rather than trusting the caller: on a\n // BORN repo already on \"master\", `symbolic-ref HEAD refs/heads/main`\n // would detach HEAD's branch pointer from its commit history without\n // moving any commits -- `main` would exist as an empty ref while every\n // real commit stays reachable only via the abandoned `master` ref. This\n // check is the same one `inspectGitRepository` uses to distinguish\n // \"no-commits\" from \"ready\".\n try {\n await runGit(cwd, [\"rev-parse\", \"--verify\", \"HEAD\"]);\n return { corrected: false, to: \"main\" }; // HEAD resolves -- repo is born, don't touch it\n } catch {\n // rev-parse --verify HEAD failing IS the unborn signal -- proceed.\n }\n\n // Also verify the TARGET doesn't already exist with real history.\n // Confirming the source is unborn is not sufficient: if `refs/heads/main`\n // already resolves (e.g. created earlier in this repo, or shared across\n // git worktrees -- refs/heads/* is shared while HEAD is per-worktree, so\n // an unborn \"master\" HEAD in one worktree can coexist with a fully born\n // \"main\" from another), repointing HEAD onto it would silently attach\n // whatever's currently staged/uncommitted on the unborn HEAD to main's\n // real commit history -- a history splice, not a safe unborn-HEAD\n // repoint, and the next `git commit` an operator runs would commit it.\n // Distinguishing \"absent\" from \"unreadable\" needs stdout/stderr, not an\n // exit code. Verified empirically against git 2.x -- BOTH `rev-parse\n // --verify` and `show-ref --verify --quiet` return the same nonzero status\n // for a corrupt ref as for a missing one (128 and 1 respectively), so\n // either one behind a bare catch reads \"this branch is broken\" as \"this\n // branch does not exist\" and repoints HEAD onto real history anyway.\n //\n // `for-each-ref` usually exits 0 and reports the state through its streams:\n // healthy -> stdout = the object id, stderr empty\n // corrupt -> stdout empty, stderr \"warning: ignoring broken ref ...\"\n // absent -> stdout empty, stderr empty\n // Only the third is safe to treat as absence, and even it needs a second\n // probe (see the symref check below). The stderr test is on emptiness, not\n // on git's English wording, so a translated locale can't defeat it.\n let mainRef: { stdout: string; stderr: string };\n try {\n mainRef = await runGit(cwd, [\"for-each-ref\", \"--format=%(objectname)\", \"refs/heads/main\"]);\n } catch {\n // for-each-ref itself failed -- a corrupt .git/packed-refs exits 128, and\n // a timeout rejects too. Either way we learned nothing about whether main\n // exists, so absence is not a conclusion we're entitled to draw.\n return { corrected: false, to: \"main\", blockedReason: \"target-unreadable\" };\n }\n\n if (mainRef.stdout.trim() !== \"\") {\n return { corrected: false, to: \"main\" }; // main already exists -- don't touch it\n }\n\n if (mainRef.stderr.trim() !== \"\") {\n // Broken ref: it may still hold real history we cannot see. Fail closed\n // -- decline the correction, and tell the caller why so it can warn.\n return { corrected: false, to: \"main\", blockedReason: \"target-unreadable\" };\n }\n\n // Empty/empty is still ambiguous: a DANGLING SYMREF (refs/heads/main is\n // `ref: refs/heads/<missing>`) reports exactly the same shape as an absent\n // ref. Repointing onto it would leave HEAD on the symref's target while\n // reporting \"repointed to main\" -- untrue, even though nothing is spliced.\n // `symbolic-ref -q` is the discriminator: 0 = main exists as a symref,\n // 1 = genuinely absent. Anything else is another unreadable state.\n try {\n await runGit(cwd, [\"symbolic-ref\", \"-q\", \"refs/heads/main\"]);\n return { corrected: false, to: \"main\", blockedReason: \"target-unreadable\" };\n } catch (error) {\n if ((error as { code?: unknown }).code !== 1) {\n return { corrected: false, to: \"main\", blockedReason: \"target-unreadable\" };\n }\n // Exit 1 -- refs/heads/main genuinely does not exist. Safe to target it.\n }\n\n await runGit(cwd, [\"symbolic-ref\", \"HEAD\", \"refs/heads/main\"]);\n return { corrected: true, from: current, to: \"main\" };\n}\n\nexport type PullRebaseResult =\n | { action: \"set\" }\n | { action: \"already-set\" }\n | { action: \"explicit-false-preserved\" };\n\n/**\n * EF-32: ensure `pull.rebase` is set at the repository-LOCAL level so the\n * mandated rebase-and-fast-forward workflow (git-conventions.md) doesn't\n * silently depend on the operator's machine-wide default (Git for Windows\n * ships pull.rebase=false at the system level). Distinguishes \"unset\n * locally\" (safe to set -- it was only ever inheriting a machine default)\n * from \"explicitly set locally, even to false\" (never silently overridden\n * -- the caller is expected to surface this as a warning instead).\n */\nexport async function ensureLocalPullRebase(cwd: string): Promise<PullRebaseResult> {\n let rawLocalValue: string | undefined;\n try {\n const { stdout } = await runGit(cwd, [\"config\", \"--local\", \"--get\", \"pull.rebase\"]);\n rawLocalValue = stdout.trim();\n } catch {\n rawLocalValue = undefined; // nothing set locally at all (may be inherited from global/system)\n }\n\n if (rawLocalValue === undefined) {\n await runGit(cwd, [\"config\", \"--local\", \"pull.rebase\", \"true\"]);\n return { action: \"set\" };\n }\n\n // Something IS set locally. Determine whether it's a falsy boolean via\n // git's own boolean parser (`--type=bool` normalizes any of git's valid\n // spellings -- true/yes/on/1 and false/no/off/0 -- to canonical\n // \"true\"/\"false\"), rather than hand-matching the literal string \"false\"\n // and missing every other valid spelling. A value `--type=bool` can't\n // coerce (e.g. \"merges\" or \"interactive\" -- both valid, deliberate\n // pull.rebase settings) means the operator explicitly chose something\n // else entirely; that's still an explicit local choice and must never be\n // overwritten, so it's treated the same as \"already-set\".\n try {\n const { stdout } = await runGit(cwd, [\"config\", \"--local\", \"--type=bool\", \"--get\", \"pull.rebase\"]);\n if (stdout.trim() === \"false\") {\n return { action: \"explicit-false-preserved\" };\n }\n return { action: \"already-set\" };\n } catch {\n return { action: \"already-set\" };\n }\n}\n","import { runGit } from \"./git.js\";\n\n/**\n * \"ADO context\" for the CLI (PRD D-03, #278): a configured remote hosted on\n * Azure DevOps. A remote host is the only ADO signal a CLI can read\n * deterministically before any PRD exists, so it is the whole definition.\n * It only pre-selects an answer; the operator is still asked.\n */\n\n/** Host of a git remote URL, lower-cased, for both URL and scp-like (`user@host:path`) forms. */\nexport function remoteUrlHost(url: string): string | undefined {\n const trimmed = url.trim();\n if (trimmed.includes(\"://\")) {\n try {\n return new URL(trimmed).hostname.toLowerCase() || undefined;\n } catch {\n return undefined;\n }\n }\n // scp-like syntax; a Windows drive letter (\"C:\\repo\") is a path, not a host.\n const scp = /^(?:[^@/\\s]+@)?([^:/\\s]+):/.exec(trimmed);\n if (!scp || /^[a-zA-Z]$/.test(scp[1]!)) return undefined;\n return scp[1]!.toLowerCase();\n}\n\n/**\n * dev.azure.com, including its `ssh.dev.azure.com` SSH endpoint, or any\n * `*.visualstudio.com` (the legacy `org.visualstudio.com` and\n * `vs-ssh.visualstudio.com` forms). Suffix-matched on a dot boundary so a\n * look-alike such as `dev.azure.com.example.net` does not count.\n */\nexport function isAdoRemoteUrl(url: string): boolean {\n const host = remoteUrlHost(url);\n if (host === undefined) return false;\n return host === \"dev.azure.com\" || host.endsWith(\".dev.azure.com\") || host.endsWith(\".visualstudio.com\");\n}\n\n/** True when any configured remote's URL is on Azure DevOps. */\nexport async function detectAdoContext(targetDir: string): Promise<boolean> {\n let stdout: string;\n try {\n ({ stdout } = await runGit(targetDir, [\"config\", \"--get-regexp\", \"^remote\\\\..*\\\\.url$\"]));\n } catch {\n // Exit 1 (no remote), or not a repository: no ADO context.\n return false;\n }\n return stdout\n .split(\"\\n\")\n .map((line) => line.slice(line.indexOf(\" \") + 1))\n .some((url) => url.trim() !== \"\" && isAdoRemoteUrl(url));\n}\n","/**\n * Push safety (EF-09 / features/push-safety/PRD.md) and shared git-hooks\n * management (ARC-037 decision 2).\n *\n * Two layered controls for a repository whose history must not reach a remote:\n * a `pre-push` hook, and disabled push URLs. Neither is tamper-proof, and the\n * PRD is explicit that they are not meant to be — the threat modelled is an\n * *accidental* push (wrong remote, muscle memory, an unsupervised agent), not a\n * determined operator. Overclaiming here would be its own hazard.\n *\n * The two layers cover different bypasses, which is why both exist:\n * - `git push --no-verify` skips hooks entirely → the disabled URL catches it.\n * - `git push <url> main`, or a second remote, never consults the first\n * remote's URL → the hook catches it.\n * Only combining both bypasses in one command gets through, which is squarely\n * the determined-operator case this does not claim to stop.\n *\n * `installHook`/`isHookInstalled`/`removeHook` below are the generalized form\n * of this module's original pre-push-only machinery: any Arcane hook (pre-push,\n * and now the consumer-facing secrets-scan pre-commit hook) claims the SAME\n * shared `.arcane/hooks/` directory and `core.hooksPath` slot through the SAME\n * R7 collision-guarded path, rather than each hook re-implementing its own\n * claim-and-refuse logic. `installPrePushHook`/`isHookEnforced`/\n * `removePrePushHook` are now thin wrappers over the generic functions, kept\n * for every existing caller (doctor.ts, init.ts, unblock-push.ts, uninstall.ts)\n * and this file's own extensive test suite -- their behavior is unchanged.\n */\nimport { mkdir, readFile, writeFile, chmod, access, rm, readdir } from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport { join, resolve, dirname, basename } from \"node:path\";\nimport { fileExists } from \"./copier.js\";\nimport { runGit } from \"./git.js\";\n\n/**\n * Sentinel push URL. Not a real scheme, so git fails to resolve a helper.\n *\n * The scheme name carries the recovery instruction because it is the ONLY text\n * git shows the operator here. With both layers in place the URL fails first and\n * the hook never runs, so the hook's carefully worded message is never seen --\n * all git prints is `fatal: remote helper '<scheme>' aborted session`. Encoding\n * the fix in the scheme turns a dead end into an instruction.\n */\nexport const DISABLED_PUSH_URL = \"arcane-push-blocked-run-spell-unblock-push://this-repository\";\n\n/**\n * Marks that a remote had NO `pushurl` key before the block. Restoring must then\n * *remove* the key rather than write the fetch URL into it -- otherwise a later\n * `git remote set-url` would change fetch only and pushes would silently keep\n * going to the old location.\n *\n * A separate boolean key rather than a magic value stored in the URL slot: a\n * remote whose genuine push URL happened to equal that magic string would\n * otherwise be recorded as \"there was no key\" and lose its original on restore.\n */\nconst NO_PUSH_URL_MARKER = (remote: string): string => `remote.${remote}.arcaneHadNoPushUrl`;\n\n/** Directory Arcane points `core.hooksPath` at when it owns the hooks. */\nexport const ARCANE_HOOKS_DIR = \".arcane/hooks\";\n\n/**\n * Where a config value is set, as git itself reports it via `--show-scope`.\n * `worktree` matters here: a linked worktree can carry its own `config.worktree`\n * value that `--local` does not see, which an earlier local-vs-effective\n * heuristic misfiled as inherited-from-global.\n */\nexport type ConfigScope = \"local\" | \"worktree\" | \"global\" | \"system\" | \"command\" | \"unknown\";\n\nexport interface HooksPathInfo {\n /** The effective value git would use, respecting scope precedence. */\n value: string;\n /** Where it is set, as reported by git rather than inferred. */\n scope: ConfigScope;\n /** Git could not be asked. Callers must fail closed, never assume \"unset\". */\n unreadable?: boolean;\n}\n\nexport type HookInstallOutcome =\n | { status: \"installed\"; path: string }\n | { status: \"already-ours\" }\n | { status: \"refused-foreign-hooks-path\"; existing: string; scope: ConfigScope }\n | { status: \"refused-default-hooks\"; hooks: string[] }\n | { status: \"refused-unreadable-config\" };\n\n/**\n * Reads the EFFECTIVE `core.hooksPath` — not just the repository-local one.\n *\n * Reading only `--local` was a real defect: a `core.hooksPath` set at *global*\n * scope (the standard way organisations deploy pre-commit and corporate hook\n * managers) was invisible, so installation reported success while silently\n * disabling it, since a local value overrides a global one. That is precisely\n * the harm R7 exists to prevent.\n */\nexport async function readHooksPath(cwd: string): Promise<HooksPathInfo | undefined> {\n let raw: string;\n try {\n // `--show-scope` makes git report the scope rather than us inferring it by\n // comparing --local against the effective value. That inference was wrong\n // for a per-worktree `config.worktree` value, which --local does not see:\n // a genuinely repository-scoped setting was reported as inherited.\n const { stdout } = await runGit(cwd, [\"config\", \"--show-scope\", \"--get\", \"core.hooksPath\"]);\n raw = stdout.trim();\n } catch (error) {\n // ONLY exit 1 means \"not set anywhere\". Anything else -- an unparseable\n // config file, a permission error, an older git that rejects --show-scope --\n // means we could not determine whether someone else owns core.hooksPath.\n // Treating that as \"no claim\" made the R7 collision guard fail OPEN, which\n // is the one direction it must never fail: it would install over an org hook\n // manager it simply failed to see.\n if ((error as { code?: unknown }).code !== 1) {\n return { value: \"\", scope: \"unknown\", unreadable: true };\n }\n return undefined;\n }\n if (raw === \"\") return undefined;\n\n // Format is `<scope>\\t<value>`. The value may itself contain whitespace.\n const separator = raw.indexOf(\"\\t\");\n if (separator === -1) return { value: raw, scope: \"unknown\" };\n\n const scope = raw.slice(0, separator).trim();\n const value = raw.slice(separator + 1).trim();\n if (value === \"\") return undefined;\n\n const known: readonly ConfigScope[] = [\"local\", \"worktree\", \"global\", \"system\", \"command\"];\n const matched = known.find((candidate) => candidate === scope);\n return { value, scope: matched ?? \"unknown\" };\n}\n\n/**\n * True when two `core.hooksPath` values name the same directory.\n *\n * Exact string equality misreported every equivalent spelling git accepts --\n * `.arcane/hooks/`, `./.arcane/hooks`, `.arcane\\hooks`, and an absolute path all\n * point where our hook actually lives and all fire on a real push, yet compared\n * unequal. That direction fails safe (we refuse rather than clobber, and report\n * \"not enforced\" for something that is), but it means doctor contradicts what\n * git does, which is the exact class of confusion this module exists to remove.\n */\nfunction isArcaneHooksPath(root: string, value: string): boolean {\n // Case folding only where the filesystem actually folds case. On POSIX,\n // `.arcane/HOOKS` is a genuinely different directory, and treating it as ours\n // would make the R7 guard fail OPEN -- installing over someone else's hooks.\n // Backslash-to-slash folding is Windows-only for the same reason as case\n // folding: on POSIX a backslash is a legal filename character, so a directory\n // genuinely named `.arcane\\hooks` would normalise onto ours and the R7 guard\n // would fail OPEN -- installing over someone else's hooks.\n const windows = process.platform === \"win32\";\n const normalize = (p: string): string => {\n const separated = windows ? p.replace(/\\\\/g, \"/\") : p;\n // Relative values resolve against the repository ROOT, matching how git\n // itself resolves core.hooksPath -- against the worktree top level, never\n // against the process's working directory.\n const absolute = resolve(root, separated).replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n return windows ? absolute.toLowerCase() : absolute;\n };\n return normalize(value) === normalize(ARCANE_HOOKS_DIR);\n}\n\nconst PRE_PUSH_HOOK_BODY = `#!/bin/sh\n# Installed by Arcane because this repository's .arcane.json sets\n# push_policy: \"blocked\".\n#\n# This is deliberately not tamper-proof. It guards against an accidental push\n# -- the wrong remote, muscle memory, an unsupervised agent -- not against a\n# determined operator, who can always bypass it.\n#\n# To push legitimately, undo the policy first:\n#\n# spell unblock-push\n#\n# That command is interactive-only and asks you to confirm the repository by\n# name. Do not work around this hook by other means: the manifest would then\n# claim a protection this repository no longer has.\necho \"arcane: push blocked (push_policy: blocked). Run 'spell unblock-push' to undo.\" >&2\nexit 1\n`;\n\n/**\n * Human phrasing for a config scope.\n *\n * Worth its own function because \"globally\" was previously printed for anything\n * not local — including a system-scope value and a per-worktree one, neither of\n * which the operator would find where that message sends them.\n */\nexport function describeConfigScope(scope: ConfigScope): string {\n switch (scope) {\n case \"local\":\n return \"set for this repository\";\n case \"worktree\":\n return \"set for this worktree\";\n case \"global\":\n return \"set globally, for your user\";\n case \"system\":\n return \"set system-wide\";\n case \"command\":\n return \"set on the command line\";\n default:\n return \"scope unknown\";\n }\n}\n\n/**\n * The repository's top level — where the hook must live.\n *\n * `targetDir` is whatever directory the CLI was run from, and\n * `--is-inside-work-tree` is true from any subdirectory. Installing the hook\n * relative to `targetDir` while pointing repo-wide `core.hooksPath` at the\n * literal `.arcane/hooks` put the file somewhere git never looks: git resolves a\n * relative hooksPath against the *worktree top-level*, so a `spell init` run\n * inside a monorepo package disabled the repository's hooks entirely and\n * reported the block installed.\n */\nasync function repositoryRoot(cwd: string): Promise<string> {\n // Anchored on the COMMON git dir, so every linked worktree resolves to the\n // same directory as the main one. `--show-toplevel` would give each worktree\n // its own root, which is exactly the mistake being fixed: the hook lives in\n // one place, and `core.hooksPath` is shared config that must point there from\n // everywhere.\n try {\n const { stdout } = await runGit(cwd, [\n \"rev-parse\",\n \"--path-format=absolute\",\n \"--git-common-dir\",\n ]);\n const commonDir = stdout.trim();\n if (commonDir !== \"\") {\n // `<main>/.git` -> `<main>`. A bare repository has no working tree above\n // its git dir, so fall through to the worktree top level there.\n const parent = dirname(commonDir);\n if (basename(commonDir) === \".git\" && parent !== \"\") return parent;\n }\n } catch {\n // Older git without --path-format; fall back below.\n }\n try {\n const { stdout } = await runGit(cwd, [\"rev-parse\", \"--show-toplevel\"]);\n const top = stdout.trim();\n if (top !== \"\") return top;\n } catch {\n // Not a work tree (bare repo).\n }\n return cwd;\n}\n\n/**\n * Absolute path of a named hook file Arcane installs, resolved against the\n * repository top level rather than the working directory. Defaults to\n * \"pre-push\" -- this module's original and, until ARC-037, only hook -- so\n * every existing single-argument call site (this file's own internals and\n * this file's test suite) is unaffected by the generalization to other\n * hook names.\n */\nexport async function hookFilePath(cwd: string, hookName: string = \"pre-push\"): Promise<string> {\n return join(await repositoryRoot(cwd), ARCANE_HOOKS_DIR, hookName);\n}\n\n/**\n * The value written to `core.hooksPath`: an ABSOLUTE path.\n *\n * A relative value is re-resolved per worktree, and `core.hooksPath` lives in\n * shared local config while `.arcane/hooks/` is an untracked directory that\n * exists only in the checkout where it was created. So every linked worktree\n * inherited the config and had no hook file — the hook layer was simply absent\n * there, and `git push <url>` (the bypass only the hook covers) delivered the\n * full history in one ordinary command while `doctor` reported the repository\n * blocked. That is not an edge case here: Arcane's own methodology sends\n * concurrent sessions into linked worktrees.\n */\nasync function hooksPathValue(cwd: string): Promise<string> {\n return join(await repositoryRoot(cwd), ARCANE_HOOKS_DIR).replace(/\\\\/g, \"/\");\n}\n\n/**\n * Installs a named hook (its body written verbatim), refusing rather than\n * clobbering someone else's hook manager (R7).\n *\n * `core.hooksPath` is a single exclusive slot — Git reads one directory, never\n * several — so pointing it at Arcane's directory silently disables Husky,\n * Lefthook, pre-commit, or anything else already using it, at either local or\n * global scope. This repository's own `core.hooksPath` is `.husky/_`, running\n * lint, typecheck and the full test suite; a naive install would have turned\n * that off while appearing to add protection.\n *\n * Safe to call for a SECOND hook name once a first has already claimed the\n * directory: the collision guard above only refuses a FOREIGN path, and\n * Arcane's own already-claimed `.arcane/hooks/` is not foreign to a second\n * Arcane hook — the two hooks share the one slot rather than racing for it.\n */\nexport async function installHook(cwd: string, hookName: string, body: string): Promise<HookInstallOutcome> {\n const root = await repositoryRoot(cwd);\n const existing = await readHooksPath(cwd);\n\n if (existing?.unreadable === true) return { status: \"refused-unreadable-config\" };\n\n if (existing !== undefined && !isArcaneHooksPath(root, existing.value)) {\n return {\n status: \"refused-foreign-hooks-path\",\n existing: existing.value,\n scope: existing.scope,\n };\n }\n\n // The guard above only sees hook managers that announce themselves via\n // core.hooksPath. A repository using git's DEFAULT hooks directory has no\n // such key, so taking the slot silently disabled every hook it had -- the\n // exact harm R7 names, reached by the one route R7 wasn't looking at.\n // Only relevant on the FIRST claim: once existing is defined (ours), a\n // second hook joining the same directory displaces nothing new.\n if (existing === undefined) {\n const displaced = await defaultHooks(cwd);\n if (displaced.length > 0) {\n return { status: \"refused-default-hooks\", hooks: displaced };\n }\n }\n\n const hookPath = join(root, ARCANE_HOOKS_DIR, hookName);\n\n if (existing !== undefined && (await hookFileMatches(cwd, hookName, body))) {\n return { status: \"already-ours\" };\n }\n\n await mkdir(join(root, ARCANE_HOOKS_DIR), { recursive: true });\n await writeFile(hookPath, body, \"utf-8\");\n // Git requires the hook be executable on POSIX. chmod is a no-op for the\n // owner on Windows, which is fine -- Git for Windows does not check the bit.\n await chmod(hookPath, 0o755);\n await runGit(cwd, [\"config\", \"--local\", \"core.hooksPath\", await hooksPathValue(cwd)]);\n\n return { status: \"installed\", path: hookPath };\n}\n\n/** Installs the pre-push hook. Thin wrapper over {@link installHook}. */\nexport async function installPrePushHook(cwd: string): Promise<HookInstallOutcome> {\n return installHook(cwd, \"pre-push\", PRE_PUSH_HOOK_BODY);\n}\n\n/**\n * True only if the named hook is genuinely in force: the config points at\n * Arcane's directory AND that hook's file actually exists there with the\n * expected body.\n *\n * Checking the config alone is a declaration check, not an enforcement check —\n * deleting the hook file leaves the config intact and pushes succeed.\n */\nexport async function isHookInstalled(cwd: string, hookName: string, body: string): Promise<boolean> {\n const hooksPath = await readHooksPath(cwd);\n if (hooksPath === undefined || hooksPath.unreadable === true) return false;\n if (!isArcaneHooksPath(await repositoryRoot(cwd), hooksPath.value)) return false;\n return hookFileMatches(cwd, hookName, body);\n}\n\n/** Whether the pre-push hook is genuinely enforced. Thin wrapper over {@link isHookInstalled}. */\nexport async function isHookEnforced(cwd: string): Promise<boolean> {\n return isHookInstalled(cwd, \"pre-push\", PRE_PUSH_HOOK_BODY);\n}\n\n/**\n * Warns -- never blocks -- when the branch being pushed already has a\n * CLOSED or MERGED pull request (ARC-035 decision 4: the silent\n * stranded-commit mode that shipped as PR #63's incident is made loud by\n * extending this same hook slot, not by adding a second, competing one).\n *\n * Shares the \"pre-push\" hook name with {@link PRE_PUSH_HOOK_BODY}, so the\n * two are mutually exclusive per repository by construction -- a repo with\n * `push_policy: \"blocked\"` already refuses every push outright and has no\n * use for a warning about which PR it would have gone to; every other\n * tier (`guarded`, `open`, unset) gets this one instead. GitHub-only,\n * matching the ADR's own cited mechanism (`gh pr view --json state`); a\n * non-GitHub remote or a missing/unauthenticated `gh` is a silent no-op,\n * never a blocked or errored push.\n */\nconst CLOSED_PR_WARNING_HOOK_BODY = `#!/bin/sh\n# Installed by Arcane (ARC-035 decision 4): warns when the branch being\n# pushed already has a closed or merged PR, instead of accepting the push\n# into a void with no signal at all. Never blocks -- pushing to a branch\n# behind a closed PR is sometimes intentional.\nremote_url=\"$2\"\ncase \"$remote_url\" in\n *github.com*) ;;\n *) exit 0 ;;\nesac\ncommand -v gh >/dev/null 2>&1 || exit 0\nbranch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || exit 0\nstate=$(gh pr view \"$branch\" --json state --jq .state 2>/dev/null) || exit 0\ncase \"$state\" in\n CLOSED|MERGED)\n echo \"arcane: WARNING -- the PR for branch '$branch' is already $state. Pushing anyway (not blocked).\" >&2\n ;;\nesac\nexit 0\n`;\n\n/** Installs the closed-PR warning hook. Thin wrapper over {@link installHook}. */\nexport async function installClosedPrWarningHook(cwd: string): Promise<HookInstallOutcome> {\n return installHook(cwd, \"pre-push\", CLOSED_PR_WARNING_HOOK_BODY);\n}\n\n/** True only if the closed-PR warning hook (not the blocking one) is the one currently installed. */\nexport async function isClosedPrWarningHookInstalled(cwd: string): Promise<boolean> {\n return isHookInstalled(cwd, \"pre-push\", CLOSED_PR_WARNING_HOOK_BODY);\n}\n\n/**\n * Real hooks living in git's default directory, which taking `core.hooksPath`\n * would silently switch off. `.sample` files are git's own inert templates.\n */\nasync function defaultHooks(cwd: string): Promise<string[]> {\n let hooksDir: string;\n try {\n const { stdout } = await runGit(cwd, [\"rev-parse\", \"--git-common-dir\"]);\n hooksDir = join(resolve(cwd, stdout.trim()), \"hooks\");\n } catch {\n return [];\n }\n try {\n return (await readdir(hooksDir)).filter((name) => !name.endsWith(\".sample\")).sort();\n } catch {\n return [];\n }\n}\n\n/**\n * True when a named hook file on disk is the one Arcane wrote, byte for byte.\n *\n * Existence alone is not enforcement: a zero-byte file, or one edited down to\n * `exit 0`, leaves the config pointing at a hook that blocks nothing while\n * `doctor` reports a pass. That is the same declaration-versus-enforcement\n * confusion review already found one level up, so the check compares content --\n * which `installHook` already needed for its `already-ours` decision.\n */\nasync function hookFileMatches(cwd: string, hookName: string, body: string): Promise<boolean> {\n const hookPath = await hookFilePath(cwd, hookName);\n if (!(await fileExists(hookPath))) return false;\n try {\n if ((await readFile(hookPath, \"utf-8\")) !== body) return false;\n } catch {\n return false;\n }\n\n // On POSIX, git silently skips a hook that is not executable, so the right\n // body in a non-executable file is still not enforcement. Git for Windows\n // does not consult the bit, and chmod there is largely advisory, so checking\n // it would produce a false \"not enforced\" for a hook that demonstrably fires.\n if (process.platform === \"win32\") return true;\n try {\n await access(hookPath, constants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Removes a named hook's wiring. Leaves a foreign `core.hooksPath` untouched.\n *\n * Only unclaims `core.hooksPath` itself once NO Arcane-installed hook remains\n * in the shared directory: since ARC-037, `.arcane/hooks/` can hold more than\n * one hook (pre-push and pre-commit), and removing one must not silently\n * disable a sibling that still needs the same claimed path. Verified rather\n * than assumed by re-listing the directory after deleting -- not by counting\n * known hook names, which this module has no reason to enumerate.\n */\nexport async function removeHook(cwd: string, hookName: string): Promise<void> {\n const existing = await readHooksPath(cwd);\n if (existing === undefined || existing.unreadable === true) return;\n const root = await repositoryRoot(cwd);\n if (!isArcaneHooksPath(root, existing.value)) return;\n if (existing.scope !== \"local\" && existing.scope !== \"worktree\") return;\n\n // Remove the hook file first. Leaving it behind means a later `spell init`\n // that re-points core.hooksPath silently re-arms a block the operator\n // lifted.\n try {\n await rm(await hookFilePath(cwd, hookName), { force: true });\n } catch {\n // Non-fatal: if core.hooksPath ends up unset below, git no longer reads\n // this directory anyway.\n }\n\n const hooksDir = join(root, ARCANE_HOOKS_DIR);\n let remaining: string[] = [];\n try {\n remaining = await readdir(hooksDir);\n } catch {\n remaining = [];\n }\n if (remaining.length > 0) return; // a sibling hook still needs core.hooksPath\n\n // Unset at the scope the value actually lives in. `--local` cannot touch a\n // per-worktree `config.worktree` value, so accepting worktree scope above and\n // then unsetting locally left the hook fully in force while `unblock-push`\n // printed \"Push unblocked\".\n const scopeFlag = existing.scope === \"worktree\" ? \"--worktree\" : \"--local\";\n try {\n await runGit(cwd, [\"config\", scopeFlag, \"--unset\", \"core.hooksPath\"]);\n } catch {\n // Already unset; nothing to undo.\n }\n}\n\n/** Removes the pre-push hook's wiring. Thin wrapper over {@link removeHook}. */\nexport async function removePrePushHook(cwd: string): Promise<void> {\n return removeHook(cwd, \"pre-push\");\n}\n\n/** Every configured remote name, in git's own order. */\nexport async function listRemotes(cwd: string): Promise<string[]> {\n try {\n const { stdout } = await runGit(cwd, [\"remote\"]);\n return stdout\n .split(\"\\n\")\n .map((r) => r.trim())\n .filter(Boolean);\n } catch {\n return [];\n }\n}\n\n/**\n * Each remote with the URL(s) a push would actually reach.\n *\n * The `guarded` reminder exists to catch a *wrong remote*, and a bare name like\n * `origin` says nothing about where it points — which is the entire question.\n */\nexport async function pushTargets(cwd: string): Promise<{ remote: string; urls: string[] }[]> {\n const targets: { remote: string; urls: string[] }[] = [];\n for (const remote of await listRemotes(cwd)) {\n try {\n const { stdout } = await runGit(cwd, [\"remote\", \"get-url\", \"--push\", \"--all\", remote]);\n targets.push({\n remote,\n urls: stdout\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean),\n });\n } catch {\n targets.push({ remote, urls: [] });\n }\n }\n return targets;\n}\n\nexport type PushUrlStatus =\n | \"disabled\"\n | \"already-disabled\"\n | \"restored\"\n | \"nothing-recorded\"\n | \"failed\";\n\nexport interface PushUrlResult {\n remote: string;\n status: PushUrlStatus;\n /** Present only on \"failed\". Why this remote could not be covered. */\n reason?: string;\n}\n\n/**\n * Where a remote's original push URL is recorded.\n *\n * Deliberately a key *inside the remote's own section* rather than a flat\n * `arcane.originalPushUrl.<remote>`, which review broke three ways:\n *\n * - `my_remote` is a legal remote name but an illegal trailing config key\n * (that segment must be alphanumeric or `-`), so `git config` errored and\n * aborted the whole loop, leaving every later remote live while the operator\n * had been told the repository was blocked.\n * - Trailing key segments are case-INSENSITIVE, so remotes `origin` and\n * `Origin` collided on one key: one original was lost and restore pointed a\n * remote at the other's URL -- the wrong-remote push this feature exists to\n * prevent.\n * - `git remote rename` left the flat key orphaned, so restore silently found\n * nothing while `unblock-push` still reported success.\n *\n * A `remote.<name>.*` subsection has none of those problems: git accepts any\n * legal remote name there, subsection names ARE case-sensitive, and `git remote\n * rename` moves the whole section including keys it has never heard of. All\n * three verified directly against git 2.53 before this was written.\n */\nfunction originalPushUrlKey(remote: string): string {\n return `remote.${remote}.arcaneOriginalPushUrl`;\n}\n\n/** Reads every value of a possibly-multivalued local config key. */\nasync function readLocalAll(cwd: string, key: string): Promise<string[]> {\n try {\n const { stdout } = await runGit(cwd, [\"config\", \"--local\", \"--get-all\", key]);\n return stdout\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean);\n } catch {\n // Exit 1 == key absent.\n return [];\n }\n}\n\n/**\n * Every value of a multivalued key, WITH the scope each came from.\n *\n * `remote.<name>.pushurl` is multivalued and git collects values across system,\n * global and local scope. Reading only `--local` was a silent hole: a pushurl\n * configured globally stayed invisible, a local `--replace-all` *appended* the\n * sentinel instead of replacing the live URL, and because the global value sorts\n * first git delivered the push to it and only then failed on the sentinel. The\n * exit code was 128 and the history was already gone -- with `doctor` reporting\n * the repository fully covered.\n */\nasync function readScopedAll(cwd: string, key: string): Promise<{ scope: string; value: string }[]> {\n let stdout: string;\n try {\n ({ stdout } = await runGit(cwd, [\"config\", \"--show-scope\", \"--get-all\", key]));\n } catch {\n return [];\n }\n return stdout\n .split(\"\\n\")\n .filter((line) => line !== \"\")\n .map((line) => {\n // `<scope>\\t<value>`; split BEFORE trimming. Trimming first ate the\n // separator tab on an empty-valued entry (`global\\t`), which then parsed\n // as `{scope: \"unknown\", value: \"global\"}` and produced a remedy telling\n // the operator to run `git config --unknown`, which is not a command.\n const separator = line.indexOf(\"\\t\");\n if (separator === -1) return { scope: \"unknown\", value: line.trim() };\n return { scope: line.slice(0, separator).trim(), value: line.slice(separator + 1).trim() };\n });\n}\n\n/** Push URLs git would actually use for a remote, across every config scope. */\nasync function effectivePushUrls(cwd: string, remote: string): Promise<string[]> {\n const scoped = await readScopedAll(cwd, `remote.${remote}.pushurl`);\n return scoped.map((entry) => entry.value);\n}\n\n/**\n * Points every remote's PUSH url at a sentinel, so `git push` fails at the\n * transport layer. Fetch is unaffected: fetch URLs are left alone, so a blocked\n * repository can still pull.\n *\n * Applies to ALL remotes, not just `origin` — a repository commonly has an\n * `upstream` or `backup` too, and protecting only one of them protects nothing\n * in particular.\n *\n * Each original is recorded in `arcane.originalPushUrl.<remote>` so\n * `spell unblock-push` restores exactly what was there rather than guessing\n * from the fetch URL, which is legitimately allowed to differ.\n */\nexport async function disablePushUrls(cwd: string): Promise<PushUrlResult[]> {\n const results: PushUrlResult[] = [];\n for (const remote of await listRemotes(cwd)) {\n // One remote must never take the others down with it. The previous loop let\n // a single `git config` error propagate, so an oddly-named remote left every\n // remote after it in git's ordering completely unprotected -- silently, in a\n // repository the operator had just been told was blocked. Failures are now\n // recorded per remote and reported, and the loop continues.\n try {\n results.push(await disableOneRemote(cwd, remote));\n } catch (error) {\n results.push({ remote, status: \"failed\", reason: describeError(error) });\n }\n }\n return results;\n}\n\nasync function disableOneRemote(cwd: string, remote: string): Promise<PushUrlResult> {\n const scoped = await readScopedAll(cwd, `remote.${remote}.pushurl`);\n\n // A pushurl set outside this repository cannot be neutralised from inside it.\n // Config is additive for multivalued keys: a local write ADDS to git's list,\n // it cannot subtract the outside value, and the outside value sorts first --\n // so the push is delivered and only then fails on the sentinel. Refuse and say\n // so, rather than writing a sentinel that makes the repository look covered\n // while the history walks out the front door.\n // `worktree` scope is writable from inside the repository, so it is ours to\n // manage, not foreign. Only genuinely outer scopes are unreachable.\n const OVERRIDABLE = new Set([\"local\", \"worktree\"]);\n const foreign = scoped.filter(\n (entry) => !OVERRIDABLE.has(entry.scope) && entry.value !== DISABLED_PUSH_URL,\n );\n if (foreign.length > 0) {\n const scopes = [...new Set(foreign.map((entry) => entry.scope))];\n const where = scopes.join(\", \");\n const flag = scopes[0] === \"unknown\" ? \"<scope>\" : `--${scopes[0]}`;\n return {\n remote,\n status: \"failed\",\n reason:\n `a push URL for this remote is configured at ${where} scope, which this repository cannot ` +\n `override — git would still deliver the push there. Remove it with ` +\n `\\`git config ${flag} --unset-all remote.${remote}.pushurl\\` and re-apply.`,\n };\n }\n\n const currentPushUrls = scoped.map((entry) => entry.value);\n if (currentPushUrls.length === 1 && currentPushUrls[0] === DISABLED_PUSH_URL) {\n return { remote, status: \"already-disabled\" };\n }\n\n const key = originalPushUrlKey(remote);\n // Record what was there BEFORE overwriting, and only if we have not already\n // recorded it -- otherwise a second disable would enshrine the sentinel as the\n // \"original\" and make restore impossible.\n if ((await readLocalAll(cwd, key)).length === 0 && !(await hadNoPushUrl(cwd, remote))) {\n if (currentPushUrls.length > 0) {\n await runGit(cwd, [\"config\", \"--local\", \"--replace-all\", key, currentPushUrls[0]!]);\n for (const extra of currentPushUrls.slice(1)) {\n await runGit(cwd, [\"config\", \"--local\", \"--add\", key, extra]);\n }\n } else {\n // A remote with no pushurl key pushes to its fetch URL. Record that as a\n // separate marker rather than a magic URL value, so restore removes the\n // key instead of pinning one that was never there -- and so a remote whose\n // genuine URL happened to equal the marker cannot be misread as \"none\".\n await runGit(cwd, [\"config\", \"--local\", NO_PUSH_URL_MARKER(remote), \"true\"]);\n }\n }\n\n // `--replace-all` rather than `git remote set-url --push`: a mirror remote\n // with two push URLs makes set-url refuse outright (\"has multiple values\"),\n // which used to abort the run and leave BOTH mirrors pushable.\n await runGit(cwd, [\n \"config\",\n \"--local\",\n \"--replace-all\",\n `remote.${remote}.pushurl`,\n DISABLED_PUSH_URL,\n ]);\n\n // Re-read what git ACTUALLY resolves, rather than trusting that the write did\n // what we intended. The scope-based refusal above catches the cases git labels\n // as an outer scope, but not all of them: a value contributed by a file the\n // repository `include`s is reported as scope `local`, survives\n // `--replace-all`, and — if the include precedes the remote section — sorts\n // first and delivers the push. Verifying the outcome closes that whole class\n // rather than the enumerated members of it, which is the difference between\n // this check and the last three rounds of enumerating members.\n const after = await effectivePushUrls(cwd, remote);\n if (after.length !== 1 || after[0] !== DISABLED_PUSH_URL) {\n const live = after.filter((url) => url !== DISABLED_PUSH_URL);\n return {\n remote,\n status: \"failed\",\n reason:\n `the sentinel was written but git still resolves a live push URL for this remote ` +\n `(${live.join(\", \")}). It most likely comes from a file this repository \\`include\\`s, ` +\n `which \\`--replace-all\\` cannot remove. Run \\`git config --show-origin --get-all ` +\n `remote.${remote}.pushurl\\` to find the file, and clear it there.`,\n };\n }\n return { remote, status: \"disabled\" };\n}\n\n/**\n * Remotes whose push URL is NOT fully disabled. Empty when fully covered.\n *\n * A remote counts as open if ANY of its push URLs is live — a mirror with one\n * blocked and one live URL still delivers the push.\n */\nexport async function undisabledRemotes(cwd: string): Promise<string[]> {\n const open: string[] = [];\n for (const remote of await listRemotes(cwd)) {\n // Effective, not local: a pushurl inherited from global or system scope is\n // a live delivery path, and reading only local scope reported such a remote\n // as covered while `--no-verify` alone still delivered the history.\n const urls = await effectivePushUrls(cwd, remote);\n if (urls.length === 0 || urls.some((url) => url !== DISABLED_PUSH_URL)) open.push(remote);\n }\n return open;\n}\n\n/**\n * Remotes still carrying the sentinel push URL.\n *\n * The inverse of `undisabledRemotes`, and needed after an unblock: if a remote\n * was renamed while blocked, its recorded original moved with it but a different\n * remote can be left holding the sentinel with nothing to restore from. Checked\n * directly rather than inferred from what the restore believes it did.\n */\nexport async function blockedRemotes(cwd: string): Promise<string[]> {\n const blocked: string[] = [];\n for (const remote of await listRemotes(cwd)) {\n const urls = await effectivePushUrls(cwd, remote);\n if (urls.some((url) => url === DISABLED_PUSH_URL)) blocked.push(remote);\n }\n return blocked;\n}\n\n/** Restores every recorded push URL. Used only by `spell unblock-push`. */\nexport async function restorePushUrls(cwd: string): Promise<PushUrlResult[]> {\n const results: PushUrlResult[] = [];\n for (const remote of await listRemotes(cwd)) {\n try {\n results.push(await restoreOneRemote(cwd, remote));\n } catch (error) {\n results.push({ remote, status: \"failed\", reason: describeError(error) });\n }\n }\n return results;\n}\n\nasync function restoreOneRemote(cwd: string, remote: string): Promise<PushUrlResult> {\n const key = originalPushUrlKey(remote);\n let recorded = await readLocalAll(cwd, key);\n let hadNone = await hadNoPushUrl(cwd, remote);\n\n if (recorded.length === 0 && !hadNone) {\n const legacy = await readLegacyRecord(cwd, remote);\n if (legacy.status !== \"usable\") return { remote, status: legacy.status, reason: legacy.reason };\n recorded = legacy.values;\n hadNone = false;\n }\n\n if (hadNone) {\n // `--unset-all` exits 5 when the key is already absent. That is the goal\n // state, not a failure -- letting it throw skipped the stale-key cleanup\n // below, and a surviving `arcaneHadNoPushUrl` marker made the NEXT block\n // record nothing and the next restore delete a genuine push URL.\n await unsetIgnoringMissing(cwd, `remote.${remote}.pushurl`);\n } else {\n await runGit(cwd, [\n \"config\",\n \"--local\",\n \"--replace-all\",\n `remote.${remote}.pushurl`,\n recorded[0]!,\n ]);\n for (const extra of recorded.slice(1)) {\n await runGit(cwd, [\"config\", \"--local\", \"--add\", `remote.${remote}.pushurl`, extra]);\n }\n }\n\n // Clear our bookkeeping only on success. Doing this unconditionally would\n // destroy a record we had *deliberately* declined to apply -- the legacy-key\n // refusal above prints the value for the operator to apply by hand, and\n // wiping it in the same breath makes that instruction unfollowable.\n //\n // The stale-marker hazard this used to guard against is closed at its source:\n // `unsetIgnoringMissing` treats git's exit 5 (\"key already absent\") as\n // success, so the throw that previously skipped this cleanup no longer\n // happens.\n for (const staleKey of [key, NO_PUSH_URL_MARKER(remote), `arcane.originalPushUrl.${remote}`]) {\n try {\n await unsetIgnoringMissing(cwd, staleKey);\n } catch {\n // Non-fatal: the URL is restored, which is what matters.\n }\n }\n return { remote, status: \"restored\" };\n}\n\n/**\n * Unsets a config key, treating \"it was already absent\" as success.\n *\n * `git config --unset-all` exits **5** for a missing key. Letting that throw\n * turned a no-op into a reported failure and, worse, skipped whatever cleanup\n * followed it.\n */\nasync function unsetIgnoringMissing(cwd: string, key: string): Promise<void> {\n try {\n await runGit(cwd, [\"config\", \"--local\", \"--unset-all\", key]);\n } catch (error) {\n if ((error as { code?: unknown }).code !== 5) throw error;\n }\n}\n\nasync function hadNoPushUrl(cwd: string, remote: string): Promise<boolean> {\n return (await readLocalAll(cwd, NO_PUSH_URL_MARKER(remote)))[0] === \"true\";\n}\n\n/**\n * Reads a record left by the released `0.20.0`, which stored originals in a flat\n * `arcane.originalPushUrl.<remote>` key -- and only applies it when it can be\n * trusted.\n *\n * Applying it unconditionally reintroduced, for exactly the population told to\n * upgrade, the wrong-remote push this release fixes. Two ways, both reproduced:\n *\n * - Trailing config-key segments are case-insensitive, so `0.20.0` wrote one\n * key for both `origin` and `Origin` and only the last writer survived.\n * Restoring it points one remote at the OTHER remote's URL, reported as a\n * clean success.\n * - Unlike a `remote.<name>.*` key, the flat key survives `git remote remove`.\n * A remote deleted and re-added to a different URL gets silently restored to\n * the old target.\n *\n * So the record is used only when the remote is still carrying OUR sentinel\n * (which a re-added remote is not) and no two remote names collide\n * case-insensitively. Otherwise the value is reported for the operator to apply\n * by hand: a restore that might pick the wrong remote is worse than none.\n */\nasync function readLegacyRecord(\n cwd: string,\n remote: string,\n): Promise<\n | { status: \"usable\"; values: string[] }\n | { status: \"nothing-recorded\" | \"failed\"; reason?: string }\n> {\n const values = await readLocalAll(cwd, `arcane.originalPushUrl.${remote}`);\n if (values.length === 0) return { status: \"nothing-recorded\" };\n\n const current = await effectivePushUrls(cwd, remote);\n if (!current.includes(DISABLED_PUSH_URL)) {\n return {\n status: \"failed\",\n reason:\n `found a push URL recorded by 0.20.0 (\"${values.join(\", \")}\") but this remote is not ` +\n \"currently blocked by Arcane, so the record may belong to a different remote of the same \" +\n \"name. Not applying it — set the URL yourself if it is correct.\",\n };\n }\n\n const names = await listRemotes(cwd);\n const collides = names.some(\n (other) => other !== remote && other.toLowerCase() === remote.toLowerCase(),\n );\n if (collides) {\n return {\n status: \"failed\",\n reason:\n `this repository has remotes whose names differ only by case, and 0.20.0 stored their ` +\n `original push URLs under one shared key, so the recorded value (\"${values.join(\", \")}\") ` +\n \"cannot be attributed to a specific remote. Not applying it — set the URLs yourself.\",\n };\n }\n\n return { status: \"usable\", values };\n}\n\nfunction describeError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n return message.split(\"\\n\")[0]!.trim();\n}\n\nexport interface PushControlMessage {\n level: \"info\" | \"warning\";\n text: string;\n}\n\nexport interface BlockedPushControlsOutcome {\n hook: HookInstallOutcome;\n /** Empty when no remote exists, or when the hook refusal stopped the run first. */\n urls: PushUrlResult[];\n /** True when `stopOnHookRefusal` returned before touching any push URL. */\n stoppedAtHook: boolean;\n messages: PushControlMessage[];\n}\n\n/** True for every hook outcome that leaves the blocking hook in force. */\nexport function hookInForce(outcome: HookInstallOutcome): boolean {\n return outcome.status === \"installed\" || outcome.status === \"already-ours\";\n}\n\n/**\n * Applies `push_policy: \"blocked\"`'s two controls (ARC-034 decision 2) and\n * describes the result. Shared by `spell init` and `spell block-push`\n * (ARC-049 decision 1) so the two install exactly the same thing with the\n * same refusals. Messages are emitted as they occur, as well as returned, so\n * a throw part-way leaves the caller with everything already reported.\n *\n * `stopOnHookRefusal` returns before any push URL is touched when the hook is\n * refused: `block-push` refuses outright rather than leave half a block in a\n * repository whose manifest will not claim it. `init` keeps applying the URL\n * layer regardless, as it always has.\n */\nexport async function applyBlockedPushControls(\n cwd: string,\n options: { stopOnHookRefusal?: boolean; emit?: (message: PushControlMessage) => void } = {},\n): Promise<BlockedPushControlsOutcome> {\n const messages: PushControlMessage[] = [];\n const say = (level: PushControlMessage[\"level\"], text: string): void => {\n const message = { level, text };\n messages.push(message);\n options.emit?.(message);\n };\n\n const hook = await installPrePushHook(cwd);\n if (hook.status === \"refused-unreadable-config\") {\n // Fail closed: we could not determine whether another hook manager\n // owns core.hooksPath, and installing on a guess could disable it.\n say(\n \"warning\",\n \"Did not install the pre-push hook: git could not report the current core.hooksPath \" +\n \"(unreadable config, or a git too old for `--show-scope`). Installing without knowing \" +\n \"whether another hook manager owns that setting could silently disable it. Fix the \" +\n \"config and run `spell doctor`.\",\n );\n } else if (hook.status === \"refused-default-hooks\") {\n // Same harm as a foreign core.hooksPath, reached by the route the R7\n // guard wasn't watching: this repository's hooks live in git's\n // default directory and have no config key to collide with, so\n // taking the slot would switch every one of them off silently.\n say(\n \"warning\",\n `Did not install the pre-push hook: this repository has hooks in git's default ` +\n `directory (${hook.hooks.join(\", \")}). Setting core.hooksPath would silently stop ` +\n `them running. Move them under \\`${ARCANE_HOOKS_DIR}\\` yourself and re-run, or add ` +\n \"the push guard to your existing pre-push hook.\",\n );\n } else if (hook.status === \"refused-foreign-hooks-path\") {\n // R7: core.hooksPath is one exclusive slot, at local OR global\n // scope, so pointing it at Arcane would silently disable whatever\n // hook manager already owns it.\n say(\n \"warning\",\n `Did not install the pre-push hook: core.hooksPath is already \"${hook.existing}\" ` +\n `(${describeConfigScope(hook.scope)}), so another hook manager owns it. Overwriting ` +\n \"would silently disable those hooks. Chain an Arcane pre-push guard into that \" +\n \"directory yourself, or unset core.hooksPath first.\",\n );\n } else {\n say(\"info\", \"Installed a pre-push hook that blocks pushes from this repository.\");\n }\n\n if (options.stopOnHookRefusal === true && !hookInForce(hook)) {\n return { hook, urls: [], stoppedAtHook: true, messages };\n }\n\n const urls = await disablePushUrls(cwd);\n const unprotected = urls.filter((u) => u.status === \"failed\");\n if (unprotected.length > 0) {\n // Never let a partial application read as a full one.\n say(\n \"warning\",\n `Could not disable the push URL for: ${unprotected\n .map((u) => `${u.remote} (${u.reason ?? \"unknown error\"})`)\n .join(\"; \")}. Those remotes are still pushable with \\`--no-verify\\`. ` +\n \"Run `spell doctor` — it lists exactly which remotes are still live.\",\n );\n }\n if (urls.length === 0) {\n // Be precise: nothing is protecting a remote added later, because\n // this only disables remotes that exist right now. Claiming\n // otherwise would be exactly the false confidence ARC-034 warns of.\n say(\n \"warning\",\n \"No remote is configured, so only the pre-push hook is active. A remote added later \" +\n \"will NOT have its push URL disabled automatically, and a `--no-verify` push to it \" +\n \"would succeed. Run `spell doctor` after adding one — it reports this gap.\",\n );\n } else {\n const covered = urls.filter((u) => u.status !== \"failed\").map((u) => u.remote);\n if (covered.length > 0) {\n say(\"info\", `Disabled the push URL for: ${covered.join(\", \")} (fetch still works).`);\n }\n }\n say(\"info\", \"Run `spell unblock-push` from a terminal to undo this.\");\n return { hook, urls, stoppedAtHook: false, messages };\n}\n","/**\n * Regex-based credential-pattern denylist, sharing denylist-scan.ts's generic\n * file-walking/matching engine (also used by org-token-lint.ts and spell\n * ward). Extracted from scripts/copy-assets.ts (ARC-037) so the same pattern\n * set backs both the pre-existing src/assets/-scoped copy-time scan and the\n * repository-wide CI backstop / pre-commit hook this ADR adds.\n *\n * Patterns are deliberately conservative -- common credential shapes. A false\n * positive blocks a build or a commit, which is the safe direction to fail.\n */\nimport type { DenylistRule } from \"./denylist-scan.js\";\nimport { installHook, isHookInstalled, removeHook, type HookInstallOutcome } from \"./push-safety.js\";\n\nexport const SECRETS_RULES: DenylistRule[] = [\n { label: \"api-key\", pattern: /API[_-]KEY\\s*[:=]\\s*\\S+/i },\n // Case-sensitive and requiring an all-caps/underscore identifier (SECRET,\n // MY_SECRET, API_SECRET_KEY...) -- the real config/env-var convention for a\n // credential-holding key. Widening this repository-wide (ARC-037 decision 3)\n // surfaced that the original case-insensitive form matched ANY lowercase\n // `secret`/`token` code identifier (e.g. `const token = value.trim();`),\n // which is overwhelmingly a parsed/resolved value, never a literal\n // credential. The `(?!\\$?\\{)` guard additionally excludes any\n // brace-delimited placeholder -- GitHub Actions/templating references\n // (`TOKEN: ${{ secrets.X }}`, a reference to a secret store, never the\n // secret itself) and this codebase's own documented `{UPPER_SNAKE}`\n // placeholder convention (denylist-scan.ts's `scanFile` already strips\n // those before matching for the sibling org-token scan; a bare single\n // brace, with no `scanFile`-style stripping in the copy-time call path,\n // needed the same exclusion here rather than only the double-brace case).\n // A minimum length of 8 further excludes short non-secret values like\n // `write` (`id-token: write`).\n { label: \"generic-secret\", pattern: /\\b[A-Z0-9_]*SECRET[A-Z0-9_]*\\s*[:=]\\s*(?!\\$?\\{)\\S{8,}/ },\n { label: \"generic-token\", pattern: /\\b[A-Z0-9_]*TOKEN[A-Z0-9_]*\\s*[:=]\\s*(?!\\$?\\{)\\S{8,}/ },\n { label: \"bearer-token\", pattern: /Bearer\\s+[A-Za-z0-9._~+/-]{20,}/ }, // real tokens are long\n { label: \"openai-key\", pattern: /sk-[A-Za-z0-9]{20,}/ },\n { label: \"pem-key\", pattern: /-----BEGIN [A-Z ]+-----/ },\n { label: \"slack-token\", pattern: /xox[bpars]-[A-Za-z0-9-]+/ },\n { label: \"aws-access-key\", pattern: /AKIA[0-9A-Z]{16}/ },\n { label: \"github-pat\", pattern: /ghp_[A-Za-z0-9]{36}/ },\n];\n\n/**\n * Back-compat plain-pattern list for `scripts/copy-assets.ts`'s existing\n * per-line copy-time scan, which predates `DenylistRule` and iterates raw\n * `RegExp`s directly. Derived from `SECRETS_RULES` so there is exactly one\n * place these patterns are authored.\n */\nexport const SECRETS_PATTERNS: RegExp[] = SECRETS_RULES.map((rule) => rule.pattern);\n\n// ─── Consumer-facing pre-commit hook (ARC-037 decision 2 / decision 4b) ──────\n// This repo's OWN pre-commit scan is a Husky step (.husky/pre-commit calls\n// `npm run doctor:leaks`); a fresh consumer repo has no Husky. This installs\n// the same `spell doctor --leaks` check via push-safety.ts's generalized,\n// push_policy-independent hook-install path (ARC-037 decision 2) instead --\n// every profile gets it regardless of push_policy, since credential leakage\n// is a risk independent of whether a repository's history may reach a\n// remote. Mandatory rather than an opt-in prompt: the check is cheap, local,\n// and `--no-verify`-bypassable by design (decision 6) -- the repository-wide\n// CI backstop this ADR also adds is the real, unavoidable defense, not this.\n\nexport const SECRETS_PRECOMMIT_HOOK_NAME = \"pre-commit\";\n\n/**\n * Pure code, not a shipped asset file -- the same shape as push-safety.ts's\n * own PRE_PUSH_HOOK_BODY. `spell` is expected on PATH per the documented\n * global-install pattern (README.md); a missing binary skips the scan rather\n * than blocking the commit; a misconfigured PATH has nothing to do with\n * whatever the operator is actually committing.\n */\nexport const SECRETS_PRECOMMIT_HOOK_BODY = `#!/bin/sh\n# Installed by Arcane (ARC-037) to scan staged content for accidentally\n# committed credentials before they enter history.\n#\n# This is deliberately not tamper-proof: \\`git commit --no-verify\\` skips it\n# entirely. The real defense is the repository-wide scan Arcane's own build\n# runs in CI on every push -- this hook exists to catch a leak locally, for\n# free, before it ever reaches a commit.\nif ! command -v spell >/dev/null 2>&1; then\n echo \"arcane: 'spell' not found on PATH -- skipping secrets scan (see: npm install -g arcane-cli)\" >&2\n exit 0\nfi\nspell doctor --leaks\n`;\n\nexport async function installSecretsPrecommitHook(cwd: string): Promise<HookInstallOutcome> {\n return installHook(cwd, SECRETS_PRECOMMIT_HOOK_NAME, SECRETS_PRECOMMIT_HOOK_BODY);\n}\n\nexport async function isSecretsPrecommitHookInstalled(cwd: string): Promise<boolean> {\n return isHookInstalled(cwd, SECRETS_PRECOMMIT_HOOK_NAME, SECRETS_PRECOMMIT_HOOK_BODY);\n}\n\nexport async function removeSecretsPrecommitHook(cwd: string): Promise<void> {\n return removeHook(cwd, SECRETS_PRECOMMIT_HOOK_NAME);\n}\n","import { readFile, stat, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { copyFile, fileExists, hashFile, listDirectoryFiles, removeEmptyAncestors, removeWithin } from \"./copier.js\";\nimport type { RegistryComponent } from \"../types.js\";\n\nexport type DestinationState = \"missing\" | \"identical\" | \"differs\" | \"directory\";\n\nexport interface PlannedFile {\n /** Installed path, relative to the repository root. */\n file: string;\n /** Absolute path of the packaged file it is copied from. */\n src: string;\n state: DestinationState;\n}\n\n/**\n * What installing one component would do, decided from the disk before\n * anything is written. `spell add` and `spell update` both consume it, so a\n * dry run and the real run cannot disagree (ARC-052 decision 3).\n */\nexport interface InstallPlan {\n component: RegistryComponent;\n /** Every file the component ships, directories flattened. */\n files: PlannedFile[];\n /** Files that will be written: absent, identical (adopted), or present under `force`. */\n toCopy: PlannedFile[];\n /** Present and user-owned (`skipExisting`): left alone, and not recorded. */\n kept: PlannedFile[];\n /** Present and not user-owned: refuse unless the caller forces. A directory is always refused. */\n conflicts: PlannedFile[];\n}\n\nexport async function planComponentInstall(\n component: RegistryComponent,\n targetDir: string,\n assetsDir: string,\n opts: { force?: boolean } = {},\n): Promise<InstallPlan> {\n const files: PlannedFile[] = [];\n const stateOf = async (file: string, src: string): Promise<PlannedFile> => {\n const dest = join(targetDir, file);\n if (!(await fileExists(dest))) return { file, src, state: \"missing\" };\n if ((await stat(dest)).isDirectory()) return { file, src, state: \"directory\" };\n const same = (await hashFile(dest)) === (await hashFile(src));\n return { file, src, state: same ? \"identical\" : \"differs\" };\n };\n for (const file of component.files) {\n files.push(await stateOf(file, join(assetsDir, component.sourceOverrides?.[file] ?? file)));\n }\n for (const dir of component.directories ?? []) {\n const srcDir = join(assetsDir, dir);\n for (const file of await listDirectoryFiles(srcDir, dir)) {\n files.push(await stateOf(file, join(assetsDir, file)));\n }\n }\n\n const of = (...states: DestinationState[]) => files.filter((f) => states.includes(f.state));\n // --force overwrites a file's content; it never deletes a tree, so a directory stays a conflict.\n if (opts.force) {\n return { component, files, toCopy: of(\"missing\", \"identical\", \"differs\"), kept: [], conflicts: of(\"directory\") };\n }\n if (component.skipExisting) {\n return { component, files, toCopy: of(\"missing\"), kept: of(\"identical\", \"differs\", \"directory\"), conflicts: [] };\n }\n return { component, files, toCopy: of(\"missing\", \"identical\"), kept: [], conflicts: of(\"differs\", \"directory\") };\n}\n\nexport interface InstalledFiles {\n files: string[];\n fileHashes: Record<string, string>;\n}\n\n/**\n * Writes the plan's `toCopy` set and returns what to record in the manifest.\n * A failure part-way undoes the files already written (a created file is\n * removed, an overwritten one gets its previous content back) before the\n * error is rethrown, so nothing is half-installed and unrecorded.\n */\nexport async function executeInstallPlan(plan: InstallPlan, targetDir: string): Promise<InstalledFiles> {\n const files: string[] = [];\n const fileHashes: Record<string, string> = {};\n const written: { file: string; previous: Buffer | null }[] = [];\n try {\n for (const { file, src, state } of plan.toCopy) {\n const previous = state === \"missing\" ? null : await readFile(join(targetDir, file));\n fileHashes[file] = await copyFile(src, targetDir, file, { force: true });\n written.push({ file, previous });\n files.push(file);\n }\n } catch (err) {\n await rollback(written, targetDir);\n throw err;\n }\n return { files, fileHashes };\n}\n\nasync function rollback(written: { file: string; previous: Buffer | null }[], targetDir: string): Promise<void> {\n for (const { file, previous } of [...written].reverse()) {\n try {\n if (previous === null) {\n await removeWithin(targetDir, file);\n await removeEmptyAncestors(targetDir, file);\n } else {\n await writeFile(join(targetDir, file), previous);\n }\n } catch {\n // Best effort: the error that stopped the install is the one to report.\n }\n }\n}\n","import {\n readManifest,\n writeManifest,\n addComponent,\n hasComponent,\n ManifestNotFoundError,\n} from \"../modules/manifest.js\";\nimport { getComponent, ComponentNotFoundError } from \"../modules/registry.js\";\nimport {\n planComponentInstall,\n executeInstallPlan,\n type InstallPlan,\n type PlannedFile,\n} from \"../modules/component-install.js\";\nimport type { InstalledComponent, SpellAddOptions } from \"../types.js\";\n\ntype AddOutcome = \"added\" | \"skipped\" | \"failed\";\n\nfunction describeExisting(f: PlannedFile): string {\n if (f.state === \"directory\") return \"is a directory\";\n return f.state === \"identical\" ? \"identical to the packaged file\" : \"differs from the packaged file\";\n}\n\nfunction directoryConflict(plan: InstallPlan): PlannedFile | undefined {\n return plan.conflicts.find((f) => f.state === \"directory\");\n}\n\nfunction printDryRun(name: string, plan: InstallPlan, options: SpellAddOptions): void {\n for (const f of plan.files) {\n if (f.state === \"missing\") {\n console.log(` [dry-run] Would copy: ${f.file}`);\n } else if (f.state === \"directory\" && !(plan.component.skipExisting && !options.force)) {\n console.log(` [dry-run] Would refuse: ${f.file} (exists, is a directory) — remove it first`);\n } else if (options.force) {\n console.log(` [dry-run] Would overwrite: ${f.file} (exists, ${describeExisting(f)})`);\n } else if (plan.component.skipExisting) {\n console.log(` [dry-run] Would keep: ${f.file} (exists, yours, ${describeExisting(f)})`);\n } else if (f.state === \"identical\") {\n console.log(` [dry-run] Would adopt: ${f.file} (exists, identical to the packaged file)`);\n } else {\n console.log(` [dry-run] Would refuse: ${f.file} (exists, ${describeExisting(f)}) — needs --force`);\n }\n }\n const dir = directoryConflict(plan);\n if (dir) {\n console.log(`\\n[dry-run] Would refuse component \"${name}\": a directory is at \"${dir.file}\", which --force does not replace. Remove it first.`);\n return;\n }\n if (plan.conflicts.length > 0) {\n console.log(\n `\\n[dry-run] Would refuse component \"${name}\": ${plan.conflicts.length} existing file${plan.conflicts.length === 1 ? \"\" : \"s\"}. Re-run with --force to overwrite.`,\n );\n return;\n }\n console.log(`\\n[dry-run] Would add component \"${name}\" \\u2014 ${plan.toCopy.length} files`);\n}\n\nasync function addOne(\n name: string,\n options: SpellAddOptions,\n targetDir: string,\n assetsDir: string,\n packageVersion: string,\n): Promise<AddOutcome> {\n const manifest = await readManifest(targetDir);\n\n let component;\n try {\n component = getComponent(name);\n } catch (err) {\n if (err instanceof ComponentNotFoundError) {\n console.error(err.message);\n return \"failed\";\n }\n throw err;\n }\n\n if (hasComponent(manifest, name)) {\n console.log(\n `Component \"${name}\" is already installed. Use \"spell update\" to update it.`,\n );\n return \"skipped\";\n }\n\n const plan = await planComponentInstall(component, targetDir, assetsDir, { force: options.force });\n\n if (options.dryRun) {\n printDryRun(name, plan, options);\n return plan.conflicts.length > 0 ? \"failed\" : \"added\";\n }\n\n const dir = directoryConflict(plan);\n if (dir) {\n console.error(\n `Cannot add \"${name}\": \"${dir.file}\" is a directory, which --force does not replace. Nothing was written. Remove it first.`,\n );\n return \"failed\";\n }\n if (plan.conflicts.length > 0) {\n const others = plan.conflicts.length - 1;\n console.error(\n `Cannot add \"${name}\": \"${plan.conflicts[0]!.file}\" already exists${others > 0 ? ` (and ${others} more)` : \"\"}. Nothing was written. Use --force to overwrite.`,\n );\n return \"failed\";\n }\n\n const { files, fileHashes } = await executeInstallPlan(plan, targetDir);\n for (const f of plan.kept) console.log(` Kept existing (yours): ${f.file}`);\n\n const newComponent: InstalledComponent = {\n name,\n files,\n installedVersion: packageVersion,\n fileHashes,\n };\n await writeManifest(targetDir, addComponent(manifest, newComponent));\n\n console.log(\n `\\n\\u2713 Added component \"${name}\" \\u2014 ${files.length} files copied${plan.kept.length > 0 ? `, ${plan.kept.length} kept` : \"\"}`,\n );\n return \"added\";\n}\n\n/**\n * Runs the `spell add <component...>` command: each name in order, stopping\n * at the first failure (R-293c); a dry run walks every name so it reports all\n * the refusals at once. A name already installed is skipped, not a\n * failure. A refusal is one error line and exit 1, with nothing written for\n * that component (R-294b).\n *\n * @param names Component names to install\n * @param options CLI flags (force, dryRun)\n * @param targetDir Directory containing the Arcane installation\n * @param assetsDir Path to the bundled assets root\n * @param packageVersion Current package version string\n */\nexport async function runAdd(\n names: string[],\n options: SpellAddOptions,\n targetDir: string,\n assetsDir: string,\n packageVersion: string,\n): Promise<void> {\n try {\n await readManifest(targetDir);\n } catch (err) {\n if (err instanceof ManifestNotFoundError) {\n console.error(\n 'Not initialized. Run \"spell init\" first before adding components.',\n );\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n throw err;\n }\n\n const added: string[] = [];\n const refused: string[] = [];\n for (const [index, name] of names.entries()) {\n const outcome = await addOne(name, options, targetDir, assetsDir, packageVersion);\n if (outcome === \"added\") added.push(name);\n if (outcome !== \"failed\") continue;\n refused.push(name);\n if (options.dryRun) continue;\n if (names.length > 1) {\n const notAttempted = names.slice(index + 1);\n console.error(\n `Added: ${added.length > 0 ? added.join(\", \") : \"none\"}.${notAttempted.length > 0 ? ` Not attempted: ${notAttempted.join(\", \")}.` : \"\"}`,\n );\n }\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n if (refused.length > 0) {\n console.error(`[dry-run] Would refuse: ${refused.join(\", \")}.`);\n process.exit(1);\n }\n}\n","import { dirname, join } from \"node:path\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport {\n copyFile,\n copyDirectory,\n fileExists,\n fileMatchesHash,\n hashFile,\n removeEmptyAncestors,\n removeWithin,\n validateTargetPath,\n} from \"../modules/copier.js\";\nimport {\n readManifest,\n writeManifest,\n ManifestNotFoundError,\n} from \"../modules/manifest.js\";\nimport { inspectGitRepository } from \"../modules/git.js\";\nimport {\n getComponent,\n ComponentNotFoundError,\n SPELL_COMPONENT_NAMES,\n LEGACY_COMPONENT_MIGRATIONS,\n COMPONENT_FILE_MOVES,\n REGISTRY_RETIREMENTS,\n} from \"../modules/registry.js\";\nimport {\n MANIFEST_RETROFITS,\n runManifestRetrofits,\n offerRegistryScaffold,\n pushPolicyNotice,\n describeFlagPatch,\n hasManifestFlags,\n manifestFlagLine,\n resolveManifestFlags,\n ManifestFlagError,\n} from \"../modules/hub.js\";\nimport { merge3 } from \"../modules/merge3.js\";\nimport {\n findMissingRequires,\n findNewlyAvailable,\n findTrackedRetirements,\n formatRegistryChanges,\n type RegistryChanges,\n} from \"../modules/registry-changes.js\";\nimport {\n describeInstallPlan,\n executeUpdateInstalls,\n offerableNewComponents,\n planUpdateInstalls,\n selectNewComponents,\n type UpdateInstallPlan,\n} from \"../modules/update-installs.js\";\nimport { fetchPublishedFile } from \"../modules/npm-registry.js\";\nimport { isClientShimPath } from \"../modules/spell-compiler.js\";\nimport {\n componentForScope,\n componentForSpellScope,\n describeFanoutOutcomes,\n effectiveSpellScope,\n misplacedUserManifestMessage,\n resolveInstallScope,\n spellIdsInStore,\n SPELL_FANOUT_CLIENTS,\n syncUserTierFanout,\n userTierRoot,\n} from \"../modules/user-tier.js\";\nimport type {\n ArcaneManifest,\n InstallScope,\n InstalledComponent,\n RegistryComponent,\n SpellUpdateOptions,\n} from \"../types.js\";\n\n/**\n * The `default_spell_scope` change a run should record, or `undefined` when it\n * asks for nothing new (ARC-048).\n *\n * Store-only and preference-only: it decides which answer `spell init`\n * pre-selects for its scope question in a NEW repository on this machine. It\n * never touches an existing repository's manifest, and it does not change what\n * an absent `spell_scope` means -- that stays `\"repo\"` permanently.\n */\n/**\n * Warns, loudly, when a repository depends on a user tier that is not there\n * (ARC-048's gate). `spell doctor`'s scope check already fails on this, but\n * nothing runs `spell doctor` -- no workflow, no hook and no spell -- so it\n * fires when a human types it, which is not when the failure happens.\n * `spell update` is a command people DO run, and a run that says nothing is\n * how a silent opt-out stays silent.\n *\n * Returns the lines rather than printing them, so a test can read them.\n *\n * The home is resolved on its own, NOT from `runUpdate`'s `homeDir`: that one\n * is `dirname(targetDir)`, which is the home only on a user-tier run. On a\n * repository run it is the repository's PARENT DIRECTORY, and an early version\n * of this warning named it -- pointing the operator at a `.arcane` beside\n * their project that was never going to exist.\n */\nexport async function missingTierWarning(\n scope: InstallScope,\n spellScope: InstallScope,\n homeDir?: string,\n): Promise<string[]> {\n if (scope !== \"repo\" || spellScope !== \"user\") return [];\n const storeRoot = homeDir === undefined ? userTierRoot() : userTierRoot(homeDir);\n try {\n await readManifest(storeRoot);\n return [];\n } catch {\n return [\n \"\",\n ` ! This repository takes its spells from the user tier, and ${storeRoot} is not installed.`,\n \" No client can reach a spell here: the routing block in CLAUDE.md and AGENTS.md still\",\n \" names spells that resolve to nothing, so an agent improvises the workflow instead of\",\n \" failing. Fix it with one of:\",\n \"\",\n \" spell init --user # install the tier on this machine\",\n ' # or set \"spell_scope\": \"repo\" in .arcane.json and re-run `spell update`',\n \"\",\n ];\n }\n}\n\n/**\n * Names what the flags recorded, then the ARC-049 notice if push_policy\n * changed by any route in this run. Update installs no push controls.\n */\nfunction reportFlagPatch(\n flagPatch: Partial<ArcaneManifest>,\n before: ArcaneManifest,\n after: ArcaneManifest,\n dryRun: boolean | undefined,\n): void {\n console.log(`\\n${dryRun ? \"[dry-run] Would record\" : \"Recorded\"} from flags:`);\n for (const line of describeFlagPatch(flagPatch)) console.log(` ${line}`);\n if (!dryRun && after.push_policy !== before.push_policy) {\n for (const line of pushPolicyNotice(after.push_policy)) console.log(line);\n }\n}\n\nexport function defaultScopePatch(\n options: SpellUpdateOptions,\n manifest: ArcaneManifest,\n): { default_spell_scope: InstallScope } | undefined {\n if (options.defaultScope === undefined) return undefined;\n if (manifest.default_spell_scope === options.defaultScope) return undefined;\n return { default_spell_scope: options.defaultScope };\n}\n\n/**\n * Runs the `spell update` command.\n *\n * For each installed component, looks up the current registry definition and\n * re-copies files from the current package assets. User-owned files in a\n * skipExisting component are preserved when present and backfilled when absent.\n * The manifest is updated with the new file paths so future updates remain\n * correct even when source paths change between versions.\n *\n * @param options CLI flags (dryRun)\n * @param targetDir Directory containing the Arcane installation\n * @param assetsDir Path to the bundled assets root\n * @param packageVersion Current package version string\n */\n/**\n * Rewrites manifest entries whose component was split or renamed in a later\n * release, so `update` can refresh their files instead of skipping them.\n *\n * Deterministic and idempotent: an already-migrated manifest contains no\n * legacy names, so this returns it unchanged. Entry order is preserved, with\n * each legacy entry's replacements expanded in place. Replacements inherit the\n * legacy entry's `installedVersion` and an empty file list -- the caller\n * re-derives each replacement's files from the registry, which is the source\n * of truth for what a component contains *now*.\n *\n * What the registry cannot supply is the set of paths the legacy component\n * actually wrote into this repository. A spell retired since that install\n * belongs to no current component, so once its entry is expanded, nothing\n * would ever look at it again: not reported, not prunable, and dropped from\n * the manifest in the same run. Those paths and their recorded hashes are\n * therefore returned separately as `inherited`, for the caller to feed through\n * the same orphan resolution every other tracked-but-unclaimed file gets.\n * Discarding them -- which this function used to do -- is exactly how a\n * consumer upgrading across the split kept a dead spell on disk forever.\n *\n * Both legacy spell components map to the same replacement set, so a manifest\n * listing both (the common case) is deduped rather than producing doubles.\n * `carrier` names the replacement that adopts any inherited file still on\n * disk, so a file Arcane wrote stays tracked instead of being forgotten.\n */\nexport interface LegacyMigration {\n components: InstalledComponent[];\n inherited: {\n files: string[];\n fileHashes: Record<string, string>;\n /** null when the manifest held no legacy entry at all. */\n carrier: string | null;\n };\n}\n\nexport function migrateLegacyComponents(components: InstalledComponent[]): LegacyMigration {\n const out: InstalledComponent[] = [];\n const seen = new Set<string>();\n const inheritedFiles: string[] = [];\n const inheritedHashes: Record<string, string> = {};\n let carrier: string | null = null;\n\n for (const entry of components) {\n const replacements = LEGACY_COMPONENT_MIGRATIONS[entry.name];\n if (!replacements) {\n if (seen.has(entry.name)) continue;\n seen.add(entry.name);\n out.push(entry);\n continue;\n }\n\n for (const file of entry.files) {\n if (!inheritedFiles.includes(file)) inheritedFiles.push(file);\n const hash = entry.fileHashes?.[file];\n // First writer wins -- deterministic given entry order, and both legacy\n // names describe the same physical install anyway.\n if (hash !== undefined && inheritedHashes[file] === undefined) {\n inheritedHashes[file] = hash;\n }\n }\n\n for (const name of replacements) {\n carrier ??= name;\n if (seen.has(name)) continue;\n seen.add(name);\n out.push({ name, files: [], installedVersion: entry.installedVersion });\n }\n }\n\n return {\n components: out,\n inherited: { files: inheritedFiles, fileHashes: inheritedHashes, carrier },\n };\n}\n\n/**\n * Re-homes tracked files that moved between two live components\n * (COMPONENT_FILE_MOVES), keeping each file's recorded hash. Idempotent.\n *\n * The destination entry is created only when the source actually tracked a\n * moved file. One that never did -- the operator's own file, preserved and\n * never recorded -- leaves the destination uninstalled, so the\n * newly-available report can offer it (or skip it, when the file is already\n * there) instead of `update` claiming a component Arcane never wrote.\n */\nexport function moveComponentFiles(\n components: InstalledComponent[],\n present: (file: string) => boolean = () => true,\n): InstalledComponent[] {\n let out = components;\n for (const move of COMPONENT_FILE_MOVES) {\n const from = out.find((c) => c.name === move.from);\n const tracked = from?.files.filter((f) => move.files.includes(f)) ?? [];\n if (!from || tracked.length === 0) continue;\n // A moved file already deleted from disk is dropped, not re-homed: an\n // entry with no files would make `spell add` refuse it forever, while an\n // absent entry lets the newly-available report offer a working `spell add`.\n const moving = tracked.filter(present);\n const dropped = tracked.filter((f) => !present(f));\n\n const movedHashes: Record<string, string> = {};\n const keptHashes: Record<string, string> = {};\n for (const [file, hash] of Object.entries(from.fileHashes ?? {})) {\n if (dropped.includes(file)) continue;\n (moving.includes(file) ? movedHashes : keptHashes)[file] = hash;\n }\n const newFrom: InstalledComponent = {\n ...from,\n files: from.files.filter((f) => !tracked.includes(f)),\n ...(from.fileHashes ? { fileHashes: keptHashes } : {}),\n };\n const existing = out.find((c) => c.name === move.to);\n const newTo: InstalledComponent = existing\n ? {\n ...existing,\n files: [...existing.files, ...moving.filter((f) => !existing.files.includes(f))],\n fileHashes: { ...movedHashes, ...existing.fileHashes },\n }\n : {\n name: move.to,\n files: moving,\n installedVersion: from.installedVersion,\n ...(from.fileHashes ? { fileHashes: movedHashes } : {}),\n };\n\n out = out.flatMap((c) => {\n if (c === from) return existing || moving.length === 0 ? [newFrom] : [newFrom, newTo];\n if (c === existing) return [newTo];\n return [c];\n });\n }\n return out;\n}\n\nexport type OrphanStatus = \"pruned\" | \"reported\" | \"not-found\";\n\n/**\n * Resolves one orphaned file: previously tracked, no longer part of any\n * current component (TODO.md T10). Report-only unless `prune` is set, and\n * even then only deletes when the on-disk content still matches the hash\n * Arcane last recorded -- an operator edit is exactly as worth preserving\n * here as it is in the main update loop (ARC-038 decision 1's \"do not\n * silently discard the edit\" applies to deleting a file, not only\n * overwriting one). A file with no recorded hash (predates ARC-038, or was\n * never hashed) is reported but never auto-pruned -- there is nothing to\n * verify it against, and guessing is not this feature's job.\n */\nexport async function resolveOrphan(\n targetDir: string,\n file: string,\n recordedHash: string | undefined,\n prune: boolean,\n): Promise<OrphanStatus> {\n // A manifest path is repository content: one that resolves outside the\n // target directory is never read, hashed or deleted -- the same guard every\n // write path applies, reported rather than acted on.\n try {\n validateTargetPath(targetDir, file);\n } catch {\n console.log(` ! Refused (path escapes the target directory): ${file}`);\n return \"reported\";\n }\n const filePath = join(targetDir, file);\n if (!(await fileExists(filePath))) return \"not-found\";\n if (!prune) return \"reported\";\n\n if (recordedHash === undefined) {\n console.log(` ! Orphaned, not pruned (no recorded hash to verify it's untouched): ${file}`);\n return \"reported\";\n }\n if (!(await fileMatchesHash(filePath, recordedHash))) {\n console.log(` ! Orphaned but edited since install — not pruning: ${file}`);\n return \"reported\";\n }\n await removeWithin(targetDir, file);\n // A component's files often sit in a directory of their own\n // (.agents/skills/<id>/SKILL.md); leaving 41 empty directories behind is\n // not \"pruned\".\n await removeEmptyAncestors(targetDir, file);\n console.log(` Pruned orphaned file: ${file}`);\n return \"pruned\";\n}\n\n/**\n * Whether a retired user-owned component's file is the operator's to keep:\n * on disk, and either edited since Arcane wrote it or never hashed by Arcane\n * (pre-dates ARC-038, or Arcane kept the operator's own copy at install).\n * Such a file leaves the manifest instead of being reported as an orphan on\n * every run -- the orphan path protects files Arcane owns, and a\n * `skipExisting` file was the operator's from the moment it was written\n * (#328). An untouched copy is not released: it is still the scaffold exactly\n * as shipped, and `--prune` is the right way to remove it. A path that\n * escapes the target directory is never read; resolveOrphan refuses it.\n */\nasync function isReleasableUserOwnedFile(\n targetDir: string,\n file: string,\n recordedHash: string | undefined,\n): Promise<boolean> {\n try {\n validateTargetPath(targetDir, file);\n } catch {\n return false;\n }\n const filePath = join(targetDir, file);\n if (!(await fileExists(filePath))) return false;\n if (recordedHash === undefined) return true;\n return !(await fileMatchesHash(filePath, recordedHash));\n}\n\n/**\n * An orphan that is still ON DISK stays in the manifest, with whatever hash\n * was recorded for it. The manifest records what Arcane put in this\n * repository, and the file is still there: dropping the entry would untrack a\n * file Arcane wrote, which `spell uninstall` then leaks (the hazard the\n * preserveExisting branch names), would overwrite an operator's edit without a\n * word if the component is ever re-adopted, and would make the remedy `update`\n * prints a lie -- a report-only run that forgot its own orphans leaves\n * `spell update --prune` with nothing to find. Reported every run until it is\n * pruned or removed by hand; dropped as soon as it is (\"pruned\"/\"not-found\").\n */\nasync function retainReportedOrphan(\n targetDir: string,\n file: string,\n recordedHash: string | undefined,\n into: InstalledComponent,\n): Promise<void> {\n try {\n validateTargetPath(targetDir, file);\n } catch {\n // Escaping path: refused and reported by resolveOrphan, and deliberately\n // dropped from the manifest -- it is not a file Arcane could have written.\n return;\n }\n if (!(await fileExists(join(targetDir, file)))) return;\n\n if (!into.files.includes(file)) into.files.push(file);\n if (recordedHash !== undefined) {\n into.fileHashes ??= {};\n into.fileHashes[file] = recordedHash;\n }\n}\n\n/**\n * Tracked files that should be on disk but are not. Feeds the same-version\n * restore path in `runUpdate` (ARC-045 / CS-03): an operator who has ported a\n * customized client shim's edits into the canonical spell deletes the old\n * file and runs `spell update` to get the generated shim back -- at the\n * same version, since nothing else changed. Only files the current registry\n * still ships for that component count (anything else is an orphan, handled\n * by `resolveOrphan`). `initOnly` components never do -- `update` must not\n * create those at any version (EF-17) -- and neither do `skipExisting`\n * (user-owned) ones: a continuity file, `.mcp.json` or `journal/.gitkeep`\n * an operator deleted on purpose is backfilled only by a version change, as\n * it always was, not on every same-version run. A component the registry no\n * longer knows has nothing to restore from.\n */\n/**\n * Spell components a repository is RE-ADOPTING: it previously opted out\n * (`spell_scope: \"user\"`, so their manifest entries were emptied and their\n * files pruned) and has now switched back. Their files are shipped by the\n * registry but neither tracked nor on disk, so without naming this case a\n * same-version `spell update` would answer \"Already up to date.\" and the\n * repository would stay empty until the next release -- a rollback that\n * silently does nothing (ARC-045 decision 4 / CS-05).\n *\n * Deliberately narrow. It does NOT relax CS-03's rule that an untracked\n * registry file is neither created nor claimed on a same-version run: only\n * a spell component whose manifest entry lists zero files qualifies, which\n * is a state nothing but the opt-out produces.\n */\nexport function findReadoptedSpellComponents(\n components: InstalledComponent[],\n scope: InstallScope = \"repo\",\n spellScope: InstallScope = \"repo\",\n): Set<string> {\n if (scope !== \"repo\" || spellScope !== \"repo\") return new Set();\n return new Set(\n components\n .filter((c) => SPELL_COMPONENT_NAMES.includes(c.name) && c.files.length === 0)\n .map((c) => c.name),\n );\n}\nexport async function findMissingTrackedFiles(\n targetDir: string,\n components: InstalledComponent[],\n scope: InstallScope = \"repo\",\n spellScope: InstallScope = \"repo\",\n readopted: Set<string> = new Set(),\n): Promise<string[]> {\n const missing: string[] = [];\n for (const installed of components) {\n let component: RegistryComponent;\n try {\n component = componentForSpellScope(\n componentForScope(getComponent(installed.name), scope),\n scope === \"repo\" ? spellScope : \"repo\",\n );\n } catch {\n continue;\n }\n if (component.initOnly || component.skipExisting) continue;\n const candidates = readopted.has(installed.name) ? component.files : installed.files;\n for (const file of candidates) {\n if (!component.files.includes(file)) continue;\n if (!(await fileExists(join(targetDir, file)))) missing.push(file);\n }\n }\n return missing;\n}\n\n/**\n * Tracked files the current registry no longer ships for their component --\n * the orphan candidates. Existence is deliberately not checked here: that is\n * `resolveOrphan`'s job, and this only has to answer whether a run has\n * anything to say at all.\n *\n * It exists because of the opt-out (ARC-045 decision 4 / CS-05). A\n * repository that sets `spell_scope: \"user\"` without a version change would\n * otherwise meet the `Already up to date.` short-circuit below -- nothing\n * missing, nothing to write -- and its now-unmanaged spell files would go\n * unmentioned until the next release. The opt-out has to be visible on the\n * very next `spell update`, which is the command an operator runs after\n * editing the field.\n */\nexport function findUnmanagedTrackedFiles(\n components: InstalledComponent[],\n scope: InstallScope = \"repo\",\n spellScope: InstallScope = \"repo\",\n): string[] {\n const unmanaged: string[] = [];\n for (const installed of components) {\n let component: RegistryComponent;\n try {\n component = componentForSpellScope(\n componentForScope(getComponent(installed.name), scope),\n scope === \"repo\" ? spellScope : \"repo\",\n );\n } catch {\n // Component gone from the registry entirely: every file it tracked is\n // an orphan, and the main loop already reports that case by itself.\n unmanaged.push(...installed.files);\n continue;\n }\n const shipped = new Set(component.files);\n for (const file of installed.files) {\n if (!shipped.has(file)) unmanaged.push(file);\n }\n }\n return unmanaged;\n}\nexport async function runUpdate(\n options: SpellUpdateOptions,\n targetDir: string,\n assetsDir: string,\n packageVersion: string,\n): Promise<void> {\n // Read existing manifest\n let manifest: ArcaneManifest;\n try {\n manifest = await readManifest(targetDir);\n } catch (err) {\n if (err instanceof ManifestNotFoundError) {\n console.error(\n `Not initialized. Run \"spell init${options.user ? \" --user\" : \"\"}\" first before updating.`,\n );\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n throw err;\n }\n // PRD D-09: re-home files that moved component before anything below\n // (up-to-date check, orphan sweep) reads the old entry as their owner.\n const movedFilesPresent = new Set<string>();\n for (const move of COMPONENT_FILE_MOVES) {\n for (const file of move.files) {\n if (await fileExists(join(targetDir, file))) movedFilesPresent.add(file);\n }\n }\n manifest = {\n ...manifest,\n components: moveComponentFiles(manifest.components, (f) => movedFilesPresent.has(f)),\n };\n\n // ARC-045 decision 3 / CS-04: the user tier at ~/.arcane is not a\n // repository -- its merge baseline is the recorded hash plus the published\n // vendor file, so the git checks below do not apply; and its client shims\n // live outside the store and are reconciled after the store itself (see\n // the fan-out calls below). Everything else in this command runs unchanged\n // over the store through componentForScope.\n const resolvedScope = resolveInstallScope(targetDir, manifest.scope, options.user);\n if (resolvedScope.misplacedUserManifest) {\n console.error(misplacedUserManifestMessage(targetDir));\n process.exit(1);\n return;\n }\n const scope: InstallScope = resolvedScope.scope;\n\n // ARC-052 / D-07: newly available components are a repository question.\n if (options.addNew && scope !== \"repo\") {\n console.error(\n \"\\n ✗ --add-new offers the components a repository's profile gained; the user tier has no profile. Run it without --user, inside a repository.\\n\",\n );\n process.exit(1);\n return;\n }\n\n // PRD D-02: validated and conflict-checked before anything is written, so a\n // refused flag (including any attempt to loosen push_policy) changes nothing.\n let flagPatch: Partial<ArcaneManifest> = {};\n if (hasManifestFlags(options.manifestFlags)) {\n if (scope !== \"repo\") {\n console.error(\n \"\\n ✗ The manifest-question flags describe a repository; the user tier has none of those fields.\\n\",\n );\n process.exit(1);\n return;\n }\n try {\n flagPatch = resolveManifestFlags(options.manifestFlags, manifest, join(targetDir, \".arcane.json\"));\n } catch (err) {\n if (!(err instanceof ManifestFlagError)) throw err;\n console.error(`\\n ✗ ${err.message}\\n`);\n process.exit(1);\n return;\n }\n }\n const answered: ArcaneManifest = { ...manifest, ...flagPatch };\n // ARC-045 decision 4 / CS-05: where THIS repository takes its spells\n // from. Absent means \"repo\", so every manifest written before 1.2.0\n // behaves exactly as it did. Meaningless for the store itself, which is\n // the thing being pointed at -- hence the scope === \"repo\" guards below.\n const spellScope: InstallScope = effectiveSpellScope(manifest);\n // Components this repository is re-adopting after opting back in; see\n // findReadoptedSpellComponents. Empty in every other case.\n const readopted = findReadoptedSpellComponents(manifest.components, scope, spellScope);\n const homeDir = dirname(targetDir);\n for (const line of await missingTierWarning(scope, spellScope)) console.log(line);\n\n if (scope === \"repo\") {\n console.warn(\n `WARNING: Arcane v${packageVersion} update safety notice: commit your work before updating.`,\n );\n console.warn(\n \"Updates can replace managed files. A clean committed baseline is required for recovery.\",\n );\n\n const gitState = await inspectGitRepository(targetDir);\n if (gitState.status === \"not-repository\") {\n console.error(\n \"Update refused: this directory is not a Git repository or Git is unavailable.\",\n );\n process.exit(1);\n return;\n }\n if (gitState.status === \"no-commits\") {\n console.error(\n \"Update refused: this repository has no commits. Commit the current baseline before updating.\",\n );\n process.exit(1);\n return;\n }\n if (gitState.uncommittedChanges > 0) {\n console.error(\n `Update refused: this repository has ${gitState.uncommittedChanges} uncommitted change${gitState.uncommittedChanges === 1 ? \"\" : \"s\"}. Commit or otherwise clean the working tree before updating.`,\n );\n process.exit(1);\n return;\n }\n }\n\n // PRD D-15, computed before anything is written. Retirements read the\n // manifest as it was, before migration renames a legacy entry away. The\n // newly-available list is repository-only: `spell add` has no --user.\n const registryChanges: RegistryChanges = {\n retired: findTrackedRetirements(manifest.components),\n ...(scope === \"repo\"\n ? {\n profile: manifest.profile,\n newlyAvailable: await findNewlyAvailable(\n targetDir,\n manifest.profile,\n migrateLegacyComponents(manifest.components).components,\n spellScope,\n ),\n missingRequires: findMissingRequires(manifest.components, manifest.profile),\n }\n : {}),\n };\n // ARC-052: beyond refreshing what is installed, `update` installs the\n // `requires` prerequisites of installed components, and the newly available\n // components the operator opted into (R-293b). The plan is made once, from\n // the disk, before anything is written: the dry run prints it and the real\n // run executes it. What is installed leaves the lists printed below.\n let installPlan: UpdateInstallPlan = { items: [], leftAlone: [] };\n if (scope === \"repo\" && manifest.components.length > 0) {\n const missingRequires = registryChanges.missingRequires ?? [];\n const selectedNew = await selectNewComponents(\n offerableNewComponents(registryChanges.newlyAvailable ?? [], missingRequires),\n { addNew: Boolean(options.addNew), interactive: Boolean(process.stdin.isTTY), dryRun: Boolean(options.dryRun) },\n );\n installPlan = await planUpdateInstalls({ targetDir, assetsDir, spellScope, missingRequires, selectedNew });\n const installing = new Set(installPlan.items.map((i) => i.plan.component.name));\n registryChanges.installedByUpdate = installing.size > 0;\n registryChanges.addNewUsed = Boolean(options.addNew);\n registryChanges.newlyAvailable = (registryChanges.newlyAvailable ?? []).filter((c) => !installing.has(c.name));\n registryChanges.missingRequires = missingRequires\n .filter((m) => !installing.has(m.name))\n .map((m) => ({ ...m, leftAloneReason: installPlan.leftAlone.find((l) => l.name === m.name)?.reason }));\n }\n const printRegistryChanges = (): void => {\n for (const line of formatRegistryChanges(registryChanges)) console.log(line);\n };\n const applyInstalls = async (): Promise<InstalledComponent[]> => {\n const declined = installPlan.leftAlone.filter((l) => l.kind === \"new\");\n if (installPlan.items.length === 0 && declined.length === 0) return [];\n console.log(options.dryRun ? \"\\n[dry-run] Components this update would install (ARC-052):\" : \"\\nComponents installed by this update (ARC-052):\");\n for (const l of declined) console.log(` ! Not installed: ${l.name} — ${l.reason}`);\n if (options.dryRun) {\n for (const line of describeInstallPlan(installPlan)) console.log(line);\n return [];\n }\n const { installed, lines } = await executeUpdateInstalls(installPlan, targetDir, packageVersion);\n for (const line of lines) console.log(line);\n return installed;\n };\n\n // Already up to date -- unless a tracked file has gone missing, in which\n // case a same-version run restores exactly that and nothing else (the\n // ARC-045 / CS-03 remedy path; see findMissingTrackedFiles).\n let sameVersionRestore = false;\n if (manifest.version === packageVersion && manifest.components.length > 0) {\n const missing = await findMissingTrackedFiles(targetDir, manifest.components, scope, spellScope, readopted);\n const unmanaged = findUnmanagedTrackedFiles(manifest.components, scope, spellScope);\n if (missing.length === 0 && unmanaged.length === 0) {\n console.log(installPlan.items.length === 0 ? \"Already up to date.\" : \"Installed components are at the current version.\");\n const newlyInstalled = await applyInstalls();\n // Still surfaced here: a component the operator has not added yet is\n // news on every run, not only on the run that changed the version.\n printRegistryChanges();\n // Flags still record their answers on a current install: a harness\n // finishing setup is not upgrading anything.\n if (scope === \"repo\" && (Object.keys(flagPatch).length > 0 || newlyInstalled.length > 0)) {\n if (!options.dryRun) {\n await writeManifest(targetDir, { ...answered, components: [...answered.components, ...newlyInstalled] });\n }\n if (Object.keys(flagPatch).length > 0) reportFlagPatch(flagPatch, manifest, answered, options.dryRun);\n }\n // The user tier's client files can go missing (or a renderer can\n // change) with the store itself intact -- reconcile them on every\n // same-version run too. A no-op when nothing changed.\n if (scope === \"user\") {\n const fanout = await syncUserTierFanout({\n ownedClients: SPELL_FANOUT_CLIENTS,\n homeDir,\n storeRoot: targetDir,\n spellIds: spellIdsInStore(manifest.components),\n previous: manifest.fanout,\n dryRun: options.dryRun,\n fallbackDir: assetsDir,\n });\n for (const line of describeFanoutOutcomes(fanout.outcomes, options.dryRun)) console.log(` ${line}`);\n const scopePatch = defaultScopePatch(options, manifest);\n if (\n !options.dryRun\n && (JSON.stringify(fanout.record) !== JSON.stringify(manifest.fanout ?? {})\n || scopePatch !== undefined)\n ) {\n await writeManifest(targetDir, { ...manifest, ...scopePatch, fanout: fanout.record });\n }\n }\n return;\n }\n sameVersionRestore = true;\n const prefix = options.dryRun ? \"[dry-run] \" : \"\";\n if (missing.length > 0) {\n console.log(\n `${prefix}Already at v${packageVersion}, but ${missing.length} tracked file${missing.length === 1 ? \" is\" : \"s are\"} missing — restoring.`,\n );\n } else {\n console.log(\n `${prefix}Already at v${packageVersion}, but ${unmanaged.length} tracked file${unmanaged.length === 1 ? \" is\" : \"s are\"} no longer managed here — reviewing.`,\n );\n }\n }\n\n if (manifest.components.length === 0) {\n console.log(\"No components installed. Nothing to update.\");\n if (scope === \"repo\" && Object.keys(flagPatch).length > 0) {\n if (!options.dryRun) await writeManifest(targetDir, answered);\n reportFlagPatch(flagPatch, manifest, answered, options.dryRun);\n }\n return;\n }\n\n let fileCount = 0;\n const updatedComponents: InstalledComponent[] = [];\n // ARC-038 decision 1: files whose merge produced genuine conflict markers,\n // collected across every component so the operator gets one summary at the\n // end rather than a per-file interruption -- the update still completes\n // for every other file either way.\n const conflictedFiles: string[] = [];\n // ARC-045 / CS-03: generated client shims the operator had edited, kept\n // untouched rather than merged -- see the per-file branch below.\n const customizedShims: string[] = [];\n // TODO.md T10: files tracked before this update that no longer belong to\n // any current component, reported always and deleted only with --prune\n // (and only when untouched -- see resolveOrphan).\n const orphanReport: Array<{ file: string; status: OrphanStatus }> = [];\n // Collected during the component loop, resolved in one sweep after it.\n // The decision cannot be made per component: `updatedFiles` below is\n // per-component, so a file another component still ships looks orphaned\n // from here. Only the union of every component's claims answers the actual\n // question -- \"is this file part of any current component?\" -- and getting\n // that wrong means deleting a live file under --prune.\n const orphanCandidates: Array<{\n owner: string;\n file: string;\n recordedHash: string | undefined;\n }> = [];\n\n const { components: componentsToUpdate, inherited } =\n migrateLegacyComponents(manifest.components);\n\n // Files a legacy component wrote, whose entry has just been expanded away.\n // They belong to no component now by construction, so they enter the sweep\n // as candidates like any other unclaimed file.\n if (inherited.carrier !== null) {\n for (const file of inherited.files) {\n orphanCandidates.push({\n owner: inherited.carrier,\n file,\n recordedHash: inherited.fileHashes[file],\n });\n }\n }\n\n for (const installed of componentsToUpdate) {\n // Look up the current registry definition (source of truth for file paths)\n let component;\n try {\n // ARC-045 decision 4 / CS-05: in a repository that takes its spells\n // from the user tier, every spells-* component installs nothing -- and\n // the files it installed before opting out fall through to the orphan\n // path below, which reports them always and deletes them only under\n // --prune, only while their recorded hash still holds.\n component = componentForSpellScope(\n componentForScope(getComponent(installed.name), scope),\n scope === \"repo\" ? spellScope : \"repo\",\n );\n } catch (err) {\n if (err instanceof ComponentNotFoundError) {\n // Component removed from registry entirely -- every file it used to\n // track is now orphaned, not just individually dropped ones. Report\n // even during dry-run (that's the point of a preview); only the\n // actual deletion is dry-run-gated, inside resolveOrphan's `prune`.\n console.log(` ! ${installed.name} not in registry — skipping.`);\n // A retired user-owned component (#328): an edited or never-hashed\n // file is the operator's and leaves the manifest; only an untouched\n // copy stays on the orphan path. See isReleasableUserOwnedFile.\n const userOwned = REGISTRY_RETIREMENTS.some(\n (r) => r.kind === \"component\" && r.name === installed.name && r.userOwned === true,\n );\n const stillTracked: string[] = [];\n for (const file of installed.files) {\n const recordedHash = installed.fileHashes?.[file];\n if (userOwned && (await isReleasableUserOwnedFile(targetDir, file, recordedHash))) {\n console.log(\n ` ${options.dryRun ? \"[dry-run] Would release\" : \"Released\"}: ${file} — yours (edited since Arcane wrote it, or never written by Arcane), no longer tracked. Delete it yourself if you do not want it.`,\n );\n continue;\n }\n const status = await resolveOrphan(\n targetDir,\n file,\n recordedHash,\n Boolean(options.prune) && !options.dryRun,\n );\n orphanReport.push({ file, status });\n if (status === \"reported\") stillTracked.push(file);\n }\n if (userOwned && !options.dryRun) {\n // Released, pruned or gone: nothing left for a later run to retry.\n if (stillTracked.length === 0) continue;\n const fileHashes: Record<string, string> = {};\n for (const file of stillTracked) {\n const hash = installed.fileHashes?.[file];\n if (hash !== undefined) fileHashes[file] = hash;\n }\n updatedComponents.push({ ...installed, files: stillTracked, fileHashes });\n continue;\n }\n // Drop the manifest entry once every file it tracked is gone or was\n // never there; otherwise keep it so a future update can retry\n // pruning what --prune (or a hash mismatch) left behind.\n if (\n options.prune &&\n !options.dryRun &&\n installed.files.every((f) => orphanReport.find((o) => o.file === f)?.status !== \"reported\")\n ) {\n continue;\n }\n updatedComponents.push(installed);\n continue;\n }\n throw err;\n }\n\n // Copy files using current registry paths (handles path changes between versions)\n const updatedFiles: string[] = [];\n const fileHashes: Record<string, string> = {};\n for (const file of component.files) {\n const srcPath = join(assetsDir, component.sourceOverrides?.[file] ?? file);\n const targetExists = await fileExists(join(targetDir, file));\n const preserveExisting = Boolean(component.skipExisting) && targetExists;\n const recordedHash = installed.fileHashes?.[file];\n\n // initOnly: update never creates these. Their appearance alone changes\n // how Git treats the whole repository, so adding one mid-life is the\n // operator's call, not a side effect of a version upgrade (EF-17). That\n // holds even for a `requires` prerequisite: update installs nothing on\n // its own except the prerequisites of installed components, and never an\n // initOnly one (ARC-052).\n if (component.initOnly && !targetExists) {\n console.log(\n ` ! Missing: ${file} — not added automatically, because doing so would change how Git treats existing files. Run \"spell add ${component.name}\" if you want it.`,\n );\n continue;\n }\n\n // Same-version restore (see the up-to-date check above): only a file\n // that is tracked, absent, and shipped by a non-user-owned component\n // falls through to be written -- exactly the set the gate counted.\n // Everything else keeps its recorded state: a present file is not\n // re-hashed or merged, an untracked registry file is neither created\n // nor claimed, and a user-owned (skipExisting) file is left to the\n // version-change backfill it always had.\n if (sameVersionRestore) {\n const tracked = installed.files.includes(file) || readopted.has(installed.name);\n const restorable = tracked && !targetExists && !component.skipExisting;\n if (!restorable) {\n if (tracked) {\n updatedFiles.push(file);\n if (recordedHash !== undefined) fileHashes[file] = recordedHash;\n }\n continue;\n }\n }\n\n // ARC-038 decision 1: on-disk content that no longer matches what\n // Arcane last wrote means the operator edited this file, and that edit\n // must never be silently discarded.\n const editedByOperator =\n recordedHash !== undefined &&\n targetExists &&\n !preserveExisting &&\n !(await fileMatchesHash(join(targetDir, file), recordedHash));\n\n if (preserveExisting) {\n // skipExisting keeps its pre-ARC-038 whole-file behavior unchanged --\n // no hash tracking, no merge machinery (ARC-038 decision 1).\n console.log(` ${options.dryRun ? \"[dry-run] Would preserve\" : \"Preserved\"}: ${file}`);\n } else if (editedByOperator && isClientShimPath(file)) {\n // ARC-045 decision 2 / CS-03: a generated client shim (Copilot\n // prompt, Claude command, Codex skill) carries no authored prose, so\n // there is nothing an operator's edit could be merged INTO. A\n // three-way merge here either reports success while leaving the edit\n // dangling under the new shim, or writes conflict markers over a body\n // that is gone -- both observed live against a real consumer fixture\n // (2026-09-09, features/codex-support/architecture.md). Keep the\n // operator's file byte-untouched, name it, and carry the previously\n // recorded hash forward: recording the edited content would make the\n // next update read the file as untouched and overwrite it; recording\n // nothing would drop it into the pre-ARC-038 overwrite path. The\n // canonical spell is written beside it as an ordinary new file, so the\n // spell keeps working in every client meanwhile.\n customizedShims.push(file);\n fileHashes[file] = recordedHash!;\n console.log(\n ` ${options.dryRun ? \"[dry-run] Would keep\" : \"Kept\"} customized (not merged): ${file}`,\n );\n } else if (options.dryRun) {\n console.log(` [dry-run] Would ${sameVersionRestore ? \"restore missing\" : \"update\"}: ${file}`);\n fileCount++;\n } else {\n let handled = false;\n\n if (editedByOperator) {\n // Fetch by the ASSET path: the published tarball holds the source\n // (`docs-baseline/gitignore`, `.arcane/spells/<id>.md`), not the\n // installed name a sourceOverrides component or the user tier's\n // store gives it.\n const oldVendorContent = await fetchPublishedFile(\n installed.installedVersion,\n component.sourceOverrides?.[file] ?? file,\n );\n if (oldVendorContent === undefined) {\n console.log(\n ` ! Could not fetch the previously published version of ${file} to merge your edits — left your version untouched. Update it manually if you want the latest.`,\n );\n // Untouched means still edited: keep the recorded hash so the\n // next update recognizes the edit again instead of reading the\n // file as Arcane-written and overwriting it.\n fileHashes[file] = recordedHash!;\n handled = true;\n } else {\n const [currentContent, newContent] = await Promise.all([\n readFile(join(targetDir, file), \"utf-8\"),\n readFile(srcPath, \"utf-8\"),\n ]);\n const result = merge3(oldVendorContent, currentContent, newContent);\n await writeFile(join(targetDir, file), result.content, \"utf-8\");\n // Record the VENDOR content's hash, not the merged file's. The\n // recorded hash means \"what a clean install of this version\n // wrote\"; recording the merged content made the next update read\n // the file as untouched and overwrite it, so an operator's edit\n // survived exactly one update (observed live 2026-09-09, variant D\n // in features/codex-support/architecture.md).\n fileHashes[file] = await hashFile(srcPath);\n if (result.hasConflict) {\n conflictedFiles.push(file);\n console.log(` ⚠ Merge conflict in ${file} — resolve the <<<<<<< markers before committing.`);\n } else {\n console.log(` Merged your edits into: ${file}`);\n }\n handled = true;\n }\n }\n\n if (!handled) {\n fileHashes[file] = await copyFile(srcPath, targetDir, file, { force: true });\n if (sameVersionRestore) console.log(` Restored missing: ${file}`);\n }\n fileCount++;\n }\n\n // Only record a preserved file if Arcane installed it in the first\n // place. Recording one it merely declined to overwrite would claim\n // ownership of an operator-authored file, and `spell uninstall` deletes\n // everything the manifest lists -- so an operator's own .gitignore or\n // DECISIONS.md would be destroyed by an uninstall that never wrote it.\n // A file Arcane DID write must stay recorded, or uninstall leaks it.\n if (!preserveExisting || installed.files.includes(file)) {\n updatedFiles.push(file);\n }\n }\n // Also copy directories\n for (const dir of component.directories ?? []) {\n const srcDirPath = join(assetsDir, dir);\n if (options.dryRun) {\n console.log(` [dry-run] Would update directory: ${dir}/`);\n } else {\n // Directory-tracked files are always freshly generated content\n // (agent rosters, etc.), never hand-edited in place -- unconditional\n // overwrite, same as before ARC-038, is still correct here.\n const copied = await copyDirectory(srcDirPath, targetDir, dir, { force: true });\n for (const { path: copiedPath, hash } of copied) {\n updatedFiles.push(copiedPath);\n fileHashes[copiedPath] = hash;\n }\n fileCount += copied.length;\n }\n }\n\n // TODO.md T10: a file this component tracked before this update but no\n // longer does (dropped from the registry's current file/directory list)\n // is orphaned -- still on disk, no longer represented in the manifest\n // this update is about to write.\n for (const file of installed.files) {\n if (updatedFiles.includes(file)) continue;\n orphanCandidates.push({\n owner: installed.name,\n file,\n recordedHash: installed.fileHashes?.[file],\n });\n }\n\n updatedComponents.push({\n ...installed,\n files: updatedFiles,\n installedVersion: packageVersion,\n fileHashes,\n });\n }\n\n updatedComponents.push(...(await applyInstalls()));\n\n // One sweep, once every component has declared what it claims. A candidate\n // some other component still ships is not an orphan at all -- the check a\n // per-component loop structurally cannot make, and getting it wrong means\n // deleting a live file under --prune.\n const claimed = new Set(updatedComponents.flatMap((c) => c.files));\n const resolvedOrphans = new Set<string>();\n\n for (const { owner, file, recordedHash } of orphanCandidates) {\n if (claimed.has(file) || resolvedOrphans.has(file)) continue;\n resolvedOrphans.add(file);\n\n const status = await resolveOrphan(\n targetDir,\n file,\n recordedHash,\n Boolean(options.prune) && !options.dryRun,\n );\n orphanReport.push({ file, status });\n if (status !== \"reported\") continue;\n\n let into = updatedComponents.find((c) => c.name === owner);\n if (!into) {\n // The owner's entry was dropped (gone from the registry, fully pruned),\n // but this file is still on disk, so it still needs somewhere to live.\n into = { name: owner, files: [], installedVersion: packageVersion, fileHashes: {} };\n updatedComponents.push(into);\n }\n await retainReportedOrphan(targetDir, file, recordedHash, into);\n }\n\n // TODO.md T10: report orphans -- files tracked before this update but no\n // longer part of any current component -- in both dry-run and real runs.\n // Only files still present matter; anything already gone needs no action.\n const orphansToReport = orphanReport.filter((o) => o.status !== \"not-found\");\n if (orphansToReport.length > 0) {\n const verb = options.dryRun ? \"[dry-run] Found\" : \"Found\";\n console.log(\n `\\n${verb} ${orphansToReport.length} orphaned file(s) (tracked before this update, no longer part of any current component):`,\n );\n for (const { file, status } of orphansToReport) {\n const marker = status === \"pruned\" ? \"pruned\" : options.prune ? \"kept (see reason above)\" : \"not removed\";\n console.log(` ${file} — ${marker}`);\n }\n if (scope === \"repo\" && spellScope === \"user\") {\n console.log(\n ' This repository takes its spells from the user tier (spell_scope: \"user\"), so the files above are no longer managed here — every client reads ~/.arcane instead.',\n );\n }\n if (!options.prune) {\n console.log(\" Run `spell update --prune` to remove the ones that are safe to delete.\");\n }\n }\n\n printRegistryChanges();\n\n // ARC-045 / CS-03: one summary for every customized client shim, in both\n // dry-run and real runs, with the remedy spelled out once.\n if (customizedShims.length > 0) {\n const verb = options.dryRun ? \"[dry-run] Would keep\" : \"Kept\";\n console.log(\n `\\n! ${verb} ${customizedShims.length} customized client file(s) untouched (ARC-045): an edited Copilot prompt, Claude command or Codex skill is never merged into the new generated shim, because a shim carries no prose of its own to merge into.\\n` +\n customizedShims.map((f) => ` ${f}`).join(\"\\n\") +\n `\\n Each keeps working in its client as-is. To converge: move your edits into the canonical spell (.arcane/spells/<id>.md — later updates merge edits there), delete the customized file, commit, and run \\`spell update\\` again — it restores the generated shim at the current version.`,\n );\n }\n\n // ARC-045 decision 3 / CS-04: with the store current, reconcile the client\n // files outside it -- a renderer change regenerates every shim, a dropped\n // spell's shims are pruned, a missing one comes back, and an edited or\n // foreign file is left alone and named. Dry-run reports the same decisions.\n let fanoutRecord: Record<string, string> | undefined;\n if (scope === \"user\") {\n const fanout = await syncUserTierFanout({\n ownedClients: SPELL_FANOUT_CLIENTS,\n homeDir,\n storeRoot: targetDir,\n spellIds: spellIdsInStore(updatedComponents),\n previous: manifest.fanout,\n dryRun: options.dryRun,\n // A dry run has restored nothing yet, so a missing store spell renders\n // from the vendor asset the real run would write (review finding F1).\n fallbackDir: assetsDir,\n });\n const lines = describeFanoutOutcomes(fanout.outcomes, options.dryRun);\n if (lines.length > 0) console.log();\n for (const line of lines) console.log(` ${line}`);\n fanoutRecord = fanout.record;\n }\n\n if (options.dryRun) {\n console.log(\n `\\n[dry-run] Would update ${fileCount} files.`,\n );\n if (Object.keys(flagPatch).length > 0) reportFlagPatch(flagPatch, manifest, answered, true);\n const applicableRetrofits =\n scope === \"repo\" ? MANIFEST_RETROFITS.filter((r) => r.needsRetrofit(answered)) : [];\n if (applicableRetrofits.length > 0) {\n console.log(\n `[dry-run] Would ask ${applicableRetrofits.length} manifest retrofit question${applicableRetrofits.length === 1 ? \"\" : \"s\"}: ${applicableRetrofits.map((r) => r.field).join(\", \")}.`,\n );\n }\n return;\n }\n\n // Retrofit wizard: ask about any manifest field this install predates\n // (e.g. `role`), once, before writing the updated manifest.\n //\n // Skipped without a TTY. These are interactive questions, and @inquirer\n // throws ExitPromptError on a closed stdin -- which would crash `update`\n // AFTER files were copied but BEFORE the manifest was written, leaving the\n // repo with new files and a stale recorded version. CI, piped input, and\n // scripted upgrades all land here. The fields stay unset and are asked on\n // the next interactive run, which is exactly how a scripted `init` already\n // behaves.\n //\n // Skipped entirely for the user tier: every retrofit field describes a\n // repository (hub role, tracking mode, subject root, sensitivity, push\n // policy), none of which the store has.\n // Flags (PRD D-02) are applied first, so a flagged question is answered\n // without a prompt, with or without a terminal.\n const interactive = Boolean(process.stdin.isTTY) && scope === \"repo\";\n const retrofitPatch = interactive ? await runManifestRetrofits(answered, targetDir) : {};\n if (!interactive && scope === \"repo\") {\n const pending = MANIFEST_RETROFITS.filter((r) => r.needsRetrofit(answered));\n if (pending.length > 0) {\n console.log(\n ` ! Skipped ${pending.length} manifest question${pending.length === 1 ? \"\" : \"s\"} (${pending\n .map((r) => r.field)\n .join(\", \")}) — no interactive terminal. Run \\`spell update\\` from a terminal, or answer by flags:`,\n );\n for (const line of manifestFlagLine(answered) ?? []) console.log(` ${line}`);\n }\n }\n\n // Update manifest with new version, refreshed component file paths, and\n // any retrofit answers (and, for the user tier, the reconciled fan-out).\n const updated: ArcaneManifest = {\n ...manifest,\n version: packageVersion,\n components: updatedComponents,\n ...flagPatch,\n ...retrofitPatch,\n ...(fanoutRecord !== undefined ? { fanout: fanoutRecord } : {}),\n ...defaultScopePatch(options, manifest),\n };\n await writeManifest(targetDir, updated);\n\n console.log(\n `\\n\\u2713 Updated ${fileCount} files.`,\n );\n\n if (Object.keys(flagPatch).length > 0) {\n reportFlagPatch(flagPatch, manifest, updated, false);\n } else if (updated.push_policy !== manifest.push_policy) {\n for (const line of pushPolicyNotice(updated.push_policy)) console.log(line);\n }\n\n // ARC-038 decision 1: the update completes regardless of conflicts -- a\n // conflicted file blocking every other file's update would be a worse\n // outcome than a clearly flagged conflict the operator resolves before\n // committing (this repository's own pre-commit/pre-push hooks, and any\n // CI the operator has, still catch an unresolved <<<<<<< marker before it\n // reaches main).\n if (conflictedFiles.length > 0) {\n console.log(\n `\\n\\u26a0 ${conflictedFiles.length} file(s) have unresolved merge conflicts:\\n` +\n conflictedFiles.map((f) => ` ${f}`).join(\"\\n\") +\n `\\n Search for \"<<<<<<< yours\" and resolve before committing.`,\n );\n }\n\n // If this update just turned the repo into a hub, offer to scaffold the\n // venture registry from whatever already exists under business_root.\n if (scope === \"repo\" && manifest.role !== \"hub\" && updated.role === \"hub\") {\n // `--role hub` reaches this without a terminal; the scaffold offer is a prompt.\n if (process.stdin.isTTY) {\n await offerRegistryScaffold(targetDir, updated.business_root ?? \"ventures\");\n } else {\n console.log(\n ` This repository is now a hub. Run \\`spell update\\` from a terminal to scaffold ${updated.business_root ?? \"ventures\"}/registry.json from existing venture folders.`,\n );\n }\n }\n}\n","/**\n * Line-based three-way text merge (ARC-038 decision 1).\n *\n * `spell update` uses this to reconcile an operator's edited copy of a\n * shipped file against a new vendor version, without silently discarding the\n * edit: `base` is the OLD vendor content the operator's edit started from,\n * `ours` is the operator's current on-disk content, `theirs` is the NEW\n * vendor content about to be installed. Non-overlapping changes on each side\n * merge automatically; a region both sides changed differently becomes a\n * conflict, marked the same way `git merge` marks one.\n *\n * Deliberately line-granular, not character-granular -- the same mental\n * model a human resolving a merge conflict already has, and the one ARC-038\n * itself names (\"the same shape copier's regenerate-old/diff/regenerate-new/\n * merge approach uses\").\n */\n\nexport const CONFLICT_START = \"<<<<<<< yours (edited)\";\nexport const CONFLICT_MID = \"=======\";\nexport const CONFLICT_END = \">>>>>>> incoming (vendor update)\";\n\ninterface DiffOp {\n type: \"equal\" | \"replace\";\n baseStart: number;\n baseEnd: number;\n otherStart: number;\n otherEnd: number;\n}\n\n/**\n * Line-based LCS diff of `base` against `other`. DP table is O(n*m) time and\n * space -- fine for governance-doc-sized files (hundreds to low thousands of\n * lines); `merge3` falls back to a whole-file conflict rather than running\n * this on pathologically large inputs (see MAX_LINES_FOR_LCS below).\n */\nfunction computeLcsDiff(base: string[], other: string[]): DiffOp[] {\n const n = base.length;\n const m = other.length;\n const dp: Uint32Array[] = Array.from({ length: n + 1 }, () => new Uint32Array(m + 1));\n for (let i = n - 1; i >= 0; i--) {\n for (let j = m - 1; j >= 0; j--) {\n dp[i]![j] =\n base[i] === other[j] ? dp[i + 1]![j + 1]! + 1 : Math.max(dp[i + 1]![j]!, dp[i]![j + 1]!);\n }\n }\n\n const ops: DiffOp[] = [];\n let i = 0;\n let j = 0;\n let pendingBaseStart = 0;\n let pendingOtherStart = 0;\n\n const flushReplace = (baseEnd: number, otherEnd: number) => {\n if (baseEnd > pendingBaseStart || otherEnd > pendingOtherStart) {\n ops.push({\n type: \"replace\",\n baseStart: pendingBaseStart,\n baseEnd,\n otherStart: pendingOtherStart,\n otherEnd,\n });\n }\n };\n\n while (i < n && j < m) {\n if (base[i] === other[j]) {\n flushReplace(i, j);\n const eqBaseStart = i;\n const eqOtherStart = j;\n while (i < n && j < m && base[i] === other[j]) {\n i++;\n j++;\n }\n ops.push({ type: \"equal\", baseStart: eqBaseStart, baseEnd: i, otherStart: eqOtherStart, otherEnd: j });\n pendingBaseStart = i;\n pendingOtherStart = j;\n } else if (dp[i + 1]![j]! >= dp[i]![j + 1]!) {\n i++;\n } else {\n j++;\n }\n }\n flushReplace(n, m);\n return ops;\n}\n\nfunction arraysEqual(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false;\n return a.every((line, idx) => line === b[idx]);\n}\n\nexport interface Merge3Result {\n content: string;\n hasConflict: boolean;\n}\n\n/**\n * Files larger than this fall back to a single whole-file conflict rather\n * than running the O(n*m) LCS diff -- a correctness-preserving degradation\n * (the operator is never silently overwritten, they're just not offered an\n * auto-merge for a file this large), not a crash or a silent choice of one\n * side. No governance/prompt content shipped by Arcane comes close to this.\n */\nconst MAX_LINES_FOR_LCS = 4000;\n\n/** Splits on \\n, preserving trailing-newline presence as the final \"\" element does. */\nfunction splitLines(text: string): string[] {\n return text.split(\"\\n\");\n}\n\n/**\n * Merges `theirs` (new vendor content) onto `ours` (the operator's edited\n * content), using `base` (old vendor content) as the common ancestor.\n *\n * Never throws and never silently prefers one side: a region changed\n * differently by both sides is always marked as a conflict in the returned\n * content, and `hasConflict` tells the caller whether any such region\n * existed, so update.ts can report it rather than commit it quietly.\n */\nexport function merge3(base: string, ours: string, theirs: string): Merge3Result {\n const baseLines = splitLines(base);\n const oursLines = splitLines(ours);\n const theirsLines = splitLines(theirs);\n\n if (\n baseLines.length > MAX_LINES_FOR_LCS ||\n oursLines.length > MAX_LINES_FOR_LCS ||\n theirsLines.length > MAX_LINES_FOR_LCS\n ) {\n return {\n content: [CONFLICT_START, ours, CONFLICT_MID, theirs, CONFLICT_END].join(\"\\n\"),\n hasConflict: true,\n };\n }\n\n const oursOps = computeLcsDiff(baseLines, oursLines).filter((op) => op.type === \"replace\");\n const theirsOps = computeLcsDiff(baseLines, theirsLines).filter((op) => op.type === \"replace\");\n\n if (oursOps.length === 0 && theirsOps.length === 0) {\n return { content: base, hasConflict: false };\n }\n\n interface ChangedInterval {\n baseStart: number;\n baseEnd: number;\n source: \"ours\" | \"theirs\";\n lines: string[];\n }\n const intervals: ChangedInterval[] = [\n ...oursOps.map((op) => ({\n baseStart: op.baseStart,\n baseEnd: op.baseEnd,\n source: \"ours\" as const,\n lines: oursLines.slice(op.otherStart, op.otherEnd),\n })),\n ...theirsOps.map((op) => ({\n baseStart: op.baseStart,\n baseEnd: op.baseEnd,\n source: \"theirs\" as const,\n lines: theirsLines.slice(op.otherStart, op.otherEnd),\n })),\n ].sort((a, b) => a.baseStart - b.baseStart || a.baseEnd - b.baseEnd);\n\n // Coalesce intervals that overlap in BASE-line space, from either side,\n // into groups -- two edits any distance apart in \"ours\" and \"theirs\" text\n // can still collide if they touch the same base lines, which is exactly\n // the case a merge must resolve as one unit, not two independent ones.\n const groups: ChangedInterval[][] = [];\n let groupMaxEnd = -1;\n for (const iv of intervals) {\n const current = groups[groups.length - 1];\n if (current && iv.baseStart < groupMaxEnd) {\n current.push(iv);\n groupMaxEnd = Math.max(groupMaxEnd, iv.baseEnd);\n } else {\n groups.push([iv]);\n groupMaxEnd = iv.baseEnd;\n }\n }\n\n const resultLines: string[] = [];\n let hasConflict = false;\n let basePos = 0;\n\n for (const group of groups) {\n const groupStart = Math.min(...group.map((g) => g.baseStart));\n const groupEnd = Math.max(...group.map((g) => g.baseEnd));\n\n resultLines.push(...baseLines.slice(basePos, groupStart));\n\n const oursInGroup = group.filter((g) => g.source === \"ours\");\n const theirsInGroup = group.filter((g) => g.source === \"theirs\");\n\n if (oursInGroup.length > 0 && theirsInGroup.length === 0) {\n for (const g of oursInGroup) resultLines.push(...g.lines);\n } else if (theirsInGroup.length > 0 && oursInGroup.length === 0) {\n for (const g of theirsInGroup) resultLines.push(...g.lines);\n } else {\n const oursGroupLines = oursInGroup.flatMap((g) => g.lines);\n const theirsGroupLines = theirsInGroup.flatMap((g) => g.lines);\n if (arraysEqual(oursGroupLines, theirsGroupLines)) {\n // Converged edit -- both sides changed this region to the same\n // result, so there is nothing to actually resolve.\n resultLines.push(...oursGroupLines);\n } else {\n hasConflict = true;\n resultLines.push(CONFLICT_START, ...oursGroupLines, CONFLICT_MID, ...theirsGroupLines, CONFLICT_END);\n }\n }\n\n basePos = groupEnd;\n }\n resultLines.push(...baseLines.slice(basePos));\n\n return { content: resultLines.join(\"\\n\"), hasConflict };\n}\n","import { join } from \"node:path\";\nimport { fileExists } from \"./copier.js\";\nimport {\n ComponentNotFoundError,\n LEGACY_COMPONENT_MIGRATIONS,\n REGISTRY_RETIREMENTS,\n SPELL_COMPONENT_NAMES,\n getComponent,\n listProfiles,\n} from \"./registry.js\";\nimport type { InstallScope, InstalledComponent, Profile, RegistryRetirement } from \"../types.js\";\n\n/**\n * The \"Registry changes since your install\" section `spell update` prints\n * (PRD D-15). Report-only by construction: everything here reads the manifest,\n * the registry and the disk and returns lines; nothing writes, installs or\n * deletes (EF-17: adding files to an existing repository is the operator's\n * call). `spell update` installs the `requires` prerequisites of installed\n * components and, on request, newly available ones (ARC-052) -- in its own\n * step, then removes what it installed from these lists before they print.\n */\nexport interface RegistryChanges {\n /** Renamed or retired spells/components this manifest still tracks (#279). */\n retired: RegistryRetirement[];\n /** Components the manifest's profile includes today but the manifest lacks. */\n newlyAvailable?: NewlyAvailableComponent[];\n /** The profile `newlyAvailable` was computed against. */\n profile?: Profile;\n /** Components an installed component `requires` that are not installed. */\n missingRequires?: MissingRequirement[];\n /** `spell update` installed something this run, so \"nothing was added\" would be untrue. */\n installedByUpdate?: boolean;\n /** The run already used `--add-new`, so the hint to use it would be circular. */\n addNewUsed?: boolean;\n}\n\nexport interface MissingRequirement {\n name: string;\n requiredBy: string[];\n /** Why `spell update` did not install it this run (ARC-052), when it declined. */\n leftAloneReason?: string;\n}\n\n/**\n * Components named in an installed component's `requires` that the manifest\n * does not list, in registry order of first mention. A legacy entry counts as\n * the components that replaced it; a name the registry no longer knows\n * requires nothing.\n *\n * Only a requirement the repository's own profile includes is reported: a\n * profile that deliberately leaves the standards out (lite, methodology) must\n * not warn on a fresh install. An unrecognized or absent profile reports all.\n */\nexport function findMissingRequires(installed: InstalledComponent[], profile?: Profile): MissingRequirement[] {\n const names = new Set(installed.flatMap((c) => LEGACY_COMPONENT_MIGRATIONS[c.name] ?? [c.name]));\n const promised = listProfiles().find((p) => p.id === profile)?.components;\n const missing = new Map<string, string[]>();\n for (const name of names) {\n let requires: string[];\n try {\n requires = getComponent(name).requires ?? [];\n } catch (err) {\n if (err instanceof ComponentNotFoundError) continue;\n throw err;\n }\n for (const required of requires) {\n if (names.has(required)) continue;\n if (promised && !promised.includes(required)) continue;\n missing.set(required, [...(missing.get(required) ?? []), name]);\n }\n }\n return [...missing].map(([name, requiredBy]) => ({ name, requiredBy }));\n}\n\nexport interface NewlyAvailableComponent {\n name: string;\n description: string;\n /** Files of a user-owned (skipExisting) component already on disk. */\n alreadyPresent: string[];\n}\n\n// Every shape a spell file has been installed under: canonical source, the\n// Copilot/Claude shims, the Codex skill, and the user tier's store.\nconst SPELL_ID_IN_PATH = /(?:^|\\/)(spell-[a-z0-9-]+)(?:\\/SKILL\\.md|\\.prompt\\.md|\\.md)$/;\n\nexport function spellIdFromTrackedPath(file: string): string | undefined {\n return SPELL_ID_IN_PATH.exec(file)?.[1];\n}\n\n/**\n * Retirements a manifest still tracks: a component entry under a retired\n * name, or a tracked file belonging to a retired spell. Pass the manifest's\n * components as read -- before any legacy migration renames them away.\n */\nexport function findTrackedRetirements(components: InstalledComponent[]): RegistryRetirement[] {\n const componentNames = new Set(components.map((c) => c.name));\n const spellIds = new Set(\n components\n .flatMap((c) => c.files)\n .map(spellIdFromTrackedPath)\n .filter((id): id is string => id !== undefined),\n );\n return REGISTRY_RETIREMENTS.filter((entry) =>\n entry.kind === \"component\" ? componentNames.has(entry.name) : spellIds.has(entry.name),\n );\n}\n\n/**\n * Components the repository's profile would install today that its manifest\n * does not list -- typically ones added to the registry after this repository\n * was installed. Pass the components after legacy migration, so a renamed\n * entry counts as the components that replaced it.\n *\n * A repository that takes its spells from the user tier is not offered\n * spells-* components: it opted out of them. A user-owned (skipExisting)\n * component whose every file is already on disk is left out -- the operator\n * already has their own, and `spell add` would refuse to overwrite it.\n */\nexport async function findNewlyAvailable(\n targetDir: string,\n profile: Profile,\n installed: InstalledComponent[],\n spellScope: InstallScope,\n): Promise<NewlyAvailableComponent[]> {\n // An unrecognized profile (hand-edited manifest) has nothing to compare to.\n const definition = listProfiles().find((p) => p.id === profile);\n if (!definition) return [];\n\n const installedNames = new Set(installed.map((c) => c.name));\n const found: NewlyAvailableComponent[] = [];\n for (const name of definition.components) {\n if (installedNames.has(name)) continue;\n if (spellScope === \"user\" && SPELL_COMPONENT_NAMES.includes(name)) continue;\n const component = getComponent(name);\n const alreadyPresent: string[] = [];\n if (component.skipExisting) {\n for (const file of component.files) {\n if (await fileExists(join(targetDir, file))) alreadyPresent.push(file);\n }\n if (component.files.length > 0 && alreadyPresent.length === component.files.length) continue;\n }\n found.push({ name, description: component.description, alreadyPresent });\n }\n return found;\n}\n\nexport function formatRegistryChanges(changes: RegistryChanges): string[] {\n const missingRequires = changes.missingRequires ?? [];\n // A component listed as a missing prerequisite is not repeated below.\n const newlyAvailable = (changes.newlyAvailable ?? []).filter(\n (c) => !missingRequires.some((m) => m.name === c.name),\n );\n if (changes.retired.length === 0 && newlyAvailable.length === 0 && missingRequires.length === 0) return [];\n\n const lines = [\"\", \"Registry changes since your install:\"];\n if (changes.retired.length > 0) {\n lines.push(\" Renamed or retired, still tracked here:\");\n for (const entry of changes.retired) {\n const successor = entry.successors.length > 0 ? entry.successors.join(\", \") : \"no successor\";\n const ownership = entry.userOwned\n ? \" (user-owned: a copy you edited is released from tracking, never deleted)\"\n : \"\";\n lines.push(` ${entry.name} (${entry.kind}) → ${successor}: ${entry.reason}${ownership}`);\n }\n lines.push(\n \" Nothing is deleted because of this list: their files go through the orphan handling above, which removes an untouched one only under `spell update --prune`.\",\n );\n }\n if (newlyAvailable.length > 0) {\n const profile = changes.profile ? ` \"${changes.profile}\"` : \"\";\n lines.push(\n changes.installedByUpdate\n ? ` Newly available for your${profile} profile, not installed by this run:`\n : ` Newly available for your${profile} profile, not installed — nothing was added:`,\n );\n for (const component of newlyAvailable) {\n lines.push(` ${component.name} — ${component.description}`);\n lines.push(` spell add ${component.name}`);\n if (component.alreadyPresent.length > 0) {\n lines.push(\n ` (already here and yours: ${component.alreadyPresent.join(\", \")} — \\`spell add\\` keeps it rather than overwrite it)`,\n );\n }\n }\n if (!changes.addNewUsed) lines.push(\" To install them in one step: `spell update --add-new`\");\n }\n if (missingRequires.length > 0) {\n lines.push(\" Missing prerequisites — installed components cite these, and they are not installed:\");\n for (const { name, requiredBy, leftAloneReason } of missingRequires) {\n lines.push(` ${name} (required by ${requiredBy.join(\", \")})`);\n lines.push(` spell add ${name}`);\n if (leftAloneReason) lines.push(` (not installed by update: ${leftAloneReason})`);\n }\n }\n return lines;\n}\n","import { checkbox } from \"@inquirer/prompts\";\nimport { getComponent } from \"./registry.js\";\nimport { componentForSpellScope } from \"./user-tier.js\";\nimport {\n executeInstallPlan,\n planComponentInstall,\n type InstallPlan,\n} from \"./component-install.js\";\nimport type { MissingRequirement, NewlyAvailableComponent } from \"./registry-changes.js\";\nimport type { InstallScope, InstalledComponent } from \"../types.js\";\n\n/**\n * What `spell update` installs on top of refreshing what is already there\n * (ARC-052): the `requires` prerequisites of installed components, and the\n * newly available components the operator opted into (R-293b). Nothing here\n * installs an `initOnly` component, and nothing here installs over a file\n * that is already on disk.\n */\nexport interface UpdateInstallItem {\n plan: InstallPlan;\n kind: \"prerequisite\" | \"new\";\n /** Installed components that cite this one; empty for `new`. */\n requiredBy: string[];\n}\n\n/** A prerequisite `update` left alone, and why. It stays in the report. */\nexport interface LeftAlone {\n name: string;\n kind: \"prerequisite\" | \"new\";\n reason: string;\n}\n\nexport interface UpdateInstallPlan {\n items: UpdateInstallItem[];\n leftAlone: LeftAlone[];\n}\n\n/** Newly available components `--add-new` or the checklist may offer: never `initOnly`. */\nexport function offerableNewComponents(\n newlyAvailable: NewlyAvailableComponent[],\n missingRequires: MissingRequirement[],\n): NewlyAvailableComponent[] {\n return newlyAvailable.filter(\n (c) => !getComponent(c.name).initOnly && !missingRequires.some((m) => m.name === c.name),\n );\n}\n\n/**\n * Which newly available components to install: all of them under `--add-new`,\n * the operator's ticks in a terminal, none otherwise (R-293b).\n */\nexport async function selectNewComponents(\n candidates: NewlyAvailableComponent[],\n mode: { addNew: boolean; interactive: boolean; dryRun: boolean },\n): Promise<NewlyAvailableComponent[]> {\n if (candidates.length === 0) return [];\n if (mode.addNew) return candidates;\n if (!mode.interactive || mode.dryRun) return [];\n const chosen = await checkbox<string>({\n message: \"Newly available components. Space to select, Enter to confirm (none selected installs nothing):\",\n choices: candidates.map((c) => ({ name: `${c.name} — ${c.description}`, value: c.name, checked: false })),\n });\n return candidates.filter((c) => chosen.includes(c.name));\n}\n\n/**\n * Decides, from the disk, what an update run would install. The dry run\n * prints this plan and the real run executes it, so the two cannot disagree.\n */\nexport async function planUpdateInstalls(args: {\n targetDir: string;\n assetsDir: string;\n spellScope: InstallScope;\n missingRequires: MissingRequirement[];\n selectedNew: NewlyAvailableComponent[];\n}): Promise<UpdateInstallPlan> {\n const items: UpdateInstallItem[] = [];\n const leftAlone: LeftAlone[] = [];\n\n for (const { name, requiredBy } of args.missingRequires) {\n const component = componentForSpellScope(getComponent(name), args.spellScope);\n if (component.initOnly) {\n leftAlone.push({ name, kind: \"prerequisite\", reason: \"it is an initOnly component: adding it mid-life changes how Git treats existing files, so it is your call\" });\n continue;\n }\n if (component.files.length === 0) continue;\n const plan = await planComponentInstall(component, args.targetDir, args.assetsDir, {});\n if (plan.conflicts.length > 0) {\n leftAlone.push({ name, kind: \"prerequisite\", reason: conflictReason(plan) });\n continue;\n }\n items.push({ plan, kind: \"prerequisite\", requiredBy });\n }\n\n for (const candidate of args.selectedNew) {\n const component = componentForSpellScope(getComponent(candidate.name), args.spellScope);\n if (component.initOnly || component.files.length === 0) continue;\n const plan = await planComponentInstall(component, args.targetDir, args.assetsDir, {});\n if (plan.conflicts.length > 0) {\n leftAlone.push({ name: candidate.name, kind: \"new\", reason: conflictReason(plan) });\n continue;\n }\n items.push({ plan, kind: \"new\", requiredBy: [] });\n }\n\n return { items, leftAlone };\n}\n\nfunction conflictReason(plan: InstallPlan): string {\n const first = plan.conflicts[0]!;\n if (first.state === \"directory\") return `${first.file} is a directory, which update does not replace`;\n const more = plan.conflicts.length > 1 ? ` (and ${plan.conflicts.length - 1} more)` : \"\";\n return `${first.file} already exists${more}, and update does not overwrite`;\n}\n\nfunction label(item: UpdateInstallItem): string {\n return item.kind === \"prerequisite\"\n ? `prerequisite of ${item.requiredBy.join(\", \")}`\n : \"newly available\";\n}\n\n/** The `--dry-run` lines: exactly the components a real run would install. */\nexport function describeInstallPlan(plan: UpdateInstallPlan): string[] {\n const lines: string[] = [];\n for (const item of plan.items) {\n lines.push(` [dry-run] Would install: ${item.plan.component.name} (${label(item)}) — ${item.plan.toCopy.length} files`);\n for (const f of item.plan.kept) lines.push(` [dry-run] Would keep: ${f.file} (exists, yours)`);\n }\n return lines;\n}\n\n/** Writes the plan and returns the manifest entries it produced. */\nexport async function executeUpdateInstalls(\n plan: UpdateInstallPlan,\n targetDir: string,\n packageVersion: string,\n): Promise<{ installed: InstalledComponent[]; lines: string[] }> {\n const installed: InstalledComponent[] = [];\n const lines: string[] = [];\n for (const item of plan.items) {\n const { files, fileHashes } = await executeInstallPlan(item.plan, targetDir);\n installed.push({\n name: item.plan.component.name,\n files,\n installedVersion: packageVersion,\n fileHashes,\n });\n lines.push(` Installed: ${item.plan.component.name} (${label(item)}) — ${files.length} files`);\n for (const f of item.plan.kept) lines.push(` Kept existing (yours): ${f.file}`);\n }\n return { installed, lines };\n}\n","/**\n * Fetches one file's content from a specific PUBLISHED version of this\n * package (ARC-038 decision 1) -- the merge base for `spell update`'s\n * content-preserving update, without vendoring a second copy of every past\n * release: npm's own registry already permanently retains every published\n * tarball.\n *\n * npm's registry API has no \"browse one file from one version\" endpoint, and\n * hand-rolling tarball extraction for a read-one-file use case is a\n * correctness risk this project has no existing dependency to cover safely.\n * unpkg.com serves directly from that same registry tarball, so this reaches\n * the identical permanent history the ADR describes, through a widely-used,\n * purpose-built CDN rather than new vendoring infrastructure. Resolved\n * in-implementation (ARC-038 left the exact fetch mechanism open); no local\n * caching, also resolved in-implementation -- within one `spell update` run\n * each (version, file) pair is fetched at most once already, and this\n * command runs rarely enough that cross-run caching is not worth the extra\n * invalidation surface.\n */\nconst NPM_PACKAGE_NAME = \"arcane-cli\";\n\n/**\n * Returns the file's text content, or `undefined` if it could not be\n * fetched (network failure, non-2xx response, or any other error) --\n * callers must treat `undefined` as \"cannot verify what changed,\" never as\n * \"no prior content,\" since silently treating a fetch failure as an empty\n * base would make the merge invent a huge fake diff instead of correctly\n * refusing to guess.\n */\nexport async function fetchPublishedFile(\n version: string,\n relativeAssetPath: string,\n): Promise<string | undefined> {\n const normalized = relativeAssetPath.replace(/\\\\/g, \"/\").replace(/^\\/+/, \"\");\n const url = `https://unpkg.com/${NPM_PACKAGE_NAME}@${version}/dist/assets/${normalized}`;\n try {\n const response = await fetch(url);\n if (!response.ok) return undefined;\n return await response.text();\n } catch {\n return undefined;\n }\n}\n","import { dirname } from \"node:path\";\nimport {\n readManifest,\n ManifestNotFoundError,\n} from \"../modules/manifest.js\";\nimport {\n checkForUpdate,\n getFeedUrl,\n} from \"../modules/version-check.js\";\nimport { generateVersionDriftDiagram } from \"../modules/diagram-generator.js\";\nimport {\n CLAUDE_PRECEDENCE_NOTE,\n FANOUT_CLIENT_LABELS,\n effectiveSpellScope,\n VSCODE_USER_TIER_NOTE,\n inspectUserTierFanout,\n misplacedUserManifestMessage,\n resolveInstallScope,\n userTierRoot,\n} from \"../modules/user-tier.js\";\nimport type { InstallScope } from \"../types.js\";\n\n/**\n * Runs the `spell status` command.\n *\n * Reads .arcane.json, checks for an available update, then prints\n * a formatted table of installed components + a version footer.\n *\n * @param targetDir Directory containing the Arcane installation (the\n * `~/.arcane` store when `options.user` is set)\n * @param packageVersion Current package version string\n * @param options.user Report on the per-user tier instead of a repository (CS-04)\n */\nexport async function runStatus(\n targetDir: string,\n packageVersion: string,\n options: { user?: boolean } = {},\n): Promise<void> {\n // Read existing manifest\n let manifest;\n try {\n manifest = await readManifest(targetDir);\n } catch (err) {\n if (err instanceof ManifestNotFoundError) {\n console.error(\n `Not initialized. Run \"spell init${options.user ? \" --user\" : \"\"}\" first.`,\n );\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n throw err;\n }\n\n const resolvedScope = resolveInstallScope(targetDir, manifest.scope, options.user);\n if (resolvedScope.misplacedUserManifest) {\n console.error(misplacedUserManifestMessage(targetDir));\n process.exit(1);\n return;\n }\n const scope: InstallScope = resolvedScope.scope;\n\n if (manifest.components.length === 0) {\n console.log(\"No components installed.\");\n return;\n }\n\n // Check for update (never throws)\n let feedUrl: string;\n try {\n feedUrl = getFeedUrl();\n } catch {\n feedUrl = \"\";\n }\n\n const versionResult = feedUrl\n ? await checkForUpdate(packageVersion, feedUrl)\n : { current: packageVersion, latest: null, updateAvailable: false, error: \"Feed URL unavailable\" };\n\n // ─── Print component table ─────────────────────────────────────────────────\n\n // Compute column widths\n const nameWidth = Math.max(\n \"Component\".length,\n ...manifest.components.map((c) => c.name.length),\n );\n const filesWidth = \"Files\".length;\n const versionWidth = Math.max(\n \"Version\".length,\n ...manifest.components.map((c) => c.installedVersion.length),\n );\n\n const pad = (s: string, n: number) => s.padEnd(n);\n const rpad = (s: string, n: number) => s.padStart(n);\n\n console.log(\n ` ${pad(\"Component\", nameWidth)} ${rpad(\"Files\", filesWidth)} ${pad(\"Version\", versionWidth)}`,\n );\n console.log(\n ` ${\"-\".repeat(nameWidth)} ${\"-\".repeat(filesWidth)} ${\"-\".repeat(versionWidth)}`,\n );\n\n for (const component of manifest.components) {\n const label = component.name;\n const fileCount = component.files.length.toString();\n const displayVersion = component.installedVersion;\n console.log(\n ` ${pad(label, nameWidth)} ${rpad(fileCount, filesWidth)} ${displayVersion}`,\n );\n }\n\n // ─── Scope (ARC-045 decision 3 / CS-04) ────────────────────────────────────\n // Which tier this manifest is, and -- for the user tier -- the state of the\n // client files it fanned out beyond the store; for a repository, whether a\n // user tier exists on this machine at all, since Claude Code will prefer\n // its copy of a same-named command (D7 in the CS-04 architecture).\n\n console.log(\"\");\n if (scope === \"user\") {\n console.log(` Scope: user — ${targetDir}`);\n const health = await inspectUserTierFanout(dirname(targetDir), manifest.fanout);\n console.log(\n ` Client files: ${health.total} (${health.byClient.codex} ${FANOUT_CLIENT_LABELS.codex}, ${health.byClient.claude} ${FANOUT_CLIENT_LABELS.claude})`,\n );\n if (health.missing.length > 0 || health.customized.length > 0) {\n const parts: string[] = [];\n if (health.missing.length > 0) parts.push(`${health.missing.length} missing`);\n if (health.customized.length > 0) parts.push(`${health.customized.length} customized`);\n console.log(\n ` ! ${parts.join(\", \")} — \\`spell update --user\\` regenerates missing files; customized files are never overwritten.`,\n );\n }\n } else {\n // ARC-045 decision 4 / CS-05: a repository that opted out says so on\n // this line, because it explains why it carries no spell files of its\n // own -- and, when the store is gone, why no client can find a spell.\n const spellScope = effectiveSpellScope(manifest);\n console.log(spellScope === \"user\" ? \" Scope: repo (spells: user tier)\" : \" Scope: repo\");\n const store = userTierRoot();\n try {\n const userManifest = await readManifest(store);\n console.log(` User tier: v${userManifest.version} at ${store} (spell status --user)`);\n } catch {\n if (spellScope === \"user\") {\n console.log(\n ` ! User tier: not installed at ${store} — this repository expects it for every spell. Run \\`spell init --user\\` (\\`spell doctor\\` explains).`,\n );\n }\n // Otherwise: no user tier on this machine (or an unreadable one --\n // `spell doctor` reports that); nothing to add here.\n }\n }\n\n // ─── Version footer ────────────────────────────────────────────────────────\n\n console.log(\"\");\n\n const latestStr = versionResult.error\n ? \"unable to check\"\n : (versionResult.latest ?? manifest.version);\n\n let footer = ` Installed: ${packageVersion} Latest: ${latestStr}`;\n\n if (versionResult.updateAvailable) {\n footer += \" (spell update available)\";\n }\n\n console.log(footer);\n\n // ─── Version-drift diagram (ARC-036 R8) ────────────────────────────────────\n // Axis A (repo-files vs installed CLI) needs no network call and is always\n // checkable. Axis B (installed CLI vs npm-latest) needs versionResult.latest,\n // which is null when the npm check failed — skip the diagram entirely in\n // that case rather than showing a partial, potentially misleading picture;\n // the \"Latest: unable to check\" footer above already covers that failure.\n if (versionResult.latest !== null) {\n const diagram = generateVersionDriftDiagram(\n manifest.version,\n packageVersion,\n versionResult.latest,\n );\n\n if (diagram !== null) {\n console.log(\"\");\n if (process.stdout.isTTY) {\n // Interactive terminal: a fenced Mermaid block doesn't render here —\n // print the same three readings as plain aligned text instead.\n console.log(\" Version drift:\");\n console.log(` repo-files: ${manifest.version}`);\n console.log(` installed-cli: ${packageVersion}`);\n console.log(` latest (npm): ${versionResult.latest}`);\n } else {\n // Piped (file, another tool, a captured PR body): emit the real\n // diagram so the redirected output is directly usable.\n console.log(\"```mermaid\");\n console.log(diagram);\n console.log(\"```\");\n }\n }\n }\n\n if (scope === \"user\") {\n console.log(\"\");\n for (const line of VSCODE_USER_TIER_NOTE) console.log(` ${line}`);\n console.log(` ${CLAUDE_PRECEDENCE_NOTE}`);\n }\n}\n","import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { createRequire } from \"node:module\";\nimport type { VersionCheckResult } from \"../types.js\";\n\nconst require = createRequire(import.meta.url);\n\ninterface PackageJson {\n publishConfig?: { registry?: string };\n}\n\n/** Read the feed URL from the package's own publishConfig.registry — never hardcoded. */\nexport function getFeedUrl(): string {\n const pkg = require(\"../../package.json\") as PackageJson;\n const url = pkg.publishConfig?.registry;\n if (!url) {\n throw new Error(\n \"publishConfig.registry is not set in package.json. Cannot resolve npm feed URL.\",\n );\n }\n return url;\n}\n\nconst DEFAULT_CACHE_PATH = join(homedir(), \".arcane\", \"version-cache.json\");\nconst CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours\n\ninterface CacheEntry {\n latest: string;\n checkedAt: string;\n}\n\n/**\n * Reads the version cache file. Returns null if missing or malformed.\n */\nexport async function readCache(\n cachePath: string,\n): Promise<CacheEntry | null> {\n try {\n const content = await readFile(cachePath, \"utf-8\");\n const parsed = JSON.parse(content) as CacheEntry;\n if (typeof parsed.latest !== \"string\" || typeof parsed.checkedAt !== \"string\") {\n return null;\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\n/**\n * Writes the version cache file, creating parent directories as needed.\n */\nexport async function writeCache(\n cachePath: string,\n latest: string,\n): Promise<void> {\n await mkdir(dirname(cachePath), { recursive: true });\n const entry: CacheEntry = { latest, checkedAt: new Date().toISOString() };\n await writeFile(cachePath, JSON.stringify(entry, null, 2), \"utf-8\");\n}\n\n/**\n * Fetches the latest published version of arcane-cli from the public npm\n * registry. Returns null (does not throw) on any network failure.\n */\nexport async function getLatestVersion(feedUrl: string): Promise<string | null> {\n const normalizedBase = feedUrl.endsWith(\"/\") ? feedUrl : feedUrl + \"/\";\n const url = `${normalizedBase}arcane-cli`;\n try {\n const response = await fetch(url, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(5000),\n });\n if (!response.ok) return null;\n const body = await response.json() as { \"dist-tags\"?: { latest?: string } };\n return body[\"dist-tags\"]?.latest ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Checks for a newer version of the package, using a 24h file cache.\n * Never throws — errors are captured in VersionCheckResult.error.\n */\nexport async function checkForUpdate(\n currentVersion: string,\n feedUrl: string,\n cachePath: string = DEFAULT_CACHE_PATH,\n): Promise<VersionCheckResult> {\n try {\n let latest: string | null = null;\n\n const cached = await readCache(cachePath);\n if (cached) {\n const age = Date.now() - new Date(cached.checkedAt).getTime();\n if (age < CACHE_TTL_MS) {\n latest = cached.latest;\n }\n }\n\n if (latest === null) {\n latest = await getLatestVersion(feedUrl);\n if (latest !== null) {\n try {\n await writeCache(cachePath, latest);\n } catch {\n // Cache write failure is non-fatal\n }\n }\n }\n\n return {\n current: currentVersion,\n latest,\n updateAvailable: latest !== null && latest !== currentVersion,\n };\n } catch (err) {\n return {\n current: currentVersion,\n latest: null,\n updateAvailable: false,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n}\n","/**\n * Generates the canonical version-drift gitGraph diagram (ARC-036, R2) from\n * the three already-computed version readings. Pure — no I/O, no network\n * calls. Mirrors spell-open-session.prompt.md's \"Arcane version check\n * (two-axis)\" template exactly, so the CLI (`spell status`) and the prompt\n * produce the identical shape from the same three inputs.\n */\n\n/**\n * Returns the gitGraph body (no ```mermaid fence — the caller chooses\n * fencing per R5) for the three-reading version-drift diagram, or `null`\n * when all three readings match (the applicability guard — nothing to\n * visualize for a single, matching reading).\n *\n * One commit per *distinct* value among the three readings, in\n * repo-files -> installed-CLI -> npm-latest order; a consecutive repeated\n * value collapses into its neighbor's commit rather than repeating it.\n * Branches off after the commit for each axis that drifts.\n */\nexport function generateVersionDriftDiagram(\n repoFilesVersion: string,\n installedCliVersion: string,\n npmLatestVersion: string,\n): string | null {\n if (\n repoFilesVersion === installedCliVersion &&\n installedCliVersion === npmLatestVersion\n ) {\n return null;\n }\n\n const lines = [\"gitGraph\", ` commit id: \"${repoFilesVersion}\"`];\n let lastDistinctVersion = repoFilesVersion;\n\n if (installedCliVersion !== repoFilesVersion) {\n lines.push(\n \" branch repo-files\",\n \" checkout main\",\n ` commit id: \"${installedCliVersion}\"`,\n );\n lastDistinctVersion = installedCliVersion;\n }\n\n if (npmLatestVersion !== lastDistinctVersion) {\n lines.push(\n \" branch installed-cli\",\n \" checkout main\",\n ` commit id: \"${npmLatestVersion}\" tag: \"latest\"`,\n );\n } else {\n lines[lines.length - 1] += ' tag: \"latest\"';\n }\n\n return lines.join(\"\\n\");\n}\n","import { rm } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { confirm } from \"@inquirer/prompts\";\nimport { removeWithin } from \"../modules/copier.js\";\nimport {\n readManifest,\n ManifestNotFoundError,\n} from \"../modules/manifest.js\";\nimport { stripMarkerSection } from \"../modules/merger.js\";\nimport { isHookEnforced, blockedRemotes } from \"../modules/push-safety.js\";\nimport { removeSecretsPrecommitHook } from \"../modules/secrets-scan.js\";\nimport {\n USER_TIER_SPELLS_DIR,\n describeFanoutOutcomes,\n misplacedUserManifestMessage,\n removeDirectoryIfEmpty,\n resolveInstallScope,\n syncUserTierFanout,\n} from \"../modules/user-tier.js\";\nimport type { ArcaneManifest } from \"../types.js\";\n\n/**\n * Runs the `spell uninstall` command.\n *\n * Deletes every file tracked in .arcane.json then removes .arcane.json itself.\n * Prompts for confirmation unless --yes is passed.\n *\n * @param options.yes Skip confirmation prompt\n * @param options.dryRun Preview what would be removed without deleting files\n * @param options.user Uninstall the per-user tier at ~/.arcane instead (CS-04)\n * @param targetDir Directory containing the Arcane installation\n */\nexport async function runUninstall(\n options: { yes?: boolean; dryRun?: boolean; user?: boolean },\n targetDir: string,\n): Promise<void> {\n // Read existing manifest\n let manifest;\n try {\n manifest = await readManifest(targetDir);\n } catch (err) {\n if (err instanceof ManifestNotFoundError) {\n console.error(`Not initialized. Run \"spell init${options.user ? \" --user\" : \"\"}\" first.`);\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n throw err;\n }\n\n // ARC-045 decision 3 / CS-04: the user tier has no push policy, no agent\n // outputs, no marker sections and no hooks -- only the store and the client\n // files it fanned out, which come off under the same hash rule they went\n // on with.\n const resolvedScope = resolveInstallScope(targetDir, manifest.scope, options.user);\n if (resolvedScope.misplacedUserManifest) {\n console.error(misplacedUserManifestMessage(targetDir));\n process.exit(1);\n return;\n }\n if (resolvedScope.scope === \"user\") {\n await uninstallUserTier(options, targetDir, manifest);\n return;\n }\n\n // A push-blocked repository must not be uninstalled while the block stands.\n //\n // Uninstall deletes .arcane.json but has no idea about core.hooksPath or the\n // sentinel push URLs, so the repo was left unable to push while `spell\n // unblock-push` -- the only supported way out -- refused to run because the\n // manifest it reads was gone. The operator was told \"Uninstalled\" and handed\n // a repository whose recovery required hand-editing git config.\n //\n // Refusing is the right shape rather than silently unblocking: ARC-034 R5\n // makes lifting the block a deliberate, interactive, separately-confirmed act,\n // and quietly doing it as a side effect of `uninstall --yes` would hand any\n // script exactly the bypass that command exists to deny.\n // Keyed on what the repository ACTUALLY has, not only on what the manifest\n // declares. A manifest-only check is the same declaration-versus-enforcement\n // confusion this feature exists to remove, and it is reachable without anyone\n // hand-editing: a partially-failed `unblock-push` writes push_policy \"open\"\n // while remotes stay blocked, after which uninstall would strand the repo\n // exactly as before.\n const policy = manifest.push_policy ?? \"open\";\n const controlsInForce =\n (await isHookEnforced(targetDir)) || (await blockedRemotes(targetDir)).length > 0;\n if ((policy === \"blocked\" || controlsInForce) && !options.dryRun) {\n const why =\n policy === \"blocked\"\n ? `this repository's push_policy is \"blocked\"`\n : `this repository still has Arcane's push block in force (the manifest says \"${policy}\", ` +\n \"but the controls are actually installed)\";\n console.error(\n `Refusing to uninstall: ${why}.\\n` +\n \"Uninstalling now would delete .arcane.json while leaving the pre-push hook and the\\n\" +\n \"disabled push URLs in place — the repository could not push, and `spell unblock-push`\\n\" +\n \"would no longer recognise it.\\n\\n\" +\n \"Run `spell unblock-push` first (interactive), then uninstall.\",\n );\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n\n // Count total tracked files\n const allFiles = manifest.components.flatMap((c) => c.files);\n const totalFiles = allFiles.length;\n\n // Dry-run: print what would be removed without deleting\n if (options.dryRun) {\n for (const file of allFiles) {\n console.log(`[dry-run] Would remove: ${file}`);\n }\n console.log(`[dry-run] Would remove: .arcane.json`);\n console.log(`[dry-run] Would remove: .github/agents/ (directory)`);\n console.log(`[dry-run] Would remove: .arcane/agents/ (directory)`);\n console.log(`[dry-run] Would remove: .arcane/generated/ (directory)`);\n console.log(`[dry-run] Would remove: .arcane/agents.yaml`);\n console.log(`[dry-run] Would strip arcane sections from: CLAUDE.md, AGENTS.md, copilot-instructions.md`);\n console.log(`\\n[dry-run] ${totalFiles} file(s) + manifest + agent outputs would be removed.`);\n if (policy === \"blocked\" || controlsInForce) {\n console.log(\n `[dry-run] BUT the real run would refuse: Arcane's push block is in force. Run \\`spell unblock-push\\` first.`,\n );\n }\n return;\n }\n\n // Confirmation prompt (skipped with --yes)\n if (!options.yes) {\n const confirmed = await confirm({\n message: `This will remove ${totalFiles} files and .arcane.json. Continue?`,\n default: false,\n });\n\n if (!confirmed) {\n console.log(\"Uninstall cancelled.\");\n return;\n }\n }\n\n // Delete each file tracked in the manifest -- through the same traversal\n // guard every write path has: a manifest entry that resolves outside the\n // repository is named and skipped, never deleted.\n let removed = 0;\n for (const component of manifest.components) {\n for (const file of component.files) {\n try {\n await removeWithin(targetDir, file);\n removed++;\n } catch (err) {\n console.log(\n ` ! Skipped ${file}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n // Delete .arcane.json\n await rm(join(targetDir, \".arcane.json\"), { force: true });\n\n // ── Clean up agent sync outputs (not tracked in manifest) ──────────────\n // Directories created by agent init/sync\n const agentDirs = [\n \".github/agents\",\n \".arcane/agents\",\n \".arcane/generated\",\n ];\n for (const dir of agentDirs) {\n await rm(join(targetDir, dir), { recursive: true, force: true });\n }\n // Roster file\n await rm(join(targetDir, \".arcane/agents.yaml\"), { force: true });\n\n // Strip arcane marker sections from merged files (keeps user content intact)\n const markerFiles = [\n \"CLAUDE.md\",\n \"AGENTS.md\",\n \".github/copilot-instructions.md\",\n ];\n for (const file of markerFiles) {\n await stripMarkerSection(targetDir, file, { dryRun: false });\n }\n\n // Secrets pre-commit hook (ARC-037): installed unconditionally by `spell\n // init` regardless of push_policy, so -- unlike the push-safety pre-push\n // hook -- there is no push_policy invariant above already guaranteeing\n // nothing is left behind. Without this, the hook would keep running\n // `spell doctor --leaks` against a project that no longer has an\n // .arcane.json to configure it, since `spell` itself is a separate global\n // install this command never touches. Best-effort: a failure here is a\n // leftover local convenience hook, not a reason to report uninstall itself\n // as failed.\n try {\n await removeSecretsPrecommitHook(targetDir);\n } catch {\n // Non-fatal.\n }\n\n console.log(`\\n✓ Uninstalled — ${removed} files removed.`);\n}\n\n// ─── spell uninstall --user ───────────────────────────────────────────────────\n\n/**\n * Removes the user tier: every client file the store manifest recorded (only\n * where its content still matches what Arcane wrote -- an edited one is kept\n * and named, exactly as `update --user` treats it), then the store's spells\n * and manifest, then the store directories if they are empty. `~/.arcane`\n * itself is removed only when nothing else lives there (the version cache\n * and the org-token file share it).\n */\nasync function uninstallUserTier(\n options: { yes?: boolean; dryRun?: boolean },\n storeRoot: string,\n manifest: ArcaneManifest,\n): Promise<void> {\n const homeDir = dirname(storeRoot);\n const storeFiles = manifest.components.flatMap((c) => c.files);\n const recordedCount = Object.keys(manifest.fanout ?? {}).length;\n\n if (options.dryRun) {\n for (const file of storeFiles) console.log(`[dry-run] Would remove: ${storeRoot}/${file}`);\n const preview = await syncUserTierFanout({\n homeDir,\n storeRoot,\n spellIds: [],\n previous: manifest.fanout,\n dryRun: true,\n });\n for (const line of describeFanoutOutcomes(preview.outcomes, true)) console.log(line);\n console.log(`[dry-run] Would remove: ${storeRoot}/.arcane.json`);\n console.log(\n `\\n[dry-run] ${storeFiles.length} spell(s), up to ${recordedCount} client file(s) + manifest would be removed from the user tier.`,\n );\n return;\n }\n\n if (!options.yes) {\n const confirmed = await confirm({\n message: `This will remove ${storeFiles.length} spells, ${recordedCount} client files and .arcane.json from the user tier at ${storeRoot}. Continue?`,\n default: false,\n });\n if (!confirmed) {\n console.log(\"Uninstall cancelled.\");\n return;\n }\n }\n\n // Client files first (they reference the store), under the hash rule.\n const fanout = await syncUserTierFanout({\n homeDir,\n storeRoot,\n spellIds: [],\n previous: manifest.fanout,\n });\n\n let removed = 0;\n for (const file of storeFiles) {\n try {\n await removeWithin(storeRoot, file);\n removed++;\n } catch (err) {\n console.log(` ! Skipped ${file}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n await rm(join(storeRoot, \".arcane.json\"), { force: true });\n // The agent roster and its definition files, mirroring the repository\n // tier's own cleanup: they are written by `spell agents init --user` and\n // tracked by no component, so nothing else would ever remove them.\n await rm(join(storeRoot, \"agents\"), { recursive: true, force: true });\n await rm(join(storeRoot, \"agents.yaml\"), { force: true });\n await removeDirectoryIfEmpty(join(storeRoot, USER_TIER_SPELLS_DIR));\n await removeDirectoryIfEmpty(storeRoot);\n\n for (const line of describeFanoutOutcomes(fanout.outcomes)) console.log(` ${line}`);\n const pruned = fanout.outcomes.filter((o) => o.status === \"pruned\").length;\n const kept = fanout.outcomes.filter((o) => o.status === \"kept-edited\").length;\n console.log(\n `\\n✓ Uninstalled the user tier — ${removed} spells and ${pruned} client files removed${\n kept > 0 ? `; ${kept} edited client file(s) kept` : \"\"\n }.`,\n );\n}\n","import { access, readdir, readFile, mkdir, copyFile as fsCopyFile } from \"node:fs/promises\";\nimport { join, dirname } from \"node:path\";\nimport { INCIDENT_QUEUE } from \"../config/incidents.js\";\nimport type { Delegation, DelegationsFile } from \"../types.js\";\nimport { evaluateIncidentGate } from \"../modules/incident-gate.js\";\nimport { runGit } from \"../modules/git.js\";\nimport {\n readManifest,\n resolveSecretsScanExcludePrefixes,\n ManifestNotFoundError,\n} from \"../modules/manifest.js\";\nimport { rosterExists } from \"../modules/agent-loader.js\";\nimport {\n USER_TIER_SPELLS_DIR,\n effectiveSpellScope,\n inspectUserTierFanout,\n resolveHomeDir,\n userTierRoot,\n} from \"../modules/user-tier.js\";\nimport { scanRepository } from \"../modules/denylist-scan.js\";\nimport { SECRETS_RULES } from \"../modules/secrets-scan.js\";\nimport {\n isHookEnforced,\n listRemotes,\n undisabledRemotes,\n pushTargets,\n} from \"../modules/push-safety.js\";\nimport {\n parseGitHubRemote,\n parseAdoRemote,\n fetchGitHubRulesets,\n fetchAdoMergeTypePolicies,\n evaluateGitHubMergePolicy,\n evaluateAdoMergePolicy,\n} from \"../modules/platform-policy.js\";\nimport { scanGovernancePlaceholders, PLACEHOLDER_STANDARD_FILE } from \"../modules/placeholders.js\";\nimport { findMissingRequires, findNewlyAvailable } from \"../modules/registry-changes.js\";\nimport { LEGACY_COMPONENT_MIGRATIONS, getComponent } from \"../modules/registry.js\";\n\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ninterface CheckResult {\n name: string;\n passed: boolean;\n message: string;\n /** If false, this is a warning, not a blocker. Defaults to true (blocking). */\n blocking?: boolean;\n /**\n * The check could not run here (e.g. what it reads is not installed). Shown\n * as \"skip\" with its message; never affects the exit code. Set only with\n * `passed: true`.\n */\n skipped?: boolean;\n}\n\n// ─── Runtime validation helpers ───────────────────────────────────────────────\n// Every JSON file below is an external boundary (hand-editable, or written by\n// an older/foreign version of this tool) -- `JSON.parse` only proves \"valid\n// JSON\", never \"matches the shape this code assumes\". An unchecked `as` cast\n// on a non-object parse (e.g. a file containing the literal `null`) let a\n// later `.foo` access throw *outside* the surrounding try/catch, crashing\n// the whole `spell doctor` run instead of reporting one degraded check.\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n// ─── Individual checks ────────────────────────────────────────────────────────\n\nasync function checkNodeVersion(): Promise<CheckResult> {\n const name = \"Node.js version\";\n const raw = process.version; // e.g. \"v20.11.0\"\n const major = parseInt(raw.replace(/^v/, \"\").split(\".\")[0] ?? \"0\", 10);\n if (major >= 18) {\n return { name, passed: true, message: `${raw} (>=18 required)` };\n }\n return {\n name,\n passed: false,\n message: `${raw} — Node.js 18 or later is required`,\n };\n}\n\n/**\n * `homeDir` is resolved through `resolveHomeDir()` (os.homedir()), never from\n * `process.env.HOME` directly: HOME is routinely unset on Windows, where the\n * home directory is USERPROFILE. Reading it directly made both probe paths\n * resolve to a drive root on every ordinary Windows run, so the check fell\n * through to the `--version` probes below -- which had no timeout, and were\n * measured taking 25s+ each, twice per `spell doctor`.\n */\nexport async function checkVSCodeExtension(\n id: string,\n label: string,\n homeDir: string = resolveHomeDir(),\n): Promise<CheckResult> {\n const name = `VS Code extension: ${label}`;\n // Check extension directories on disk — more reliable than --list-extensions\n // in child process contexts (snap binaries may not output to non-TTY stdout).\n const flavours = [\n { dir: join(homeDir, \".vscode\", \"extensions\"), bin: \"code\" },\n { dir: join(homeDir, \".vscode-insiders\", \"extensions\"), bin: \"code-insiders\" },\n ];\n\n // Which binary to name in the remedy is decided from the extension\n // directories already being read, never by spawning the binaries to ask.\n // The old probe shelled out to `code --version` and then `code-insiders\n // --version` on every run that got this far -- measured at 2.5s apiece on a\n // developer machine where both answer normally, and unbounded when one does\n // not -- to choose one word in the message of a check that is already a\n // non-blocking warning. An existing extensions directory is the same\n // evidence at no cost.\n let installBin: string | null = null;\n\n for (const flavour of flavours) {\n let entries: string[];\n try {\n entries = await readdir(flavour.dir);\n } catch {\n continue; // That flavour of VS Code isn't installed for this user.\n }\n installBin ??= flavour.bin;\n\n const prefix = id.toLowerCase() + \"-\";\n const found = entries.some(\n (e) => e.toLowerCase() === id.toLowerCase() || e.toLowerCase().startsWith(prefix),\n );\n if (found) {\n return { name, passed: true, message: `${id} is installed` };\n }\n }\n\n return {\n name,\n passed: false,\n message: `${id} not found. Install via: ${installBin ?? \"code\"} --install-extension ${id}`,\n blocking: false,\n };\n}\n\nexport async function checkArcaneManifest(targetDir: string): Promise<CheckResult> {\n const name = \"Arcane manifest (.arcane.json)\";\n const manifestPath = join(targetDir, \".arcane.json\");\n try {\n await access(manifestPath);\n } catch {\n const selfHostedManifestPath = join(targetDir, \"src\", \"assets\", \".arcane.json\");\n try {\n const raw = await readFile(selfHostedManifestPath, \"utf8\");\n const manifest: unknown = JSON.parse(raw);\n if (\n isRecord(manifest) &&\n manifest[\"selfHosted\"] === true &&\n manifest[\"tracking_mode\"] === \"internal\"\n ) {\n return {\n name,\n passed: true,\n message: \"self-hosted source tree — committed source manifest is authoritative\",\n };\n }\n } catch {\n // No valid self-hosting marker — report the normal missing-manifest warning.\n }\n return {\n name,\n passed: false,\n message: \"No .arcane.json found. Run `spell init` to initialize Arcane in this project.\",\n blocking: false,\n };\n }\n\n try {\n const raw = await readFile(manifestPath, \"utf8\");\n const manifest: unknown = JSON.parse(raw);\n if (\n !isRecord(manifest) ||\n typeof manifest[\"version\"] !== \"string\" ||\n !manifest[\"version\"] ||\n !Array.isArray(manifest[\"components\"])\n ) {\n return {\n name,\n passed: false,\n message: \".arcane.json is present but missing required fields (version, components). Re-run `spell init`.\",\n };\n }\n return {\n name,\n passed: true,\n message: `v${manifest[\"version\"]} — ${manifest[\"components\"].length} component(s) installed`,\n };\n } catch {\n return {\n name,\n passed: false,\n message: \".arcane.json is present but contains invalid JSON. Re-run `spell init`.\",\n };\n }\n}\n\n/**\n * EF-32: independently verify the EFFECTIVE (not just repository-local)\n * pull.rebase resolves to \"true\", catching drift regardless of source --\n * an install that predates `spell init`'s own fix, a value unset again\n * later, or any other path that bypassed init's one-time correction.\n * Non-blocking: this repo has no CI job running inside the operator's own\n * working copy, so a doctor warning is the only enforcement mode available\n * here (see ARC-023's inline-enforcement-contract requirement).\n */\nexport async function checkPullRebase(targetDir: string): Promise<CheckResult> {\n const name = \"Git pull.rebase (rebase-and-fast-forward mandate)\";\n try {\n // --type=bool normalizes any of git's valid boolean spellings\n // (true/yes/on/1, false/no/off/0) to canonical \"true\"/\"false\" -- a raw\n // string comparison against \"true\" alone would false-positive-warn on\n // a fully compliant \"yes\"/\"on\"/\"1\".\n const { stdout } = await runGit(targetDir, [\"config\", \"--type=bool\", \"--get\", \"pull.rebase\"]);\n const value = stdout.trim();\n if (value === \"true\") {\n return { name, passed: true, message: \"pull.rebase=true\" };\n }\n return {\n name,\n passed: false,\n message: `pull.rebase resolves to \"${value}\", not \"true\" -- a bare \\`git pull\\` will create merge commits, contradicting git-conventions.md's rebase-and-fast-forward mandate. Run: git config --local pull.rebase true`,\n blocking: false,\n };\n } catch {\n // Two cases collapse here: unset entirely, or set to a non-boolean\n // value (e.g. \"merges\") --type=bool can't coerce -- both mean\n // \"not affirmatively true\", which is what this check cares about.\n return {\n name,\n passed: false,\n message: \"pull.rebase is unset, not a recognized boolean, or this isn't a Git repository -- effective behavior may not be rebase-and-fast-forward, and Git for Windows defaults this to false when unset. Run: git config --local pull.rebase true\",\n blocking: false,\n };\n }\n}\n\nexport function checkIncidentReleaseGate(): CheckResult {\n const result = evaluateIncidentGate(INCIDENT_QUEUE);\n if (result.blocked) {\n return {\n name: \"Incident release gate (ARC-024)\",\n passed: false,\n message: result.blockers.join(\"; \"),\n };\n }\n const accepted = result.acceptedRisks.length > 0\n ? `; ${result.acceptedRisks.length} explicitly deferred`\n : \"\";\n return {\n name: \"Incident release gate (ARC-024)\",\n passed: true,\n message: `${result.checked} incidents classified${accepted}`,\n };\n}\n\n/**\n * ARC-037 decision 7: `spell check-leaks` folded into `spell doctor` as an\n * on-demand mode (`--leaks`) instead of a standalone command -- one fewer\n * entry point for the same scan the build's own CI backstop already runs\n * (scripts/copy-assets.ts). Not part of the default battery below: a\n * full-repository walk is a different cost profile than doctor's other\n * checks, and (per decision 6) the real defense is the CI backstop --\n * this is a fast, opt-in, run-it-yourself pass, mirroring `spell ward`'s\n * own separateness from the default battery.\n */\nexport async function checkSecrets(targetDir: string): Promise<CheckResult> {\n const name = \"Secrets scan (ARC-037)\";\n const excludePrefixes = await resolveSecretsScanExcludePrefixes(targetDir);\n const findings = await scanRepository(targetDir, SECRETS_RULES, undefined, excludePrefixes);\n if (findings.length === 0) {\n return { name, passed: true, message: \"no credential-shaped patterns found\" };\n }\n\n const summary = findings.map((f) => `${f.file}:${f.line} [${f.rule}]`).join(\"; \");\n return {\n name,\n passed: false,\n message: `${findings.length} credential-shaped pattern(s) found: ${summary}`,\n };\n}\n\n// ─── Session continuity checks ────────────────────────────────────────────────\n\n/** Files required for spell-close-session / spell-open-session to function. */\nexport const SESSION_CONTINUITY_FILES = [\n \"TODO.md\",\n \"DECISIONS.md\",\n \"ai-context/system-prompt-context.md\",\n \"journal/.gitkeep\",\n];\n\nexport async function checkSessionContinuity(targetDir: string): Promise<CheckResult[]> {\n const results: CheckResult[] = [];\n\n for (const file of SESSION_CONTINUITY_FILES) {\n const name = `Session file: ${file}`;\n const filePath = join(targetDir, file);\n try {\n await access(filePath);\n results.push({ name, passed: true, message: \"present\" });\n } catch {\n results.push({\n name,\n passed: false,\n message: `Missing. Required for spell-close-session. Run \\`spell doctor --fix\\` to create it.`,\n blocking: false,\n });\n }\n }\n\n return results;\n}\n\nexport async function fixSessionContinuity(targetDir: string, assetsDir: string): Promise<string[]> {\n const created: string[] = [];\n\n for (const file of SESSION_CONTINUITY_FILES) {\n const destPath = join(targetDir, file);\n try {\n await access(destPath);\n // File exists — skip\n } catch {\n // File missing — create from template\n const srcPath = join(assetsDir, file);\n await mkdir(dirname(destPath), { recursive: true });\n await fsCopyFile(srcPath, destPath);\n created.push(file);\n }\n }\n\n return created;\n}\n\n// ─── Main ─────────────────────────────────────────────────────────────────────\n\nexport interface DoctorOptions {\n fix?: boolean;\n /** ARC-037 decision 7: run only the on-demand secrets scan, skipping the default battery. */\n leaks?: boolean;\n /** The running CLI's version, for the user-tier compatibility check (CS-04). Passed by the CLI entry. */\n packageVersion?: string;\n}\n\n/**\n * ARC-045 decision 3 / CS-04. Non-blocking, always: the user tier is\n * optional. Absent → pass with the pointer. Present → warn when the store's\n * `major.minor` is behind (or ahead of) this CLI, or when a client file the\n * store manifest recorded is gone -- both remedied by `spell update --user`.\n * A recorded file the operator edited is reported in the pass message, not\n * warned about: keeping it is the designed behavior, not a defect. Reads the\n * store manifest and file hashes only; VS Code's own settings are neither\n * read nor needed (the CS-04 architecture's finding 2).\n */\nexport async function checkUserTier(\n homeDir: string = resolveHomeDir(),\n cliVersion?: string,\n): Promise<CheckResult> {\n const name = \"User tier (ARC-045)\";\n const storeRoot = userTierRoot(homeDir);\n\n let manifest;\n try {\n manifest = await readManifest(storeRoot);\n } catch (err) {\n if (err instanceof ManifestNotFoundError) {\n return {\n name,\n passed: true,\n blocking: false,\n message: \"no user tier installed (optional — `spell init --user` shares every spell across the repositories on this machine)\",\n };\n }\n return {\n name,\n passed: false,\n blocking: false,\n message: `could not read ${storeRoot}/.arcane.json (${err instanceof Error ? err.message : String(err)}) — remove it and re-run \\`spell init --user\\``,\n };\n }\n\n // Everything below reads a file the operator (or an older/foreign tool)\n // may have shaped oddly. readManifest validates the enum fields, not\n // `version`; a check that throws here would take the whole `spell doctor`\n // run down with it, in every repository on the machine (review finding\n // F2), so every failure becomes one non-blocking row instead.\n try {\n if (typeof manifest.version !== \"string\" || manifest.version.length === 0) {\n return {\n name,\n passed: false,\n blocking: false,\n message: `${storeRoot}/.arcane.json has no version field — remove the store and re-run \\`spell init --user\\``,\n };\n }\n const majorMinor = (v: string): string => v.split(\".\").slice(0, 2).join(\".\");\n const problems: string[] = [];\n if (cliVersion !== undefined && majorMinor(manifest.version) !== majorMinor(cliVersion)) {\n problems.push(`installed at v${manifest.version}, this CLI is v${cliVersion}`);\n }\n const health = await inspectUserTierFanout(homeDir, manifest.fanout);\n if (health.missing.length > 0) {\n problems.push(`${health.missing.length} of ${health.total} client file(s) missing`);\n }\n\n if (problems.length > 0) {\n return {\n name,\n passed: false,\n blocking: false,\n message: `${problems.join(\"; \")} — run \\`spell update --user\\` (regenerates missing files; never overwrites edited ones)`,\n };\n }\n const customized = health.customized.length > 0 ? `, ${health.customized.length} customized` : \"\";\n return {\n name,\n passed: true,\n blocking: false,\n message: `v${manifest.version} at ${storeRoot} — ${health.total} client file(s) in place${customized}`,\n };\n } catch (err) {\n return {\n name,\n passed: false,\n blocking: false,\n message: `could not inspect the user tier at ${storeRoot} (${err instanceof Error ? err.message : String(err)}) — run \\`spell update --user\\`; if that fails too, remove the store and re-run \\`spell init --user\\``,\n };\n }\n}\n\n/**\n * EF-09 R4. Reports the repository's declared push posture and, for a\n * \"blocked\" repo, whether the controls are actually in place.\n *\n * Deliberately fires for every \"guarded\" repo REGARDLESS of remote state. An\n * earlier draft only reported when no remote was configured, which meant the\n * reminder went silent the instant any remote was added -- including a wrong\n * one -- making \"guarded\" behave identically to \"open\" exactly when it\n * mattered most.\n */\nexport async function checkPushPolicy(targetDir: string): Promise<CheckResult> {\n const name = \"Push policy (EF-09)\";\n let manifest;\n try {\n manifest = await readManifest(targetDir);\n } catch {\n return { name, passed: true, message: \"not initialized — nothing to check\" };\n }\n\n const policy = manifest.push_policy ?? \"open\";\n\n if (policy === \"open\") {\n return { name, passed: true, message: \"open — pushes allowed\" };\n }\n\n if (policy === \"guarded\") {\n // Name AND URL: the reminder's whole job is catching a wrong remote, and the\n // name alone reveals nothing about where it points.\n const targets = await pushTargets(targetDir);\n const where =\n targets.length === 0\n ? \"none configured\"\n : targets\n .map(({ remote, urls }) => `${remote} → ${urls.length > 0 ? urls.join(\", \") : \"unknown\"}`)\n .join(\"; \");\n return {\n name,\n passed: false,\n blocking: false,\n message: `guarded — no technical control installed. Push targets: ${where}. Confirm those are the remotes you intend before pushing.`,\n };\n }\n\n // blocked: verify the controls are genuinely IN FORCE, not merely declared.\n // Checking config alone was a declaration check wearing an enforcement\n // check's name -- deleting the hook file leaves core.hooksPath intact and\n // pushes succeed, while this reported the hook as present.\n const hookActive = await isHookEnforced(targetDir);\n const remotes = await listRemotes(targetDir);\n const open = await undisabledRemotes(targetDir);\n\n if (hookActive && open.length === 0) {\n if (remotes.length === 0) {\n // Don't claim a push URL is \"in place\" when there is no remote at all.\n // The hook is the only live control here, and it is the one a\n // `--no-verify` push walks straight past -- so say that plainly rather\n // than reporting full protection.\n return {\n name,\n passed: false,\n blocking: false,\n message:\n \"blocked — pre-push hook in place, but no remote is configured, so no push URL is disabled. If a remote is added later, run `spell doctor` again: a --no-verify push to it would succeed until the URL is disabled too.\",\n };\n }\n return {\n name,\n passed: true,\n message: `blocked — pre-push hook in place and push URL disabled for ${remotes.join(\", \")}`,\n };\n }\n\n const missing = [\n hookActive ? null : \"pre-push hook (config or hook file absent)\",\n open.length > 0 ? `push URL still live for: ${open.join(\", \")}` : null,\n ].filter(Boolean);\n\n return {\n name,\n passed: false,\n blocking: false,\n // ARC-049 decision 3: name the command that closes the gap.\n message: `declared \"blocked\" but not enforced — ${missing.join(\"; \")}. The manifest claims a protection this repository does not have. Run \\`spell block-push\\` to install the controls.`,\n };\n}\n\n/**\n * ARC-051 decision 4 (#277). Warns -- never fails -- on a `{UPPER_SNAKE}`\n * token in a `status: active` installed governance document that is neither\n * in spell-authoring-standards.md's runtime-placeholders list nor in a\n * `status: template` document. Every unknown token is named with its file.\n * Skipped when the standard (or its list) is not installed, since without\n * the list every token would read as unknown.\n */\nexport async function checkGovernancePlaceholders(targetDir: string): Promise<CheckResult> {\n const name = \"Governance placeholders (ARC-051)\";\n let scan;\n try {\n scan = await scanGovernancePlaceholders(join(targetDir, \".arcane\", \"governance\"));\n } catch (err) {\n return {\n name,\n passed: false,\n blocking: false,\n message: `could not scan .arcane/governance (${err instanceof Error ? err.message : String(err)})`,\n };\n }\n\n if (scan.status === \"skipped\") {\n return { name, passed: true, blocking: false, skipped: true, message: `skipped — ${scan.reason}` };\n }\n\n if (scan.findings.length === 0) {\n return {\n name,\n passed: true,\n blocking: false,\n message: `${scan.activeDocs.length} active doc(s) scanned, no unknown placeholders`,\n };\n }\n\n const listed = scan.findings.map(({ file, tokens }) => `.arcane/governance/${file}: ${tokens.join(\", \")}`);\n return {\n name,\n passed: false,\n blocking: false,\n message:\n `unknown placeholder(s) in status: active doc(s) — ${listed.join(\"; \")}. ` +\n `Fill each one in; or, if an agent resolves it at use time, add it to the runtime-placeholders list in ` +\n `.arcane/governance/${PLACEHOLDER_STANDARD_FILE}; or, if the document is meant to be filled in by hand, ` +\n \"mark it `status: template`.\",\n };\n}\n\ninterface McpServerConfig {\n timeout?: number;\n args?: unknown;\n}\n\n// A scope nobody on this project owns: the scaffold used to ship `@example/mcp-server`, which anyone\n// could publish (#295). A server whose args still name it runs a stranger's code on the next `npx -y`.\nconst UNOWNED_EXAMPLE_SCOPE = \"@example/\";\n\n// The placeholder the retired `mcp-config-template` scaffold shipped (1.10.0 through 1.10.2, #328).\n// npm rejects it, so the server fails on every session start of every client that loads the file.\nconst RETIRED_SCAFFOLD_PLACEHOLDER = \"<your-mcp-server-package>\";\n\ninterface McpConfigFile {\n mcpServers?: Record<string, McpServerConfig>;\n}\n\n/**\n * I12/BC-22. Checks that any configured .mcp.json server sets a per-server\n * \"timeout\" -- companion to the MCP fail-fast/fallback rule in\n * git-conventions.md. Without one, a hung server runs to the client's own\n * default idle limit (as long as 30 minutes) instead of a predictable one.\n * Also warns on the two placeholders the old scaffold shipped (#295, #328).\n * A missing .mcp.json is a silent pass -- MCP is optional. Advisory only:\n * the file is the operator's, and doctor never edits it.\n */\n/**\n * TODO.md \"a spell can be installed without the governance doc it cites\".\n * Warns -- never fails -- when an installed component's `requires` names a\n * component the manifest does not list; installs nothing (EF-17). `spell\n * update` is what installs them (ARC-052), and the message names it.\n */\nexport async function checkComponentRequires(targetDir: string): Promise<CheckResult> {\n const name = \"Component prerequisites\";\n let manifest;\n try {\n manifest = await readManifest(targetDir);\n } catch {\n // checkArcaneManifest reports a missing or unreadable manifest.\n return { name, passed: true, blocking: false, skipped: true, message: \"skipped — no readable .arcane.json\" };\n }\n if (manifest.scope === \"user\") {\n return { name, passed: true, blocking: false, skipped: true, message: \"skipped — the user tier holds spells only\" };\n }\n\n // A hand-edited manifest may lack components; checkArcaneManifest reports that.\n if (!Array.isArray(manifest.components)) {\n return { name, passed: true, blocking: false, skipped: true, message: \"skipped — .arcane.json lists no components\" };\n }\n const missing = findMissingRequires(manifest.components, manifest.profile);\n if (missing.length === 0) {\n return { name, passed: true, blocking: false, message: \"every installed component's prerequisites are installed\" };\n }\n return {\n name,\n passed: false,\n blocking: false,\n message: missing\n .map(({ name: required, requiredBy }) => `${requiredBy.join(\", \")} cites ${required}, which is not installed — ${getComponent(required).initOnly ? \"\" : \"`spell update` installs it, or \"}\\`spell add ${required}\\``)\n .join(\"; \"),\n };\n}\n\n/**\n * Names the components the repository's profile gained since it was installed\n * and the command that installs them (R-293b). Read-only and non-blocking:\n * doctor installs nothing (ARC-052). Prerequisites are left to\n * checkComponentRequires, which reports them.\n */\nexport async function checkNewlyAvailableComponents(targetDir: string): Promise<CheckResult> {\n const name = \"Newly available components\";\n let manifest;\n try {\n manifest = await readManifest(targetDir);\n } catch {\n return { name, passed: true, blocking: false, skipped: true, message: \"skipped — no readable .arcane.json\" };\n }\n if (manifest.scope === \"user\" || !Array.isArray(manifest.components)) {\n return { name, passed: true, blocking: false, skipped: true, message: \"skipped — no repository install to compare\" };\n }\n const installed = manifest.components.flatMap((c) =>\n (LEGACY_COMPONENT_MIGRATIONS[c.name] ?? [c.name]).map((n) => ({ ...c, name: n })),\n );\n const prerequisites = findMissingRequires(manifest.components, manifest.profile).map((m) => m.name);\n const fresh = (await findNewlyAvailable(targetDir, manifest.profile, installed, effectiveSpellScope(manifest)))\n .filter((c) => !prerequisites.includes(c.name));\n if (fresh.length === 0) {\n return { name, passed: true, blocking: false, message: \"the profile has nothing this install lacks\" };\n }\n const offerable = fresh.filter((c) => !getComponent(c.name).initOnly).map((c) => c.name);\n const byHand = fresh.filter((c) => getComponent(c.name).initOnly).map((c) => c.name);\n const parts: string[] = [];\n if (offerable.length > 0) {\n parts.push(`${offerable.join(\", \")} — \\`spell update --add-new\\` installs ${offerable.length === 1 ? \"it\" : \"them\"}`);\n }\n if (byHand.length > 0) {\n parts.push(`${byHand.join(\", \")} — initOnly, so only ${byHand.map((n) => `\\`spell add ${n}\\``).join(\", \")}`);\n }\n return { name, passed: false, blocking: false, message: `newly available for your profile, not installed: ${parts.join(\"; \")}` };\n}\n\nexport async function checkMcpConfig(targetDir: string): Promise<CheckResult> {\n const name = \"MCP config (per-server timeout, I12)\";\n const filePath = join(targetDir, \".mcp.json\");\n\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf8\");\n } catch {\n return { name, passed: true, blocking: false, message: \"no .mcp.json — nothing to check\" };\n }\n\n let rawParsed: unknown;\n try {\n rawParsed = JSON.parse(raw);\n } catch {\n return {\n name,\n passed: false,\n blocking: false,\n message: \".mcp.json exists but contains invalid JSON\",\n };\n }\n\n if (!isRecord(rawParsed)) {\n return {\n name,\n passed: false,\n blocking: false,\n message: \".mcp.json exists but is not a JSON object\",\n };\n }\n\n // Field-level shape (mcpServers, per-server timeout) is untrusted beyond\n // this point -- every access below is `?.`-guarded rather than assumed.\n const parsed = rawParsed as McpConfigFile;\n const servers = parsed.mcpServers ?? {};\n const serverNames = Object.keys(servers);\n if (serverNames.length === 0) {\n return { name, passed: true, blocking: false, message: \"no MCP servers configured\" };\n }\n\n const exampleHits: string[] = [];\n const placeholderHits: string[] = [];\n for (const n of serverNames) {\n const args = servers[n]?.args;\n if (!Array.isArray(args)) continue;\n for (const a of args) {\n if (typeof a !== \"string\") continue;\n if (a.startsWith(UNOWNED_EXAMPLE_SCOPE)) exampleHits.push(`${n} (${a})`);\n if (a === RETIRED_SCAFFOLD_PLACEHOLDER) placeholderHits.push(n);\n }\n }\n if (exampleHits.length > 0) {\n return {\n name,\n passed: false,\n blocking: false,\n message:\n `.mcp.json runs a package under the ${UNOWNED_EXAMPLE_SCOPE} scope nobody here owns: ${exampleHits.join(\", \")}. ` +\n \"The old `spell init` scaffold shipped it as a placeholder, and `npx -y` would run whoever publishes it. \" +\n \"Replace the entry with your own MCP server package, or remove it; the file is yours, so doctor does not edit it\",\n };\n }\n if (placeholderHits.length > 0) {\n return {\n name,\n passed: false,\n blocking: false,\n message:\n `.mcp.json still holds the placeholder server from the retired mcp-config-template scaffold: ${placeholderHits.join(\", \")} (${RETIRED_SCAFFOLD_PLACEHOLDER}). ` +\n \"Clients load this file as live MCP config, so the placeholder fails on every session start. \" +\n \"Delete the file if you run no MCP servers, or replace the entry with a real server package; the file is yours, so doctor does not edit it. \" +\n \"An untouched scaffold is removed by `spell update --prune`\",\n };\n }\n\n const missingTimeout = serverNames.filter((n) => typeof servers[n]?.timeout !== \"number\");\n if (missingTimeout.length > 0) {\n return {\n name,\n passed: false,\n blocking: false,\n message:\n `server(s) missing a \"timeout\": ${missingTimeout.join(\", \")} — add \"timeout\": 30000 (milliseconds; Claude Code ignores a value below 1000) ` +\n \"to each entry under mcpServers, so a stuck tool call ends after 30 s instead of running to the client's own idle limit (as long as 30 minutes)\",\n };\n }\n\n return {\n name,\n passed: true,\n blocking: false,\n message: `${serverNames.length} server(s) configured, all with a timeout set`,\n };\n}\n\nfunction isDelegation(value: unknown): value is Delegation {\n return (\n isRecord(value) &&\n typeof value[\"id\"] === \"string\" &&\n typeof value[\"status\"] === \"string\" &&\n typeof value[\"scope\"] === \"string\" &&\n Array.isArray(value[\"excludedActions\"])\n );\n}\n\nfunction isDelegationsFile(value: unknown): value is DelegationsFile {\n return isRecord(value) && Array.isArray(value[\"delegations\"]) && value[\"delegations\"].every(isDelegation);\n}\n\n/**\n * T13/BC-19. Lists standing-authority delegations recorded in\n * `.arcane/delegations.json` -- the solo-operator/no-roster counterpart to\n * agent-policies.md's roster-based power-level matrix. A missing file is a\n * silent pass, not a warning: a repo with no ad hoc grants has nothing to\n * report. This check only reports; it never grants, revokes, or validates\n * authority -- revocation is the git-native act of editing the file itself.\n */\nexport async function checkDelegations(targetDir: string): Promise<CheckResult> {\n const name = \"Standing delegations (T13)\";\n const filePath = join(targetDir, \".arcane\", \"delegations.json\");\n\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf8\");\n } catch {\n return { name, passed: true, blocking: false, message: \"no delegations recorded\" };\n }\n\n let rawParsed: unknown;\n try {\n rawParsed = JSON.parse(raw);\n } catch {\n return {\n name,\n passed: false,\n blocking: false,\n message: `.arcane/delegations.json exists but contains invalid JSON`,\n };\n }\n\n if (!isDelegationsFile(rawParsed)) {\n return {\n name,\n passed: false,\n blocking: false,\n message: `.arcane/delegations.json exists but is not a valid delegations file (expected a \"delegations\" array of well-formed entries)`,\n };\n }\n const parsed = rawParsed;\n\n const active = parsed.delegations.filter((d) => d.status === \"active\");\n if (active.length === 0) {\n return { name, passed: true, blocking: false, message: \"no active delegations\" };\n }\n\n const summary = active\n .map((d) => `${d.id} (scope: ${d.scope}; excludes: ${d.excludedActions.length} action(s))`)\n .join(\"; \");\n return {\n name,\n passed: true,\n blocking: false,\n message: `${active.length} active: ${summary}`,\n };\n}\n\n/**\n * T11/BC-17. Verifies the live platform branch/merge policy matches the\n * declared ladder (git-conventions.md: merge + rebase sanctioned, squash\n * never sanctioned). GitHub is checked via Rulesets, never the classic\n * `/branches/{branch}/protection` endpoint alone -- that endpoint 404s\n * \"not protected\" for a Rulesets-only repo (confirmed live against this\n * repo's own main branch), a false negative this check must not repeat.\n * Report-only: never mutates a platform setting. Degrades to a\n * non-blocking warning for any provider it can't resolve, any CLI it\n * can't reach, or any auth/network failure -- this check depends on\n * external state doctor's other checks never do, so it must never be the\n * reason `doctor` crashes.\n */\nexport async function checkPlatformBranchPolicy(targetDir: string): Promise<CheckResult> {\n const name = \"Platform branch/merge policy (T11)\";\n const targets = await pushTargets(targetDir);\n const origin = targets.find((t) => t.remote === \"origin\") ?? targets[0];\n const url = origin?.urls[0];\n\n if (!url) {\n return { name, passed: true, message: \"no remote configured — nothing to check\", blocking: false };\n }\n\n const github = parseGitHubRemote(url);\n if (github) {\n const rulesets = await fetchGitHubRulesets(github.owner, github.repo);\n if (rulesets === null) {\n return {\n name,\n passed: false,\n blocking: false,\n message: `could not query GitHub Rulesets for ${github.owner}/${github.repo} — is \\`gh\\` installed and authenticated? Verify manually: gh api repos/${github.owner}/${github.repo}/rulesets`,\n };\n }\n const result = evaluateGitHubMergePolicy(rulesets);\n return { name, passed: result.passed, blocking: false, message: result.message };\n }\n\n const ado = parseAdoRemote(url);\n if (ado) {\n const policies = await fetchAdoMergeTypePolicies(ado.org, ado.project, ado.repo, \"main\");\n if (policies === null) {\n return {\n name,\n passed: false,\n blocking: false,\n message: `could not query the Azure DevOps \"Limit merge types\" branch policy for ${ado.org}/${ado.project}/${ado.repo} — is \\`az\\` installed and authenticated? Verify manually in the ADO web UI under Branch Policies.`,\n };\n }\n const result = evaluateAdoMergePolicy(policies);\n return { name, passed: result.passed, blocking: false, message: result.message };\n }\n\n return {\n name,\n passed: true,\n blocking: false,\n message: \"remote is neither GitHub nor Azure DevOps — platform policy verification not available for this provider\",\n };\n}\n\n/**\n * ARC-045 decision 4 / CS-05. The one BLOCKING user-tier check, and it can\n * only fire in a repository that opted out on purpose: a repository whose\n * manifest says `spell_scope: \"user\"` has no spells of its own, so a missing\n * store leaves every client with nothing to run -- exactly the silently\n * unreachable state that decision exists to prevent. A repository that has\n * not opted in (the default, and every manifest written before 1.2.0) gets a\n * non-blocking pass, so the row never disappears without saying why.\n *\n * Complementary to checkUserTier above, not a duplicate: that one asks\n * whether the tier is healthy and is always advisory; this one asks whether\n * THIS repository depends on it.\n */\nexport async function checkSpellScope(\n targetDir: string,\n homeDir: string = resolveHomeDir(),\n): Promise<CheckResult> {\n const name = \"Spell scope (ARC-045)\";\n\n let manifest;\n try {\n manifest = await readManifest(targetDir);\n } catch {\n // No install here, or an unreadable manifest -- checkArcaneManifest\n // above is what reports that; nothing to add.\n return { name, passed: true, blocking: false, message: \"no .arcane.json — nothing to check\" };\n }\n\n if (effectiveSpellScope(manifest) === \"repo\") {\n return {\n name,\n passed: true,\n blocking: false,\n message: \"repo — this repository carries its own spells (the default)\",\n };\n }\n\n const storeRoot = userTierRoot(homeDir);\n let store;\n try {\n store = await readManifest(storeRoot);\n } catch (err) {\n return {\n name,\n passed: false,\n message:\n `this repository takes its spells from the user tier (spell_scope: \"user\"), but ${storeRoot} ` +\n `${err instanceof ManifestNotFoundError ? \"is not installed\" : `could not be read (${err instanceof Error ? err.message : String(err)})`}` +\n \" — no client can reach a spell here. Run `spell init --user`, or set spell_scope back to \\\"repo\\\" and run `spell update`.\",\n };\n }\n\n // Verified, not assumed: count the store's spells and probe a named one.\n let spellCount = 0;\n let probed = false;\n const probe = \"spell-status.md\";\n try {\n const entries = await readdir(join(storeRoot, USER_TIER_SPELLS_DIR));\n const spells = entries.filter((e) => e.startsWith(\"spell-\") && e.endsWith(\".md\"));\n spellCount = spells.length;\n probed = spells.includes(probe);\n } catch {\n // Left at 0/false -- reported as the empty-store failure below.\n }\n if (spellCount === 0 || !probed) {\n return {\n name,\n passed: false,\n message:\n `the user tier at ${storeRoot} holds ${spellCount} spell(s)` +\n `${spellCount > 0 && !probed ? ` and not ${probe}` : \"\"}` +\n \" — this repository expects it to supply every spell. Run `spell update --user`.\",\n };\n }\n\n const majorMinor = (v: string): string => v.split(\".\").slice(0, 2).join(\".\");\n if (typeof manifest.version === \"string\" && majorMinor(store.version) !== majorMinor(manifest.version)) {\n return {\n name,\n passed: false,\n blocking: false,\n message:\n `this repository is at v${manifest.version} but the user tier it uses is at v${store.version}` +\n \" — run `spell update --user` (and `spell update` here) to bring them together.\",\n };\n }\n\n // CS-06 / ARC-047 decision 9. An opted-out repository writes no\n // `.github/agents` files, so its agent modes can only come from the tier.\n // Failed only where agents are actually expected: a repository with its own\n // roster expects them, and one that has never run `spell agents init`\n // expects nothing and is not failed for having nothing.\n const repoExpectsAgents = await rosterExists(targetDir);\n if (repoExpectsAgents && !(await rosterExists(storeRoot, \"user\"))) {\n return {\n name,\n passed: false,\n message:\n `this repository has an agent roster and takes its client files from the user tier, but ` +\n `${storeRoot} has no roster of its own — no agent mode can reach a client here. ` +\n \"Run `spell agents init --user`, or set spell_scope back to \\\"repo\\\" and run `spell agents sync`.\",\n };\n }\n\n return {\n name,\n passed: true,\n blocking: false,\n message:\n `user — ${spellCount} spell(s) from ${storeRoot} (v${store.version}), ${probe} present` +\n `${repoExpectsAgents ? \", agent roster present in the tier\" : \"\"}`,\n };\n}\nexport async function runDoctor(targetDir: string, options: DoctorOptions = {}, assetsDir?: string): Promise<void> {\n console.log(\"\\nspell doctor — checking your Arcane environment\\n\");\n\n if (options.leaks) {\n const result = await checkSecrets(targetDir);\n const icon = result.passed ? \"✓\" : \"✗\";\n console.log(` ${icon} [${result.passed ? \"pass\" : \"FAIL\"}] ${result.name}`);\n if (!result.passed) {\n console.log(` ${result.message}`);\n console.log(\"\\n One or more checks failed. Fix the issues above before proceeding.\\n\");\n process.exitCode = 1;\n } else {\n console.log(\"\\n All checks passed. Your environment is ready.\\n\");\n }\n return;\n }\n\n const results = await Promise.all([\n checkNodeVersion(),\n checkVSCodeExtension(\"GitHub.copilot-chat\", \"GitHub Copilot (Chat)\", resolveHomeDir()),\n checkArcaneManifest(targetDir),\n Promise.resolve(checkIncidentReleaseGate()),\n checkPullRebase(targetDir),\n checkPushPolicy(targetDir),\n checkPlatformBranchPolicy(targetDir),\n checkDelegations(targetDir),\n checkMcpConfig(targetDir),\n checkUserTier(resolveHomeDir(), options.packageVersion),\n checkSpellScope(targetDir),\n checkGovernancePlaceholders(targetDir),\n checkComponentRequires(targetDir),\n checkNewlyAvailableComponents(targetDir),\n ]);\n\n // Add session continuity checks\n const sessionResults = await checkSessionContinuity(targetDir);\n results.push(...sessionResults);\n\n let allPassed = true;\n\n for (const result of results) {\n const skipped = result.passed && result.skipped === true;\n const icon = skipped ? \"–\" : result.passed ? \"✓\" : result.blocking === false ? \"⚠\" : \"✗\";\n const label = skipped\n ? \"skip\"\n : result.passed\n ? \"pass\"\n : result.blocking === false\n ? \"warn\"\n : \"FAIL\";\n console.log(` ${icon} [${label}] ${result.name}`);\n if (!result.passed || skipped) {\n console.log(` ${result.message}`);\n }\n if (!result.passed && result.blocking !== false) {\n allPassed = false;\n }\n }\n\n console.log();\n\n if (allPassed) {\n console.log(\" All checks passed. Your environment is ready.\\n\");\n } else {\n console.log(\" One or more checks failed. Fix the issues above before proceeding.\\n\");\n process.exitCode = 1;\n }\n\n // --fix: remediate missing session continuity files (runs regardless of allPassed,\n // since session continuity checks are warnings, not blocking failures)\n const hasMissingSession = sessionResults.some((r) => !r.passed);\n if (options.fix && hasMissingSession && assetsDir) {\n console.log(\" 🔧 Fixing missing session continuity files...\\n\");\n const created = await fixSessionContinuity(targetDir, assetsDir);\n if (created.length > 0) {\n for (const file of created) {\n console.log(` ✓ Created: ${file}`);\n }\n console.log(`\\n ${created.length} file(s) created. Repo is now close-session-ready.\\n`);\n } else {\n console.log(\" No files needed creation (all present).\\n\");\n }\n } else if (hasMissingSession && !options.fix) {\n console.log(\" 💡 Run `spell doctor --fix` to create missing session files.\\n\");\n }\n}\n","export type IncidentSeverity = \"critical\" | \"high\" | \"medium\" | \"low\";\nexport type IncidentImpact = \"data-integrity\" | \"security\" | \"other\";\nexport type IncidentConfidence = \"confirmed\" | \"research\";\nexport type IncidentState = \"open\" | \"resolved\" | \"deferred\";\n\nexport interface IncidentRecord {\n id: string;\n severity?: IncidentSeverity;\n impact: IncidentImpact;\n confidence: IncidentConfidence;\n state: IncidentState;\n detectedOn: string;\n resolution?: string;\n deferral?: string;\n}\n\nexport const INCIDENT_QUEUE: IncidentRecord[] = [\n { id: \"EF-05\", severity: \"medium\", impact: \"other\", confidence: \"confirmed\", state: \"open\", detectedOn: \"2026-07-31\" },\n { id: \"EF-08\", severity: \"medium\", impact: \"other\", confidence: \"confirmed\", state: \"open\", detectedOn: \"2026-07-31\" },\n { id: \"EF-16\", severity: \"high\", impact: \"other\", confidence: \"confirmed\", state: \"open\", detectedOn: \"2026-07-31\" },\n { id: \"EF-17\", severity: \"high\", impact: \"other\", confidence: \"confirmed\", state: \"open\", detectedOn: \"2026-07-31\" },\n { id: \"EF-20\", severity: \"high\", impact: \"other\", confidence: \"research\", state: \"open\", detectedOn: \"2026-07-31\" },\n { id: \"EF-21\", severity: \"high\", impact: \"other\", confidence: \"confirmed\", state: \"open\", detectedOn: \"2026-07-31\" },\n { id: \"EF-23\", severity: \"medium\", impact: \"other\", confidence: \"confirmed\", state: \"open\", detectedOn: \"2026-07-31\" },\n { id: \"EF-25\", severity: \"high\", impact: \"data-integrity\", confidence: \"confirmed\", state: \"resolved\", detectedOn: \"2026-07-14\", resolution: \"PRs 13 and 14\" },\n { id: \"EF-26\", severity: \"high\", impact: \"security\", confidence: \"confirmed\", state: \"resolved\", detectedOn: \"2026-07-31\", resolution: \"PR 15\" },\n { id: \"EF-27\", severity: \"high\", impact: \"security\", confidence: \"confirmed\", state: \"resolved\", detectedOn: \"2026-07-31\", resolution: \"PR 16\" },\n { id: \"EF-28\", severity: \"high\", impact: \"security\", confidence: \"confirmed\", state: \"resolved\", detectedOn: \"2026-07-31\", resolution: \"PR 17\" },\n { id: \"EF-29\", severity: \"medium\", impact: \"other\", confidence: \"confirmed\", state: \"resolved\", detectedOn: \"2026-07-31\", resolution: \"PR 18\" },\n];","import type { IncidentRecord } from \"../config/incidents.js\";\n\nconst DEFERRAL_PATTERN = /^Deferred \\d{4}-\\d{2}-\\d{2} — known open, accepting the risk\\.$/;\nconst BLOCKING_SEVERITIES = new Set([\"critical\", \"high\"]);\nconst PROTECTED_IMPACTS = new Set([\"data-integrity\", \"security\"]);\n\nexport interface IncidentGateResult {\n blocked: boolean;\n blockers: string[];\n acceptedRisks: string[];\n checked: number;\n}\n\nexport function evaluateIncidentGate(records: IncidentRecord[]): IncidentGateResult {\n const blockers: string[] = [];\n const acceptedRisks: string[] = [];\n const seen = new Set<string>();\n\n for (const record of records) {\n if (seen.has(record.id)) {\n blockers.push(`${record.id}: duplicate incident record`);\n continue;\n }\n seen.add(record.id);\n\n if (record.confidence === \"research\") continue;\n if (!PROTECTED_IMPACTS.has(record.impact)) continue;\n\n if (!record.severity) {\n blockers.push(`${record.id}: confirmed ${record.impact} incident is missing severity`);\n continue;\n }\n if (!BLOCKING_SEVERITIES.has(record.severity)) continue;\n\n if (record.state === \"resolved\") {\n if (!record.resolution?.trim()) {\n blockers.push(`${record.id}: resolved incident is missing resolution evidence`);\n }\n continue;\n }\n\n if (record.state === \"deferred\") {\n if (!record.deferral || !DEFERRAL_PATTERN.test(record.deferral)) {\n blockers.push(\n `${record.id}: deferral must be exactly \"Deferred <date> — known open, accepting the risk.\"`,\n );\n } else {\n acceptedRisks.push(`${record.id}: ${record.deferral}`);\n }\n continue;\n }\n\n blockers.push(\n `${record.id}: unresolved ${record.severity} ${record.impact} incident blocks release`,\n );\n }\n\n return {\n blocked: blockers.length > 0,\n blockers,\n acceptedRisks,\n checked: records.length,\n };\n}","/**\n * Generic denylist scanning engine: word-boundary matching, tree-walking,\n * and dedup -- shared by `scripts/org-token-lint.ts` (Arcane's own private\n * build gate) and `spell ward` (BC-21, a shipped CLI command any consumer\n * repo can run). Extracted here because these functions were never actually\n * org-token-specific -- only the *resolution* of which tokens to scan for\n * (an env var, a CLI flag, a mandatory vendor list) differs per caller.\n */\n\nimport { readFile, readdir } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\n\nexport interface DenylistRule {\n label: string;\n pattern: RegExp;\n}\n\nexport interface DenylistFinding {\n file: string;\n line: number;\n rule: string;\n}\n\nexport function escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/** Word-boundary rules: a token must not be flanked by another word character. */\nexport function createDenylistRules(\n tokens: string[],\n labelPrefix: string,\n): DenylistRule[] {\n return tokens.map((token, index) => ({\n label: `${labelPrefix}-${index + 1}`,\n pattern: new RegExp(\n `(?:^|[^A-Za-z0-9])${escapeRegExp(token)}(?=$|[^A-Za-z0-9])`,\n \"i\",\n ),\n }));\n}\n\n/**\n * Scans one file for denylist rules. Documented {UPPER_SNAKE} placeholders\n * are stripped before matching so they never trip a rule.\n */\nexport async function scanFile(\n absolutePath: string,\n displayPath: string,\n rules: DenylistRule[],\n): Promise<DenylistFinding[]> {\n const findings: DenylistFinding[] = [];\n let content: string;\n try {\n content = await readFile(absolutePath, \"utf8\");\n } catch {\n return findings;\n }\n\n const lines = content.split(\"\\n\");\n for (let index = 0; index < lines.length; index++) {\n const searchable = lines[index]!.replace(/\\{[A-Z][A-Z0-9_]*\\}/g, \"\");\n for (const rule of rules) {\n if (rule.pattern.test(searchable)) {\n findings.push({ file: displayPath, line: index + 1, rule: rule.label });\n break;\n }\n }\n }\n\n return findings;\n}\n\n/**\n * Directories never worth scanning -- generated output, dependencies, VCS\n * metadata. \".claude\" specifically covers `.claude/worktrees/`: each entry\n * there is a full nested checkout of this same repository (a linked git\n * worktree), so anything real inside one is scanned again at the primary\n * checkout's own path -- walking it too only triples every finding without\n * surfacing anything new (confirmed empirically widening the ARC-037 scan\n * repository-wide: every one of a worktree's findings duplicated a root-path\n * finding byte-for-byte).\n */\nexport const SKIP_DIRECTORIES = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \"coverage\",\n \".vitest\",\n \".claude\",\n]);\n\nexport const DEFAULT_SCANNABLE_EXTENSIONS = [\".md\", \".ts\", \".js\", \".json\", \".yml\", \".yaml\"];\n\n/**\n * Recursively collects every scannable file under `root`, returning\n * [absolutePath, repoRelativePath] pairs. `extensions` defaults to text\n * formats; a caller scanning binary asset strings (ward) passes its own list.\n */\nexport async function collectScannableFiles(\n root: string,\n repoRoot: string = root,\n extensions: string[] = DEFAULT_SCANNABLE_EXTENSIONS,\n): Promise<Array<[string, string]>> {\n const collected: Array<[string, string]> = [];\n let entries;\n try {\n entries = await readdir(root, { withFileTypes: true });\n } catch {\n return collected;\n }\n\n for (const entry of entries) {\n const absolutePath = join(root, entry.name);\n if (entry.isDirectory()) {\n if (SKIP_DIRECTORIES.has(entry.name)) continue;\n collected.push(...(await collectScannableFiles(absolutePath, repoRoot, extensions)));\n } else if (extensions.some((ext) => entry.name.endsWith(ext))) {\n const displayPath = relative(repoRoot, absolutePath).replace(/\\\\/g, \"/\");\n collected.push([absolutePath, displayPath]);\n }\n }\n\n return collected;\n}\n\n/**\n * Scans an entire repository tree (text files by default) for denylist rules.\n * `excludePrefixes` drops any file whose repo-relative display path contains\n * one of the given literal substrings before it's ever read -- a scan blind\n * spot the caller opted into, not a post-hoc filter on findings (ARC-037).\n */\nexport async function scanRepository(\n repoRoot: string,\n rules: DenylistRule[],\n extensions?: string[],\n excludePrefixes: string[] = [],\n): Promise<DenylistFinding[]> {\n if (rules.length === 0) return [];\n\n const findings: DenylistFinding[] = [];\n for (const [absolutePath, displayPath] of await collectScannableFiles(repoRoot, repoRoot, extensions)) {\n if (excludePrefixes.some((prefix) => displayPath.includes(prefix))) continue;\n findings.push(...(await scanFile(absolutePath, displayPath, rules)));\n }\n return findings;\n}\n\n/** Merges finding lists, collapsing duplicates reported at the same file:line. */\nexport function dedupeFindings(...lists: DenylistFinding[][]): DenylistFinding[] {\n const seen = new Map<string, DenylistFinding>();\n for (const list of lists) {\n for (const finding of list) {\n const key = `${finding.file}:${finding.line}`;\n if (!seen.has(key)) seen.set(key, finding);\n }\n }\n return [...seen.values()];\n}\n","import { execFile } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { win32 } from \"node:path\";\n\n/**\n * Budget for an external CLI that may reach the network (`gh api`, `az repos`).\n * Long enough for a real API round trip, short enough that an unauthenticated\n * or wedged CLI cannot hold a `spell doctor` run open indefinitely.\n */\nexport const EXTERNAL_CLI_TIMEOUT_MS = 10_000;\n\nexport interface ExecResult {\n stdout: string;\n stderr: string;\n}\n\n/**\n * Runs a child process under a hard timeout, with stdin closed.\n *\n * Mirrors the option set `runGit` already uses (src/modules/git.ts) minus\n * git's own environment, rather than inventing a second mechanism: Node's\n * native `timeout` instead of an AbortController, SIGTERM so a POSIX child\n * can still clean up after itself, `windowsHide` so no console window\n * flashes, and stdin ended immediately so a child that reads it gets EOF\n * instead of blocking on an open, unwritten pipe (EF-20).\n *\n * A timed-out child rejects with Node's own error carrying `killed: true`;\n * callers that only care whether a command answered can treat every rejection\n * the same way.\n *\n * The callers are the `gh` and `az` invocations behind `spell doctor`'s\n * platform-policy checks, which reach the network and previously had no\n * timeout at all -- an unauthenticated or wedged CLI could hold a doctor run\n * open with nothing to stop it.\n */\nexport function execFileWithTimeout(\n file: string,\n args: string[],\n timeoutMs: number,\n options: { env?: NodeJS.ProcessEnv } = {},\n): Promise<ExecResult> {\n return new Promise((resolve, reject) => {\n const child = execFile(\n file,\n args,\n {\n timeout: timeoutMs,\n killSignal: \"SIGTERM\",\n windowsHide: true,\n encoding: \"utf8\",\n ...(options.env ? { env: { ...process.env, ...options.env } } : {}),\n },\n (error, stdout, stderr) => {\n if (error) {\n reject(error);\n return;\n }\n resolve({ stdout, stderr });\n },\n );\n child.stdin?.end();\n });\n}\n\nexport interface AzureCliLaunch {\n file: string;\n prefixArgs: string[];\n env?: NodeJS.ProcessEnv;\n}\n\nexport interface AzureCliProbes {\n platform: NodeJS.Platform;\n pathEnv: string | undefined;\n readText(path: string): string | undefined;\n exists(path: string): boolean;\n}\n\nfunction defaultProbes(): AzureCliProbes {\n return {\n platform: process.platform,\n pathEnv: process.env[\"PATH\"] ?? process.env[\"Path\"],\n readText: (path) => {\n try {\n return readFileSync(path, \"utf8\");\n } catch {\n return undefined;\n }\n },\n exists: (path) => existsSync(path),\n };\n}\n\n// The one launcher line the Azure CLI MSI writes: its own interpreter, isolated, running its own module.\nconst MSI_LAUNCHER_LINE = /^\\s*\"%~dp0\\\\\\.\\.\\\\python\\.exe\"\\s+-IBm\\s+azure\\.cli\\s+%\\*\\s*$/im;\n\n/**\n * How to start the Azure CLI without any shell reading text that came from a git remote.\n *\n * Node cannot spawn `az.cmd` directly on Windows (EINVAL), and the usual cure, a shell, hands `&`, `|`, `\"`\n * and `%VAR%` in a remote's project or repository name to `cmd.exe`. So on Windows this reads the MSI\n * launcher, and only when it is exactly the recognised one, runs the interpreter it points at directly with\n * the same arguments. Anything else (no az.cmd, an unrecognised launcher, no interpreter) gets the plain\n * launch `az` by name, which is what runs an `az.exe` packaging and fails harmlessly otherwise. It never\n * falls back to a shell. Elsewhere `az` is a plain executable and is launched by name.\n */\nexport function resolveAzureCli(probes: AzureCliProbes = defaultProbes()): AzureCliLaunch {\n const plain: AzureCliLaunch = { file: \"az\", prefixArgs: [] };\n if (probes.platform !== \"win32\") return plain;\n\n for (const raw of (probes.pathEnv ?? \"\").split(\";\")) {\n const dir = raw.trim().replace(/^\"(.*)\"$/, \"$1\");\n if (!dir || !win32.isAbsolute(dir)) continue;\n const launcher = probes.readText(win32.join(dir, \"az.cmd\"));\n if (launcher === undefined) continue;\n if (!MSI_LAUNCHER_LINE.test(launcher)) return plain;\n const interpreter = win32.resolve(dir, \"..\", \"python.exe\");\n if (!probes.exists(interpreter)) return plain;\n return { file: interpreter, prefixArgs: [\"-IBm\", \"azure.cli\"], env: { AZ_INSTALLER: \"MSI\" } };\n }\n return plain;\n}\n","/**\n * Platform branch/merge policy verification (T11/BC-17).\n *\n * Report-only: nothing here mutates a repository or organization setting.\n * Rule-interpretation logic is split into pure functions taking\n * already-fetched JSON (evaluate*) so it is unit-testable with fixture\n * data, separate from the thin CLI-invocation wrappers (fetch*) that are\n * not exercised in CI (no live GitHub/ADO credentials to test against).\n *\n * GitHub Rulesets path is live-verified against this repo's own real\n * ruleset. The Azure DevOps path is implemented from documented API\n * shape only -- this repo has no ADO remote to test against -- and that\n * limitation is disclosed here rather than presented as verified.\n */\n\nimport {\n execFileWithTimeout,\n resolveAzureCli,\n EXTERNAL_CLI_TIMEOUT_MS,\n type AzureCliLaunch,\n} from \"./exec.js\";\n\n\n// ─── Declared ladder (git-conventions.md § Merge Strategy by Repo Risk) ──────\n\nexport type MergeMethod = \"merge\" | \"squash\" | \"rebase\";\nexport const SANCTIONED_MERGE_METHODS: readonly MergeMethod[] = [\"merge\", \"rebase\"];\n\nexport interface PolicyEvaluation {\n passed: boolean;\n /** null when no policy/ruleset was found to evaluate at all. */\n effectiveMergeMethods: MergeMethod[] | null;\n /** Methods the raw config nominally allows but a separate rule silently blocks. */\n declaredButBlocked: MergeMethod[];\n /** Methods effectively permitted despite never being sanctioned. */\n disallowedButPermitted: MergeMethod[];\n message: string;\n}\n\nfunction compareToLadder(\n effective: MergeMethod[],\n declaredButBlocked: MergeMethod[],\n interactionNote?: string,\n): PolicyEvaluation {\n const disallowedButPermitted = effective.filter(\n (m) => !SANCTIONED_MERGE_METHODS.includes(m),\n );\n const missingSanctioned = SANCTIONED_MERGE_METHODS.filter(\n (m) => !effective.includes(m),\n );\n const passed = disallowedButPermitted.length === 0 && missingSanctioned.length === 0;\n\n const messages: string[] = [];\n if (interactionNote) messages.push(interactionNote);\n if (disallowedButPermitted.length > 0) {\n messages.push(\n `disallowed method(s) effectively permitted: ${disallowedButPermitted.join(\", \")} (never sanctioned per git-conventions.md)`,\n );\n }\n if (missingSanctioned.length > 0) {\n messages.push(\n `sanctioned method(s) not effectively available: ${missingSanctioned.join(\", \")}`,\n );\n }\n\n return {\n passed,\n effectiveMergeMethods: effective,\n declaredButBlocked,\n disallowedButPermitted,\n message: passed\n ? `effective merge methods [${effective.join(\", \")}] match the declared ladder`\n : messages.join(\"; \"),\n };\n}\n\n// ─── GitHub Rulesets ──────────────────────────────────────────────────────────\n\nexport interface GitHubRulesetRule {\n type: string;\n parameters?: Record<string, unknown>;\n}\n\nexport interface GitHubRuleset {\n id: number;\n target: string;\n enforcement: string;\n rules: GitHubRulesetRule[];\n}\n\n/**\n * Pure evaluation: given already-fetched ruleset detail JSON for the rulesets\n * covering the target branch, determines the EFFECTIVE merge-method policy.\n *\n * Detects the interaction this repo's own 2026-08-24/25 incident found:\n * a `required_linear_history` rule silently blocks merge-commit PRs\n * regardless of what a `pull_request` rule's `allowed_merge_methods`\n * parameter declares.\n */\nexport function evaluateGitHubMergePolicy(\n rulesets: GitHubRuleset[],\n): PolicyEvaluation {\n const active = rulesets.filter(\n (r) => r.enforcement === \"active\" && r.target === \"branch\",\n );\n\n if (active.length === 0) {\n return {\n passed: false,\n effectiveMergeMethods: null,\n declaredButBlocked: [],\n disallowedButPermitted: [],\n message:\n \"No active branch ruleset covers the target branch (checked via Rulesets, not the classic protection endpoint, which false-negatives on Rulesets-only repos).\",\n };\n }\n\n // GitHub applies the most restrictive union when multiple rulesets target\n // the same ref -- intersect allowed_merge_methods across all of them.\n let allowedMergeMethods: MergeMethod[] | null = null;\n let hasRequiredLinearHistory = false;\n\n for (const ruleset of active) {\n for (const rule of ruleset.rules) {\n if (rule.type === \"required_linear_history\") {\n hasRequiredLinearHistory = true;\n }\n if (rule.type === \"pull_request\") {\n const methods = rule.parameters?.[\"allowed_merge_methods\"];\n if (Array.isArray(methods)) {\n const typed = methods as MergeMethod[];\n allowedMergeMethods =\n allowedMergeMethods === null\n ? typed\n : allowedMergeMethods.filter((m) => typed.includes(m));\n }\n }\n }\n }\n\n // No pull_request rule set allowed_merge_methods at all -- GitHub's\n // platform default (all three methods) applies, which is never compliant.\n const nominal = allowedMergeMethods ?? [\"merge\", \"squash\", \"rebase\"];\n const effective = hasRequiredLinearHistory\n ? nominal.filter((m) => m !== \"merge\")\n : nominal;\n const declaredButBlocked: MergeMethod[] =\n hasRequiredLinearHistory && nominal.includes(\"merge\") ? [\"merge\"] : [];\n\n const interactionNote =\n declaredButBlocked.length > 0\n ? `allowed_merge_methods declares [${nominal.join(\", \")}] but a required_linear_history rule silently blocks merge commits regardless -- effective methods are [${effective.join(\", \")}]`\n : undefined;\n\n return compareToLadder(effective, declaredButBlocked, interactionNote);\n}\n\nexport async function fetchGitHubRulesets(\n owner: string,\n repo: string,\n): Promise<GitHubRuleset[] | null> {\n try {\n const { stdout: listOut } = await execFileWithTimeout(\n \"gh\",\n [\"api\", `repos/${owner}/${repo}/rulesets`],\n EXTERNAL_CLI_TIMEOUT_MS,\n );\n const summaries = JSON.parse(listOut) as Array<{ id: number }>;\n const detailed = await Promise.all(\n summaries.map(async (s) => {\n const { stdout } = await execFileWithTimeout(\n \"gh\",\n [\"api\", `repos/${owner}/${repo}/rulesets/${s.id}`],\n EXTERNAL_CLI_TIMEOUT_MS,\n );\n return JSON.parse(stdout) as GitHubRuleset;\n }),\n );\n return detailed;\n } catch {\n return null;\n }\n}\n\n// ─── Azure DevOps branch policy ───────────────────────────────────────────────\n// NOT live-verified (see module doc comment and the PRD's Open Questions).\n\nexport interface AdoMergeTypePolicy {\n isEnabled: boolean;\n isBlocking: boolean;\n settings?: {\n useSquashMerge?: boolean;\n allowNoFastForward?: boolean;\n allowRebase?: boolean;\n allowRebaseMerge?: boolean;\n };\n}\n\nexport function evaluateAdoMergePolicy(\n policies: AdoMergeTypePolicy[],\n): PolicyEvaluation {\n const enabled = policies.filter((p) => p.isEnabled);\n if (enabled.length === 0) {\n return {\n passed: false,\n effectiveMergeMethods: null,\n declaredButBlocked: [],\n disallowedButPermitted: [],\n message: \"No enabled \\\"Limit merge types\\\" branch policy found for the target branch.\",\n };\n }\n\n const effective = new Set<MergeMethod>();\n for (const policy of enabled) {\n const s = policy.settings ?? {};\n if (s.allowNoFastForward) effective.add(\"merge\");\n if (s.allowRebase || s.allowRebaseMerge) effective.add(\"rebase\");\n if (s.useSquashMerge) effective.add(\"squash\");\n }\n\n return compareToLadder([...effective], []);\n}\n\nexport async function fetchAdoMergeTypePolicies(\n org: string,\n project: string,\n repoName: string,\n branch: string,\n launch: AzureCliLaunch = resolveAzureCli(),\n): Promise<AdoMergeTypePolicy[] | null> {\n const az = (args: string[]) =>\n execFileWithTimeout(launch.file, [...launch.prefixArgs, ...args], EXTERNAL_CLI_TIMEOUT_MS, {\n ...(launch.env ? { env: launch.env } : {}),\n });\n try {\n const orgUrl = `https://dev.azure.com/${org}`;\n // az repos policy list needs the repository's GUID, not its name.\n const { stdout: repoOut } = await az([\n \"repos\",\n \"show\",\n \"--repository\",\n repoName,\n \"--org\",\n orgUrl,\n \"--project\",\n project,\n \"--query\",\n \"id\",\n \"--output\",\n \"tsv\",\n ]);\n const repositoryId = repoOut.trim();\n if (!repositoryId) return null;\n\n const { stdout } = await az([\n \"repos\",\n \"policy\",\n \"list\",\n \"--repository-id\",\n repositoryId,\n \"--branch\",\n branch,\n \"--org\",\n orgUrl,\n \"--project\",\n project,\n \"--output\",\n \"json\",\n ]);\n return JSON.parse(stdout) as AdoMergeTypePolicy[];\n } catch {\n return null;\n }\n}\n\n// ─── Remote URL parsing ───────────────────────────────────────────────────────\n\nexport function parseGitHubRemote(url: string): { owner: string; repo: string } | null {\n const match = /github\\.com[/:]([^/]+)\\/([^/.]+?)(?:\\.git)?\\/?$/.exec(url);\n if (!match) return null;\n const [, owner, repo] = match;\n if (!owner || !repo) return null;\n return { owner, repo };\n}\n\n/**\n * One path segment of an Azure DevOps remote, as text. Git stores the remote percent-encoded\n * (`My%20Project`), and `az` wants the name itself. A malformed sequence makes the whole remote\n * unusable (`null`, never a throw), and so does a decoded segment beginning with `-`: the segments\n * become `az` arguments, and its parser would read such a value as an option rather than a name.\n * Decoding changes nothing about how they are launched: resolveAzureCli() never involves a shell, so\n * a decoded `&`, `|`, `\"` or `%` is a character in an argument and nothing more.\n */\nfunction decodeAdoSegment(raw: string): string | null {\n let decoded: string;\n try {\n decoded = decodeURIComponent(raw);\n } catch {\n return null;\n }\n if (!decoded || decoded.startsWith(\"-\")) return null;\n return decoded;\n}\n\nexport function parseAdoRemote(\n url: string,\n): { org: string; project: string; repo: string } | null {\n // Modern: https://dev.azure.com/{org}/{project}/_git/{repo}\n // Legacy: https://{org}.visualstudio.com/{project}/_git/{repo}\n const match =\n /dev\\.azure\\.com\\/([^/]+)\\/([^/]+)\\/_git\\/([^/?]+)/.exec(url) ??\n /([^./]+)\\.visualstudio\\.com\\/([^/]+)\\/_git\\/([^/?]+)/.exec(url);\n if (!match) return null;\n const [, rawOrg, rawProject, rawRepo] = match;\n if (!rawOrg || !rawProject || !rawRepo) return null;\n const org = decodeAdoSegment(rawOrg);\n const project = decodeAdoSegment(rawProject);\n const repo = decodeAdoSegment(rawRepo);\n if (org === null || project === null || repo === null) return null;\n return { org, project, repo };\n}\n","/**\n * ARC-051 — placeholder taxonomy for governance documents (#277).\n *\n * Governance documents use `{UPPER_SNAKE}` tokens in two ways:\n * - runtime-resolved tokens, which an agent resolves when it uses the\n * document (legal in a `status: active` document), enumerated once in\n * `spell-authoring-standards.md` between the runtime-placeholders markers;\n * - fill-in slots, which the operator replaces by hand. A document built\n * around them carries `status: template` and is not scanned.\n *\n * This module finds tokens in `status: active` documents that are in neither\n * category (\"unknown\"). It only reads files; `spell doctor` turns its result\n * into a non-blocking warning (ARC-051 decision 4).\n */\n\nimport { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\n/** The installed standard that holds the runtime-token list. */\nexport const PLACEHOLDER_STANDARD_FILE = \"spell-authoring-standards.md\";\nexport const RUNTIME_PLACEHOLDERS_START = \"<!-- runtime-placeholders:start -->\";\nexport const RUNTIME_PLACEHOLDERS_END = \"<!-- runtime-placeholders:end -->\";\n\n/** `{UPPER_SNAKE}`: an uppercase letter, then uppercase letters/digits in `_`-separated words. */\nconst PLACEHOLDER_PATTERN = /(?<!\\$)\\{([A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*)\\}/g;\n\nconst FRONTMATTER_PATTERN = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---[ \\t]*(?:\\r?\\n|$)/;\n\n/** The document's frontmatter `status:` value, or null when there is none. */\nexport function parseFrontmatterStatus(content: string): string | null {\n const block = FRONTMATTER_PATTERN.exec(content);\n if (!block) return null;\n const status = /^status:[ \\t]*[\"']?([^\"'\\s#]+)/m.exec(block[1]!);\n return status ? status[1]! : null;\n}\n\nfunction stripFrontmatter(content: string): string {\n const block = FRONTMATTER_PATTERN.exec(content);\n return block ? content.slice(block[0].length) : content;\n}\n\n/**\n * Drops fenced code blocks (``` or ~~~, opening fence indented at most three\n * spaces; an unclosed fence runs to the end of the document, as in\n * CommonMark). Indented code blocks are not recognized: in these documents a\n * four-space indent is almost always list continuation, not code.\n */\nfunction stripFencedCode(text: string): string {\n const kept: string[] = [];\n let fence: { char: string; length: number } | null = null;\n for (const line of text.split(/\\r?\\n/)) {\n if (fence) {\n const close = /^ {0,3}(`{3,}|~{3,})[ \\t]*$/.exec(line);\n if (close && close[1]![0] === fence.char && close[1]!.length >= fence.length) fence = null;\n kept.push(\"\");\n continue;\n }\n const open = /^ {0,3}(`{3,}|~{3,})/.exec(line);\n // A backtick fence's info string may not itself contain a backtick.\n if (open && !(open[1]![0] === \"`\" && line.slice(open[0].length).includes(\"`\"))) {\n fence = { char: open[1]![0]!, length: open[1]!.length };\n kept.push(\"\");\n continue;\n }\n kept.push(line);\n }\n return kept.join(\"\\n\");\n}\n\n/**\n * Drops inline code spans. A run of N backticks opens a span that closes at\n * the next run of exactly N backticks in the same paragraph; a run with no\n * match is literal text.\n */\nfunction stripInlineCode(text: string): string {\n return text\n .split(/\\n[ \\t]*\\n/)\n .map((paragraph) => {\n let out = \"\";\n let i = 0;\n while (i < paragraph.length) {\n if (paragraph[i] !== \"`\") {\n out += paragraph[i];\n i += 1;\n continue;\n }\n let runEnd = i;\n while (paragraph[runEnd] === \"`\") runEnd += 1;\n const run = runEnd - i;\n let close = -1;\n let j = runEnd;\n while (j < paragraph.length) {\n if (paragraph[j] !== \"`\") {\n j += 1;\n continue;\n }\n let k = j;\n while (paragraph[k] === \"`\") k += 1;\n if (k - j === run) {\n close = j;\n break;\n }\n j = k;\n }\n if (close === -1) {\n out += paragraph.slice(i, runEnd);\n i = runEnd;\n } else {\n out += \" \";\n i = close + run;\n }\n }\n return out;\n })\n .join(\"\\n\\n\");\n}\n\n/**\n * The distinct `{UPPER_SNAKE}` tokens in a document's prose, in first-seen\n * order. Frontmatter, fenced code and inline code are ignored: a token there\n * is an example or a literal, not a slot the reader is asked to fill.\n * `${NAME}` is shell/template syntax and is not a placeholder.\n */\nexport function findPlaceholderTokens(content: string): string[] {\n const prose = stripInlineCode(stripFencedCode(stripFrontmatter(content)));\n const seen = new Set<string>();\n for (const match of prose.matchAll(PLACEHOLDER_PATTERN)) seen.add(`{${match[1]!}}`);\n return [...seen];\n}\n\n/**\n * The runtime-resolved tokens listed between the runtime-placeholders markers:\n * each list item whose first element is a code span holding one token, e.g.\n * \"- `{ADO_ORG}` — …\". Returns null when the markers are absent or out of\n * order, so a caller can tell \"no list installed\" from \"an empty list\".\n */\nexport function parseRuntimePlaceholders(standardContent: string): Set<string> | null {\n const start = standardContent.indexOf(RUNTIME_PLACEHOLDERS_START);\n const end = standardContent.indexOf(RUNTIME_PLACEHOLDERS_END);\n if (start === -1 || end === -1 || end < start) return null;\n const region = standardContent.slice(start + RUNTIME_PLACEHOLDERS_START.length, end);\n const tokens = new Set<string>();\n for (const match of region.matchAll(/^[ \\t]*[-*][ \\t]+`(\\{[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*\\})`/gm)) {\n tokens.add(match[1]!);\n }\n return tokens;\n}\n\nexport interface PlaceholderFinding {\n /** File name relative to the scanned governance directory. */\n file: string;\n /** Unknown tokens, in first-seen order, braces included. */\n tokens: string[];\n}\n\nexport type PlaceholderScan =\n | { status: \"skipped\"; reason: string }\n | {\n status: \"scanned\";\n runtimeTokens: Set<string>;\n /** `status: active` documents scanned. */\n activeDocs: string[];\n /** `status: template` documents skipped (ARC-051 decision 3). */\n templateDocs: string[];\n /** One entry per active document with at least one unknown token. */\n findings: PlaceholderFinding[];\n };\n\n/**\n * Scans every `*.md` directly inside `governanceDir`. Only `status: active`\n * documents are checked; template, draft, deprecated and status-less documents\n * are not. Skipped (never failed) when the runtime-token list is not installed:\n * without it every token would read as unknown.\n */\nexport async function scanGovernancePlaceholders(governanceDir: string): Promise<PlaceholderScan> {\n let standard: string;\n try {\n standard = await readFile(join(governanceDir, PLACEHOLDER_STANDARD_FILE), \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return { status: \"skipped\", reason: `${PLACEHOLDER_STANDARD_FILE} is not installed` };\n }\n throw err;\n }\n const runtimeTokens = parseRuntimePlaceholders(standard);\n if (!runtimeTokens) {\n return {\n status: \"skipped\",\n reason: `${PLACEHOLDER_STANDARD_FILE} has no runtime-placeholders list (an older version?)`,\n };\n }\n\n const files = (await readdir(governanceDir, { withFileTypes: true }))\n .filter((entry) => entry.isFile() && entry.name.endsWith(\".md\"))\n .map((entry) => entry.name)\n .sort();\n\n const activeDocs: string[] = [];\n const templateDocs: string[] = [];\n const findings: PlaceholderFinding[] = [];\n for (const file of files) {\n const content = await readFile(join(governanceDir, file), \"utf8\");\n const status = parseFrontmatterStatus(content);\n if (status === \"template\") templateDocs.push(file);\n if (status !== \"active\") continue;\n activeDocs.push(file);\n const unknown = findPlaceholderTokens(content).filter((token) => !runtimeTokens.has(token));\n if (unknown.length > 0) findings.push({ file, tokens: unknown });\n }\n return { status: \"scanned\", runtimeTokens, activeDocs, templateDocs, findings };\n}\n","import { readFile } from \"node:fs/promises\";\nimport { runWard, type WardExclusion } from \"../modules/ward.js\";\n\nexport interface WardCliOptions {\n terms?: string;\n termsFile?: string;\n gate?: boolean;\n}\n\nfunction parseTerms(raw?: string): string[] {\n if (!raw) return [];\n return raw\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean);\n}\n\n/**\n * ARC-041: --terms-file escapes the shell-history exposure a --terms value\n * has on any multi-user or logged machine. Same delimiter convention as\n * resolvePrivateTokens() (comma and/or newline) -- one format across every\n * place this framework accepts a local denylist, nothing new to document.\n */\nfunction parseTermsFileContent(raw: string): string[] {\n return raw\n .split(/[,\\r\\n]+/)\n .map((t) => t.trim())\n .filter(Boolean);\n}\n\nasync function readTermsFile(filePath: string): Promise<string[]> {\n try {\n return parseTermsFileContent(await readFile(filePath, \"utf8\"));\n } catch (error: unknown) {\n console.warn(\n `⚠ arcane: could not read --terms-file \"${filePath}\": ${(error as Error).message}`,\n );\n return [];\n }\n}\n\n/**\n * spell ward CLI entry point. Report-only by default (matching\n * spell-check-drift's own convention); --gate exits non-zero on any `leak`\n * finding so it can be wired into CI. A `protected-vendor-identifier`\n * finding is informational -- it exists to make a human look before an\n * automated rename touches a real vendor identifier, not to fail a build by\n * itself.\n */\nexport async function runWardCli(\n targetDir: string,\n options: WardCliOptions = {},\n exclusions: WardExclusion[] = [],\n): Promise<void> {\n const terms = [\n ...parseTerms(options.terms),\n ...(options.termsFile ? await readTermsFile(options.termsFile) : []),\n ];\n const report = await runWard(targetDir, { terms, exclusions });\n\n console.log(\"\\nspell ward — scanning for leaked identifiers\\n\");\n\n const leaks = report.findings.filter((f) => f.category === \"leak\");\n const protectedHits = report.findings.filter((f) => f.category === \"protected-vendor-identifier\");\n\n if (leaks.length === 0 && protectedHits.length === 0) {\n console.log(\" ✓ No denylisted terms found.\");\n }\n\n for (const finding of leaks) {\n const where = finding.line === 0 ? `${finding.file} (filename)` : `${finding.file}:${finding.line}`;\n console.log(` ✗ [leak] ${where} — rule: ${finding.rule}`);\n }\n\n for (const finding of protectedHits) {\n const where = finding.line === 0 ? `${finding.file} (filename)` : `${finding.file}:${finding.line}`;\n console.log(\n ` ⚠ [protected-vendor-identifier] ${where} — rule: ${finding.rule} — verify before any automated rename touches this line`,\n );\n }\n\n if (report.mediaFlags.length > 0) {\n console.log(\"\\n Grep-proof media flagged for manual review:\");\n for (const flag of report.mediaFlags) {\n console.log(` ? ${flag.file} — ${flag.reason}`);\n }\n }\n\n console.log();\n\n if (options.gate && leaks.length > 0) {\n console.log(` ${leaks.length} leak(s) found. Failing (--gate mode).\\n`);\n process.exitCode = 1;\n return;\n }\n\n if (leaks.length > 0) {\n console.log(` ${leaks.length} leak(s) found. Run with --gate to fail CI on this.\\n`);\n }\n}\n","/**\n * spell ward (T18/BC-21): local IP/trademark leakage scan.\n *\n * Finds what leaked IN -- third-party identifiers, brand tokens, org names --\n * baked into a repo's content, filenames, or binary assets. `--terms` is a\n * generic denylist: seed it from the operator's OWN identity too, not just\n * the org's -- a username, machine name, or personal handle leaks the same\n * way a private venture name does (I1; the motivating incident was exactly\n * this -- a branch name containing a username, sitting in this repo's own\n * ai-context file, in a public repo). The opposite concern from spell scry\n * (T18), which clears a candidate name before it ships OUT.\n *\n * Deliberately out of scope: secret/credential detection. That is a\n * different scanning concern reserved for BC-10's secret-detection ADR\n * (ARC-037, still Proposed) -- ward never attempts pattern-matching on\n * keys, tokens, or credentials, only on identifiers and trademarks.\n */\n\nimport { readdir, readFile } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport {\n createDenylistRules,\n collectScannableFiles,\n SKIP_DIRECTORIES,\n type DenylistRule,\n type DenylistFinding,\n} from \"./denylist-scan.js\";\n\n/**\n * Ward's own content scan, deliberately NOT reusing denylist-scan.ts's\n * `scanFile` -- that function stops at the first matching rule per line\n * (correct for org-token-lint, which only needs \"does this line have any\n * hit\"), but ward must distinguish a leak from a protected-vendor-identifier\n * hit even when both land on the same line, so it needs every matching rule\n * per line, not just the first.\n */\nasync function scanFileAllRules(\n absolutePath: string,\n displayPath: string,\n rules: DenylistRule[],\n): Promise<DenylistFinding[]> {\n const findings: DenylistFinding[] = [];\n let content: string;\n try {\n content = await readFile(absolutePath, \"utf8\");\n } catch {\n return findings;\n }\n\n const lines = content.split(\"\\n\");\n for (let index = 0; index < lines.length; index++) {\n const searchable = lines[index]!.replace(/\\{[A-Z][A-Z0-9_]*\\}/g, \"\");\n for (const rule of rules) {\n if (rule.pattern.test(searchable)) {\n findings.push({ file: displayPath, line: index + 1, rule: rule.label });\n }\n }\n }\n\n return findings;\n}\n\n// ─── Mandatory vendor-identifier protection list ─────────────────────────────\n\n/**\n * Always active, independent of any user-supplied terms. A reasonable\n * starting set (not exhaustive -- see the PRD's Open Questions), covering\n * widely-referenced third-party AI model and vendor identifiers that an\n * automated rename/refactor pass could plausibly mistake for a project-local\n * token and \"correct\" into something else, corrupting a real API reference.\n * A match here is reported as PROTECTED, never as a plain leak finding --\n * ward's job for these terms is to stop automated correction, not to flag\n * their legitimate presence as a problem.\n */\nexport const VENDOR_IDENTIFIER_PROTECTION_LIST: readonly string[] = [\n \"claude\",\n \"gpt-4\",\n \"gpt-3.5\",\n \"gemini\",\n \"llama\",\n \"mistral\",\n \"copilot\",\n \"anthropic\",\n \"openai\",\n];\n\nexport interface WardExclusion {\n /** The denylist rule label this exclusion applies to. */\n rule: string;\n /** A substring that, if present on the same line as a match, suppresses it. */\n context: string;\n reason: string;\n}\n\nexport interface WardOptions {\n /** User-supplied terms, in addition to the mandatory vendor list. */\n terms?: string[];\n /** Substring-hazard exclusions (the \"author/provision\" class). */\n exclusions?: WardExclusion[];\n /** Extra file extensions to treat as scannable binary-asset carriers. */\n binaryExtensions?: string[];\n}\n\nexport interface WardFinding extends DenylistFinding {\n category: \"leak\" | \"protected-vendor-identifier\";\n}\n\nexport interface WardMediaFlag {\n file: string;\n reason: string;\n}\n\nexport interface WardReport {\n findings: WardFinding[];\n /** Grep-proof media flagged for manual review, never silently cleared. */\n mediaFlags: WardMediaFlag[];\n}\n\n/** Binary media formats where reliable text extraction isn't possible. */\nconst GREP_PROOF_EXTENSIONS = [\".gif\", \".png\", \".jpg\", \".jpeg\", \".mp4\", \".mov\", \".mp3\", \".wav\"];\n\n/** Extensions ward treats as text-scannable content, beyond denylist-scan's default set. */\nconst WARD_TEXT_EXTENSIONS = [\n \".md\", \".ts\", \".tsx\", \".js\", \".jsx\", \".json\", \".yml\", \".yaml\", \".txt\", \".mdx\",\n];\n\nfunction applyExclusions(\n findings: DenylistFinding[],\n fileLines: Map<string, string[]>,\n exclusions: WardExclusion[],\n): DenylistFinding[] {\n if (exclusions.length === 0) return findings;\n return findings.filter((finding) => {\n const applicable = exclusions.filter((e) => e.rule === finding.rule);\n if (applicable.length === 0) return true;\n const lines = fileLines.get(finding.file);\n const lineText = lines?.[finding.line - 1] ?? \"\";\n return !applicable.some((e) => lineText.includes(e.context));\n });\n}\n\n/** Scans filenames themselves (not just content) against the denylist rules. */\nasync function scanFilenames(\n root: string,\n rules: DenylistRule[],\n repoRoot: string = root,\n): Promise<DenylistFinding[]> {\n const findings: DenylistFinding[] = [];\n let entries;\n try {\n entries = await readdir(root, { withFileTypes: true });\n } catch {\n return findings;\n }\n\n for (const entry of entries) {\n const absolutePath = join(root, entry.name);\n if (entry.isDirectory()) {\n if (SKIP_DIRECTORIES.has(entry.name)) continue;\n findings.push(...(await scanFilenames(absolutePath, rules, repoRoot)));\n continue;\n }\n const displayPath = relative(repoRoot, absolutePath).replace(/\\\\/g, \"/\");\n for (const rule of rules) {\n if (rule.pattern.test(entry.name)) {\n findings.push({ file: displayPath, line: 0, rule: rule.label });\n break;\n }\n }\n }\n\n return findings;\n}\n\n/** Flags grep-proof media files for manual review -- never silently cleared. */\nasync function flagGrepProofMedia(root: string, repoRoot: string = root): Promise<WardMediaFlag[]> {\n const files = await collectScannableFiles(root, repoRoot, GREP_PROOF_EXTENSIONS);\n return files.map(([, displayPath]) => ({\n file: displayPath,\n reason: \"binary media format -- text extraction is unreliable; review manually for embedded identifiers\",\n }));\n}\n\n/**\n * Runs the full ward scan: content, filenames, and grep-proof media flags.\n * Report-only -- never mutates anything. Categorizes each finding as a plain\n * leak or a protected-vendor-identifier hit per VENDOR_IDENTIFIER_PROTECTION_LIST.\n */\nexport async function runWard(repoRoot: string, options: WardOptions = {}): Promise<WardReport> {\n const userTerms = options.terms ?? [];\n const exclusions = options.exclusions ?? [];\n const extensions = [...WARD_TEXT_EXTENSIONS, ...(options.binaryExtensions ?? [])];\n\n const userRules = createDenylistRules(userTerms, \"ward-term\");\n const vendorRules = createDenylistRules([...VENDOR_IDENTIFIER_PROTECTION_LIST], \"ward-vendor\");\n const allRules = [...userRules, ...vendorRules];\n\n const contentFiles = await collectScannableFiles(repoRoot, repoRoot, extensions);\n const fileLines = new Map<string, string[]>();\n const contentFindings: DenylistFinding[] = [];\n for (const [absolutePath, displayPath] of contentFiles) {\n const findings = await scanFileAllRules(absolutePath, displayPath, allRules);\n if (findings.length > 0) {\n try {\n fileLines.set(displayPath, (await readFile(absolutePath, \"utf8\")).split(\"\\n\"));\n } catch {\n // Unreadable after the initial scan (rare race) -- leave unmapped; exclusions simply won't match.\n }\n }\n contentFindings.push(...findings);\n }\n\n // Not dedupeFindings: that collapses on file:line alone, which would\n // wrongly merge a leak and a protected-vendor-identifier hit that land on\n // the same line -- exactly the case ward needs to keep distinct. Content\n // (line >= 1) and filename (line 0) findings can never collide on\n // file:line, and scanFileAllRules never double-reports the same rule on\n // the same line, so a plain concatenation is already duplicate-free.\n const filenameFindings = await scanFilenames(repoRoot, allRules);\n const combined = [...contentFindings, ...filenameFindings];\n const excluded = applyExclusions(combined, fileLines, exclusions);\n\n const vendorLabels = new Set(vendorRules.map((r) => r.label));\n const findings: WardFinding[] = excluded.map((f) => ({\n ...f,\n category: vendorLabels.has(f.rule) ? \"protected-vendor-identifier\" : \"leak\",\n }));\n\n const mediaFlags = await flagGrepProofMedia(repoRoot);\n\n return { findings, mediaFlags };\n}\n","import { basename, resolve } from \"node:path\";\nimport { input } from \"@inquirer/prompts\";\nimport { readManifest, writeManifest, ManifestNotFoundError } from \"../modules/manifest.js\";\nimport {\n removePrePushHook,\n restorePushUrls,\n blockedRemotes,\n isHookEnforced,\n} from \"../modules/push-safety.js\";\nimport { printInfo, printSuccess, printWarning } from \"../modules/banner.js\";\nimport type { ArcaneManifest } from \"../types.js\";\n\n/**\n * Runs `spell unblock-push` (EF-09 R5).\n *\n * Deliberately its own command and never a side effect of `init`/`update`:\n * a protection that can be lifted by a routine command is not a protection.\n *\n * Two gates, and the PRD is honest about what each one is worth:\n *\n * - **Interactive TTY only.** This is the real bar against a scripted agent —\n * the threat actor the PRD ranks second, since a push is irreversible. It is\n * not absolute (a pseudo-TTY defeats it), and the PRD says so rather than\n * claiming otherwise.\n * - **Typed repository name.** This guards a human against unblocking the wrong\n * repository. It is explicitly NOT agent-resistance: anything that can read\n * `.arcane.json` can read the name back.\n */\nexport async function runUnblockPush(targetDir: string): Promise<void> {\n let manifest: ArcaneManifest;\n try {\n manifest = await readManifest(targetDir);\n } catch (err) {\n if (err instanceof ManifestNotFoundError) {\n console.error('Not initialized. Run \"spell init\" first.');\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n throw err;\n }\n\n // Gate on the CONTROLS, not the declaration. A partial lift writes\n // push_policy \"open\" while the hook or a sentinel URL is still in force, and\n // keying the early return on the manifest meant that state made this command\n // refuse to run again — the failed attempt closed its own retry path. Making\n // partial lifts visible (0.20.1) is not the same as making them recoverable.\n const policy = manifest.push_policy ?? \"open\";\n const stillInForce =\n (await isHookEnforced(targetDir)) || (await blockedRemotes(targetDir)).length > 0;\n if (policy === \"open\" && !stillInForce) {\n printInfo(\"This repository is not push-blocked — nothing to undo.\");\n return;\n }\n if (policy === \"open\") {\n printWarning(\n \"The manifest says push_policy: open, but this repository still has Arcane's push block \" +\n \"in force — most likely a previous unblock only partly succeeded. Continuing so it can \" +\n \"be finished.\",\n );\n }\n\n if (!process.stdin.isTTY) {\n console.error(\n \"spell unblock-push must be run from an interactive terminal.\\n\" +\n \"Lifting a push block is a deliberate decision, so it cannot be scripted, piped, or run in CI.\",\n );\n process.exit(1);\n return;\n }\n\n const repoName = basename(resolve(targetDir));\n if (repoName === \"\") {\n // A repository at a filesystem root has no basename, which would turn the\n // confirmation into \"press Enter\". A gate that can silently become vacuous\n // must fail closed instead.\n console.error(\n \"Could not determine a repository name to confirm against (is this repository at a filesystem root?). Refusing to unblock.\",\n );\n process.exit(1);\n return;\n }\n\n printWarning(`This repository's push_policy is \"${policy}\".`);\n printInfo(\n \"Unblocking restores the ability to push this repository's full history to its remote. \" +\n \"Deleting a file later does not undo a push: the content stays in history, in any clone \" +\n \"already made, and in the remote's own backups.\",\n );\n console.log();\n\n const typed = await input({\n message: `Type the repository name to confirm (${repoName}):`,\n });\n\n if (typed.trim() !== repoName) {\n console.error(`\\nName did not match (\"${typed.trim()}\" ≠ \"${repoName}\"). Nothing changed.`);\n process.exit(1);\n return;\n }\n\n await removePrePushHook(targetDir);\n const results = await restorePushUrls(targetDir);\n const restored = results.filter((r) => r.status === \"restored\").map((r) => r.remote);\n const failed = results.filter((r) => r.status === \"failed\");\n // A remote still carrying the sentinel after we claimed to have unblocked it\n // is the manifest-lies-about-posture failure in the opposite direction, so it\n // is checked directly rather than inferred from what we think we did.\n const stillBlocked = await blockedRemotes(targetDir);\n\n // No \"just this once\" mode: the manifest must never claim a protection the\n // repository no longer has.\n const updated: ArcaneManifest = {\n ...manifest,\n push_policy: \"open\",\n push_policy_unblocked_at: new Date().toISOString(),\n };\n await writeManifest(targetDir, updated);\n\n // Check the hook too, not just the URLs: `removePrePushHook` declines to touch\n // a foreign or unreadable core.hooksPath, so it can legitimately do nothing.\n const hookStillOn = await isHookEnforced(targetDir);\n const fullyLifted = failed.length === 0 && stillBlocked.length === 0 && !hookStillOn;\n\n // Never let a partial result read as a clean one. Reporting success first and\n // appending warnings underneath is exactly the shape this feature refuses to\n // accept elsewhere.\n if (fullyLifted) {\n printSuccess(`Push unblocked for \"${repoName}\".`);\n } else {\n printWarning(\n `Push only PARTIALLY unblocked for \"${repoName}\". The manifest now says open; the ` +\n \"repository is not fully open yet. Details below.\",\n );\n }\n if (restored.length > 0) {\n printInfo(`Restored push URL for: ${restored.join(\", \")}.`);\n }\n if (hookStillOn) {\n printWarning(\n \"The pre-push hook is still in force. Arcane did not remove it because `core.hooksPath` is \" +\n \"not one it owns, or could not be read. Check `git config --show-scope --get core.hooksPath`.\",\n );\n }\n if (failed.length > 0) {\n printWarning(\n `Could not restore the push URL for: ${failed\n .map((r) => `${r.remote} (${r.reason ?? \"unknown error\"})`)\n .join(\"; \")}.`,\n );\n }\n if (stillBlocked.length > 0) {\n // Most likely cause: the remote was renamed while blocked, so the recorded\n // original moved with it but a *different* remote now carries the sentinel.\n printWarning(\n `These remotes still have a blocked push URL and no recorded original: ${stillBlocked.join(\", \")}. ` +\n \"Set them yourself with `git remote set-url --push <name> <url>` — until then, pushing to \" +\n \"them will fail even though the manifest now says open.\",\n );\n }\n printInfo(\n \"Recorded in .arcane.json as push_policy: open, with the time it was lifted. \" +\n \"Commit that change so the repository's actual posture stays visible to everyone.\",\n );\n}\n","import { readManifest, writeManifest, ManifestNotFoundError } from \"../modules/manifest.js\";\nimport { inspectGitRepository } from \"../modules/git.js\";\nimport {\n applyBlockedPushControls,\n hookInForce,\n isHookEnforced,\n listRemotes,\n undisabledRemotes,\n} from \"../modules/push-safety.js\";\nimport { printInfo, printSuccess, printWarning } from \"../modules/banner.js\";\nimport type { ArcaneManifest } from \"../types.js\";\n\n/**\n * Runs `spell block-push` (ARC-049 decision 1): enforces `push_policy:\n * \"blocked\"` on an already-initialized repository by installing exactly what\n * `spell init` installs for it, through the same helper and with the same\n * refusals.\n *\n * No TTY gate, unlike `spell unblock-push`: this can only tighten. The\n * manifest is changed to `blocked` only once both controls are in force, so\n * a refusal never leaves it claiming a protection the repository lacks.\n */\nexport async function runBlockPush(targetDir: string): Promise<void> {\n let manifest: ArcaneManifest;\n try {\n manifest = await readManifest(targetDir);\n } catch (err) {\n if (err instanceof ManifestNotFoundError) {\n console.error('Not initialized. Run \"spell init\" first.');\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n throw err;\n }\n\n if (manifest.scope === \"user\") {\n console.error(\n \"spell block-push applies to a repository, not the user tier: push_policy describes a repository.\",\n );\n process.exit(1);\n return;\n }\n\n if ((await inspectGitRepository(targetDir)).status === \"not-repository\") {\n console.error(\"spell block-push needs a Git repository: there is nothing here to block.\");\n process.exit(1);\n return;\n }\n\n const recorded = manifest.push_policy ?? \"open\";\n const alreadyEnforced =\n recorded === \"blocked\" &&\n (await isHookEnforced(targetDir)) &&\n (await undisabledRemotes(targetDir)).length === 0;\n if (alreadyEnforced) {\n printSuccess(\"Push is already blocked and enforced here. Nothing changed.\");\n if ((await listRemotes(targetDir)).length === 0) {\n printWarning(\n \"No remote is configured, so only the pre-push hook is active. Re-run `spell block-push` \" +\n \"after adding a remote to disable its push URL too.\",\n );\n }\n printInfo(\"Run `spell unblock-push` from a terminal to undo this.\");\n return;\n }\n\n const outcome = await applyBlockedPushControls(targetDir, {\n stopOnHookRefusal: true,\n emit: (m) => (m.level === \"warning\" ? printWarning(m.text) : printInfo(m.text)),\n });\n\n const failedRemotes = outcome.urls.filter((u) => u.status === \"failed\");\n if (!hookInForce(outcome.hook) || failedRemotes.length > 0) {\n printWarning(\n outcome.stoppedAtHook\n ? \"Nothing was changed. Resolve the refusal above and re-run `spell block-push`.\"\n : \"Push is NOT fully blocked: the remotes named above can still be pushed to. Fix them and \" +\n \"re-run `spell block-push`.\",\n );\n if (recorded !== \"blocked\") {\n printInfo(\n `The manifest still records push_policy: \"${recorded}\" — it is not changed to \"blocked\" ` +\n \"until both controls are in force.\",\n );\n }\n process.exit(1);\n return;\n }\n\n if (recorded !== \"blocked\") {\n await writeManifest(targetDir, { ...manifest, push_policy: \"blocked\" });\n printSuccess(`Push blocked. Recorded push_policy: \"blocked\" (was \"${recorded}\") in .arcane.json.`);\n printInfo(\"Commit that change so the repository's posture stays visible to everyone.\");\n } else {\n printSuccess('Push blocked. The manifest already recorded push_policy: \"blocked\"; it is now enforced.');\n }\n}\n","/**\n * Chunked credential store for `spell login` (features/spell-login/PRD.md, R4a).\n *\n * Windows Credential Manager refuses a secret longer than 1,280 characters\n * (measured through @napi-rs/keyring, 2026-10-05), and a real refresh token is\n * about 1,470, so one secret is split across several entries on every\n * platform -- one code path, not a Windows special case.\n *\n * Layout, inside one backend (an OS keychain in production, a fake in tests):\n *\n * session/index {\"v\":1,\"gen\":\"<hex>\",\"count\":N,\"sha256\":\"<hex>\"}\n * session/<gen>/<n> chunk n of the secret, at most CHUNK_SIZE chars\n *\n * Saving writes every chunk of a NEW generation first and the index LAST. The\n * index is the switch: until it is written the previous session is still the\n * one a read returns, so a crash at any point leaves the old session or the\n * new one, never a mixture. A read verifies the SHA-256 of the reassembled\n * secret; a missing chunk, a bad index or a mismatch reads as `corrupt`,\n * never as a partial secret.\n *\n * Nothing here ever puts a secret value, or a piece of one, in an error\n * message or a return value other than `load()`'s `secret` (R10).\n */\n\nimport { createHash, randomBytes } from \"node:crypto\";\n\n/** Characters per chunk. Well under the 1,280-character Windows limit. */\nexport const CHUNK_SIZE = 1000;\n\n/**\n * Most chunks one secret may use (64,000 characters). A corrupt index naming a\n * billion chunks must not turn a read into a billion keychain calls.\n */\nexport const MAX_CHUNKS = 64;\n\nconst INDEX_NAME = \"session/index\";\nconst CHUNK_PREFIX = \"session/\";\nconst INDEX_VERSION = 1;\n\n/**\n * A key-value secret store addressed by name. Names are the keychain's\n * \"account\" half; the service half belongs to the backend.\n */\nexport interface SecretBackend {\n get(name: string): string | null;\n set(name: string, value: string): void;\n delete(name: string): void;\n /** Every name currently stored, so stray chunks from a crashed save can be swept. */\n list(): string[];\n}\n\n/** The keychain could not be used (missing, locked, denied, or erroring). Maps to R6. */\nexport class KeychainUnavailableError extends Error {\n constructor(operation: string, cause: unknown) {\n // The cause's own message is kept out of ours: a backend error could echo a value.\n super(`The OS keychain is not available (${operation} failed).`);\n this.name = \"KeychainUnavailableError\";\n this.cause = cause;\n }\n}\n\nexport type LoadResult =\n | { state: \"signed-out\" }\n | { state: \"ok\"; secret: string }\n | { state: \"corrupt\"; reason: string };\n\ninterface Index {\n v: number;\n gen: string;\n count: number;\n sha256: string;\n}\n\nfunction sha256Hex(value: string): string {\n return createHash(\"sha256\").update(value, \"utf8\").digest(\"hex\");\n}\n\nfunction chunkName(gen: string, n: number): string {\n return `${CHUNK_PREFIX}${gen}/${n}`;\n}\n\n/** Cuts `secret` into pieces of at most `size` UTF-16 units, never inside a surrogate pair. */\nexport function splitSecret(secret: string, size = CHUNK_SIZE): string[] {\n const chunks: string[] = [];\n let start = 0;\n while (start < secret.length) {\n let end = Math.min(start + size, secret.length);\n if (end < secret.length) {\n const last = secret.charCodeAt(end - 1);\n if (last >= 0xd800 && last <= 0xdbff) end -= 1;\n }\n chunks.push(secret.slice(start, end));\n start = end;\n }\n return chunks;\n}\n\nfunction parseIndex(raw: string): Index | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n if (typeof parsed !== \"object\" || parsed === null) return null;\n const o = parsed as Record<string, unknown>;\n if (\n o[\"v\"] !== INDEX_VERSION ||\n typeof o[\"gen\"] !== \"string\" ||\n !/^[0-9a-f]{16}$/.test(o[\"gen\"]) ||\n typeof o[\"count\"] !== \"number\" ||\n !Number.isInteger(o[\"count\"]) ||\n o[\"count\"] < 1 ||\n o[\"count\"] > MAX_CHUNKS ||\n typeof o[\"sha256\"] !== \"string\" ||\n !/^[0-9a-f]{64}$/.test(o[\"sha256\"])\n ) {\n return null;\n }\n return { v: o[\"v\"], gen: o[\"gen\"], count: o[\"count\"], sha256: o[\"sha256\"] };\n}\n\nexport class ChunkedSecretStore {\n constructor(private readonly backend: SecretBackend) {}\n\n private guard<T>(operation: string, fn: () => T): T {\n try {\n return fn();\n } catch (error) {\n throw new KeychainUnavailableError(operation, error);\n }\n }\n\n /** Reads the stored secret. Never throws for bad data; throws only when the keychain itself fails. */\n load(): LoadResult {\n const raw = this.guard(\"read\", () => this.backend.get(INDEX_NAME));\n if (raw === null) return { state: \"signed-out\" };\n\n const index = parseIndex(raw);\n if (index === null) return { state: \"corrupt\", reason: \"the index entry is not valid\" };\n\n const parts: string[] = [];\n for (let n = 0; n < index.count; n += 1) {\n const part = this.guard(\"read\", () => this.backend.get(chunkName(index.gen, n)));\n if (part === null) return { state: \"corrupt\", reason: `chunk ${n} of ${index.count} is missing` };\n parts.push(part);\n }\n const secret = parts.join(\"\");\n if (sha256Hex(secret) !== index.sha256) {\n return { state: \"corrupt\", reason: \"the stored pieces do not match their checksum\" };\n }\n return { state: \"ok\", secret };\n }\n\n /**\n * Replaces the stored secret. The previous session stays readable until the\n * new index is written; a failure before that leaves it untouched.\n */\n save(secret: string): void {\n if (secret.length === 0) throw new Error(\"Refusing to store an empty secret.\");\n\n const gen = randomBytes(8).toString(\"hex\");\n const chunks = splitSecret(secret);\n if (chunks.length > MAX_CHUNKS) {\n throw new Error(`Refusing to store a secret longer than ${MAX_CHUNKS * CHUNK_SIZE} characters.`);\n }\n\n chunks.forEach((chunk, n) => {\n this.guard(\"write\", () => this.backend.set(chunkName(gen, n), chunk));\n });\n\n const index: Index = { v: INDEX_VERSION, gen, count: chunks.length, sha256: sha256Hex(secret) };\n this.guard(\"write\", () => this.backend.set(INDEX_NAME, JSON.stringify(index)));\n\n // The switch has happened. Everything from older generations is now garbage;\n // failing to delete it must not fail a save that already succeeded.\n try {\n this.sweep(gen);\n } catch {\n // The next save or `spell logout` sweeps again.\n }\n }\n\n /** Deletes everything this store wrote, strays included. Succeeds when nothing is stored. */\n clear(): void {\n this.guard(\"delete\", () => this.backend.delete(INDEX_NAME));\n this.sweep(null);\n }\n\n /** Removes every chunk that does not belong to `keepGen` (all chunks when it is null). */\n private sweep(keepGen: string | null): void {\n const names = this.guard(\"list\", () => this.backend.list());\n for (const name of names) {\n if (name === INDEX_NAME || !name.startsWith(CHUNK_PREFIX)) continue;\n if (keepGen !== null && name.startsWith(`${CHUNK_PREFIX}${keepGen}/`)) continue;\n this.guard(\"delete\", () => this.backend.delete(name));\n }\n }\n}\n","/**\n * The identity environments `spell login` can sign in to\n * (features/spell-login/PRD.md, R9). Two tenants, production and\n * development, selected by `ARCANE_ENVIRONMENT`; production when unset.\n *\n * Every value here is public configuration, not a secret: the client ids are\n * those of a public OAuth client (they ship in this package by definition),\n * and the tenant ids are part of the discovery URLs anyone can fetch. Keeping\n * them in one module is what lets a later change of provider stay a\n * configuration change.\n */\n\nexport type IdentityEnvironment = \"production\" | \"development\";\n\nexport interface IdentityEnvironmentConfig {\n environment: IdentityEnvironment;\n /** The tenant's own id, part of the discovery URL and the token issuer. */\n tenantId: string;\n /** The host that serves sign-in and the discovery document. */\n signInHost: string;\n /** The CLI's public-client id in that tenant. */\n clientId: string;\n discoveryUrl: string;\n}\n\nfunction defineIdentityEnvironment(\n config: Omit<IdentityEnvironmentConfig, \"discoveryUrl\">,\n): IdentityEnvironmentConfig {\n return {\n ...config,\n discoveryUrl: `https://${config.signInHost}/${config.tenantId}/v2.0/.well-known/openid-configuration`,\n };\n}\n\nexport const IDENTITY_ENVIRONMENTS: Readonly<Record<IdentityEnvironment, IdentityEnvironmentConfig>> = {\n production: defineIdentityEnvironment({\n environment: \"production\",\n tenantId: \"9f8edbfb-291c-4c4a-b9cc-8820a04f8973\",\n signInHost: \"arcaneai.ciamlogin.com\",\n clientId: \"7cd8f05d-9979-4458-8ca2-47c67748c41f\",\n }),\n development: defineIdentityEnvironment({\n environment: \"development\",\n tenantId: \"b365de0e-3026-4afe-bf5b-556fccb4ffcf\",\n signInHost: \"arcaneaidev.ciamlogin.com\",\n clientId: \"651be720-83cc-452b-8346-abf20713ad12\",\n }),\n};\n\n/**\n * What `spell login` asks for. `offline_access` is what returns a refresh\n * token, and without it there is nothing to store. One place to extend when a\n * service API's scope is published, so the person consents once (PRD, decision 6).\n */\nexport const SIGN_IN_SCOPES: readonly string[] = [\"openid\", \"profile\", \"email\", \"offline_access\"];\n\nexport const ENVIRONMENT_VARIABLE = \"ARCANE_ENVIRONMENT\";\n\nexport class UnknownEnvironmentError extends Error {\n constructor(value: string) {\n super(\n `${ENVIRONMENT_VARIABLE}=${JSON.stringify(value)} is not an environment. ` +\n \"Use \\\"production\\\" (the default) or \\\"development\\\".\",\n );\n this.name = \"UnknownEnvironmentError\";\n }\n}\n\n/** Resolves the environment from `ARCANE_ENVIRONMENT`; production when unset or empty. */\nexport function resolveIdentityEnvironment(\n env: NodeJS.ProcessEnv = process.env,\n): IdentityEnvironmentConfig {\n const raw = (env[ENVIRONMENT_VARIABLE] ?? \"\").trim().toLowerCase();\n if (raw === \"\" || raw === \"prod\" || raw === \"production\") return IDENTITY_ENVIRONMENTS.production;\n if (raw === \"dev\" || raw === \"development\") return IDENTITY_ENVIRONMENTS.development;\n throw new UnknownEnvironmentError(env[ENVIRONMENT_VARIABLE] ?? \"\");\n}\n","/**\n * The sign-in flows behind `spell login` (features/spell-login/PRD.md, R1,\n * R2, R8), on `openid-client`: the authorization-code flow with PKCE through a\n * loopback redirect, and the device-code flow for machines with no browser.\n *\n * Two things here are deliberate and tested:\n *\n * - The configuration is built from the fetched discovery document rather\n * than through `client.discovery()`, because the identity tenant publishes\n * its issuer under a different host from the one that serves the document,\n * and the standard \"issuer must match the URL\" check would refuse it.\n * - Nothing in this module prints or throws a token, an authorization code or\n * a `state` value (R10). The loopback server answers the browser with a\n * fixed page and never echoes the request.\n */\n\nimport { createServer, type Server } from \"node:http\";\nimport { execFile } from \"node:child_process\";\nimport * as client from \"openid-client\";\nimport type { IdentityEnvironmentConfig } from \"./identity-config.js\";\n\nexport interface TokenResult {\n refreshToken: string;\n sub: string;\n email: string | null;\n obtainedAt: Date;\n expiresAt: Date;\n}\n\nexport interface LoadConfigurationOptions {\n /** A replacement `fetch`, for proxies (R12) and for tests. */\n fetch?: typeof fetch;\n /** Tests only: let the flows talk to an `http://` provider. */\n allowInsecureHttp?: boolean;\n}\n\nexport class IdentityUnreachableError extends Error {\n constructor(host: string, cause: unknown) {\n super(`Could not reach the identity service at ${host}.`);\n this.name = \"IdentityUnreachableError\";\n this.cause = cause;\n }\n}\n\nexport class LoginTimeoutError extends Error {\n constructor() {\n super(\"Timed out waiting for the sign-in to finish in the browser.\");\n this.name = \"LoginTimeoutError\";\n }\n}\n\nexport class LoginDeniedError extends Error {\n constructor(code: string) {\n // `code` is the provider's error code (e.g. access_denied), never a secret.\n super(`The identity service refused the sign-in (${code}).`);\n this.name = \"LoginDeniedError\";\n }\n}\n\nexport async function loadIdentityConfiguration(\n environment: IdentityEnvironmentConfig,\n options: LoadConfigurationOptions = {},\n): Promise<client.Configuration> {\n const fetchImpl = options.fetch ?? fetch;\n let metadata: client.ServerMetadata;\n try {\n const response = await fetchImpl(environment.discoveryUrl, { redirect: \"manual\" });\n if (!response.ok) throw new Error(`discovery returned HTTP ${response.status}`);\n metadata = (await response.json()) as client.ServerMetadata;\n } catch (error) {\n throw new IdentityUnreachableError(environment.signInHost, error);\n }\n if (typeof metadata.issuer !== \"string\" || !metadata.token_endpoint || !metadata.authorization_endpoint) {\n throw new IdentityUnreachableError(environment.signInHost, new Error(\"discovery document incomplete\"));\n }\n\n const config = new client.Configuration(metadata, environment.clientId, undefined, client.None());\n if (options.fetch) config[client.customFetch] = options.fetch as client.CustomFetch;\n if (options.allowInsecureHttp) client.allowInsecureRequests(config);\n return config;\n}\n\nfunction toTokenResult(\n tokens: client.TokenEndpointResponse & client.TokenEndpointResponseHelpers,\n /** Used when the service did not rotate the refresh token. */\n previousRefreshToken?: string,\n): TokenResult {\n const claims = tokens.claims();\n if (!claims?.sub) throw new Error(\"The identity service returned no account identifier.\");\n const refreshToken = tokens.refresh_token ?? previousRefreshToken;\n if (!refreshToken) {\n throw new Error(\"The identity service returned no refresh token; nothing can be stored.\");\n }\n const now = new Date();\n const seconds = tokens.expiresIn() ?? 0;\n const email =\n typeof claims[\"email\"] === \"string\"\n ? claims[\"email\"]\n : typeof claims[\"preferred_username\"] === \"string\"\n ? claims[\"preferred_username\"]\n : null;\n return {\n refreshToken,\n sub: claims.sub,\n email,\n obtainedAt: now,\n expiresAt: new Date(now.getTime() + seconds * 1000),\n };\n}\n\nconst CLOSE_PAGE = [\n \"<!doctype html><meta charset=\\\"utf-8\\\"><title>Arcane</title>\",\n \"<body style=\\\"font-family:system-ui;margin:3rem\\\"><h1>Signed in to Arcane</h1>\",\n \"<p>You can close this window and go back to the terminal.</p></body>\",\n].join(\"\");\n\nconst FAILED_PAGE = [\n \"<!doctype html><meta charset=\\\"utf-8\\\"><title>Arcane</title>\",\n \"<body style=\\\"font-family:system-ui;margin:3rem\\\"><h1>Sign-in not completed</h1>\",\n \"<p>Go back to the terminal for what to do next.</p></body>\",\n].join(\"\");\n\nexport interface BrowserLoginOptions {\n scopes: readonly string[];\n /** Opens the URL in the person's browser; resolves false when no browser could be opened. */\n openUrl: (url: string) => Promise<boolean>;\n /** Completes local persistence before the browser can claim sign-in succeeded. */\n onValidated?: (result: TokenResult) => void | Promise<void>;\n /** Called with the URL when the browser could not be opened, so the person can open it by hand. */\n onCannotOpen?: (url: string) => void;\n timeoutMs?: number;\n}\n\n/**\n * Authorization code + PKCE through a loopback redirect: a server on\n * 127.0.0.1 and a random free port, one request accepted, closed on success,\n * failure, timeout or interrupt (R8).\n */\nexport async function browserLogin(\n config: client.Configuration,\n options: BrowserLoginOptions,\n): Promise<TokenResult> {\n const codeVerifier = client.randomPKCECodeVerifier();\n const codeChallenge = await client.calculatePKCECodeChallenge(codeVerifier);\n const state = client.randomState();\n const nonce = client.randomNonce();\n\n const server: Server = createServer();\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(0, \"127.0.0.1\", () => resolve());\n });\n const address = server.address();\n if (address === null || typeof address === \"string\") {\n server.close();\n throw new Error(\"Could not open a loopback port for the sign-in.\");\n }\n // Serialized through URL so it is byte-identical to what openid-client later\n // derives from the callback URL (a trailing slash): the token endpoint compares them.\n const redirectUri = new URL(`http://127.0.0.1:${address.port}`).href;\n\n const authorizationUrl = client.buildAuthorizationUrl(config, {\n redirect_uri: redirectUri,\n scope: options.scopes.join(\" \"),\n code_challenge: codeChallenge,\n code_challenge_method: \"S256\",\n state,\n nonce,\n });\n\n const timeoutMs = options.timeoutMs ?? 5 * 60 * 1000;\n let timer: NodeJS.Timeout | undefined;\n const close = (): void => {\n if (timer) clearTimeout(timer);\n server.closeAllConnections?.();\n server.close();\n };\n\n const tokens = new Promise<TokenResult>((resolve, reject) => {\n server.on(\"request\", (req, res) => {\n const url = new URL(req.url ?? \"/\", redirectUri);\n if (url.pathname !== \"/\" || (!url.searchParams.has(\"code\") && !url.searchParams.has(\"error\"))) {\n res.writeHead(404).end();\n return;\n }\n const errorCode = url.searchParams.get(\"error\");\n if (errorCode) {\n res.writeHead(200, { \"content-type\": \"text/html; charset=utf-8\" }).end(FAILED_PAGE);\n reject(new LoginDeniedError(errorCode.replace(/[^a-z_]/gi, \"\")));\n return;\n }\n client\n .authorizationCodeGrant(config, url, {\n pkceCodeVerifier: codeVerifier,\n expectedState: state,\n expectedNonce: nonce,\n idTokenExpected: true,\n })\n .then(async (response) => {\n const result = toTokenResult(response);\n await options.onValidated?.(result);\n res.writeHead(200, { \"content-type\": \"text/html; charset=utf-8\" }).end(CLOSE_PAGE);\n resolve(result);\n })\n .catch((error: unknown) => {\n res.writeHead(200, { \"content-type\": \"text/html; charset=utf-8\" }).end(FAILED_PAGE);\n reject(error);\n });\n });\n timer = setTimeout(() => reject(new LoginTimeoutError()), timeoutMs);\n });\n // The callback can fail before `await tokens` below attaches, while the\n // browser opener is still running; mark the rejection handled so Node does\n // not report it. `await tokens` still throws the same error.\n tokens.catch(() => {});\n\n try {\n const opened = await options.openUrl(authorizationUrl.href);\n if (!opened) options.onCannotOpen?.(authorizationUrl.href);\n return await tokens;\n } finally {\n close();\n }\n}\n\nexport interface DeviceLoginOptions {\n scopes: readonly string[];\n /** Shows the person where to go and which code to type. */\n prompt: (verificationUri: string, userCode: string, expiresInSeconds: number) => void;\n}\n\nexport async function deviceLogin(\n config: client.Configuration,\n options: DeviceLoginOptions,\n): Promise<TokenResult> {\n const handle = await client.initiateDeviceAuthorization(config, { scope: options.scopes.join(\" \") });\n options.prompt(handle.verification_uri, handle.user_code, handle.expires_in);\n const response = await client.pollDeviceAuthorizationGrant(config, handle);\n return toTokenResult(response);\n}\n\n/** Exchanges the stored refresh token for fresh tokens; the identity service may rotate it. */\nexport async function refreshSession(\n config: client.Configuration,\n refreshToken: string,\n scopes: readonly string[],\n): Promise<TokenResult> {\n const response = await client.refreshTokenGrant(config, refreshToken, { scope: scopes.join(\" \") });\n return toTokenResult(response, refreshToken);\n}\n\n/**\n * Opens a URL in the default browser without a shell, so nothing in the URL\n * is ever interpreted. Resolves false when the platform's opener is missing\n * or fails, which is the \"no browser\" case `spell login` answers with a\n * `--device-code` suggestion.\n */\nexport function openInDefaultBrowser(url: string): Promise<boolean> {\n const [command, args] =\n process.platform === \"win32\"\n ? [\"rundll32\", [\"url.dll,FileProtocolHandler\", url]]\n : process.platform === \"darwin\"\n ? [\"open\", [url]]\n : [\"xdg-open\", [url]];\n return new Promise((resolve) => {\n try {\n const child = execFile(command, args, { windowsHide: true }, (error) => resolve(!error));\n child.on(\"error\", () => resolve(false));\n } catch {\n resolve(false);\n }\n });\n}\n","/**\n * The signed-in session for `spell login` (features/spell-login/PRD.md, R3 to\n * R6): what is stored, where, and the explicit opt-in to a file.\n *\n * Stored (R4): the refresh token, the account's `sub` and email, the\n * environment, and when the last token was obtained and expires. Never the\n * access token, never the ID token.\n *\n * Where: the OS keychain through the chunked store (R4a) by default. With\n * `--insecure-storage` only, a JSON file under `~/.arcane/` instead -- mode\n * 0600 on POSIX; on Windows, access restricted to the current account with\n * `icacls`, and the file is refused when that fails (PRD, decision 3).\n * Saving to one medium removes the other, so a stale token never lingers in\n * the medium that stopped being used.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, existsSync } from \"node:fs\";\nimport { userInfo } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport {\n ChunkedSecretStore,\n KeychainUnavailableError,\n type LoadResult,\n type SecretBackend,\n} from \"./credential-store.js\";\nimport { createKeychainBackend } from \"./keychain-backend.js\";\nimport type { IdentityEnvironment } from \"./identity-config.js\";\nimport { userTierRoot } from \"./user-tier.js\";\n\nexport const INSECURE_SESSION_FILE_NAME = \"session-insecure.json\";\n\nexport interface SessionRecord {\n v: 1;\n environment: IdentityEnvironment;\n sub: string;\n email: string | null;\n refreshToken: string;\n /** ISO 8601. When the tokens were obtained. */\n obtainedAt: string;\n /** ISO 8601. When the last access token expired or expires; a hint, not proof the session is alive. */\n expiresAt: string;\n}\n\nexport type SessionMedium = \"keychain\" | \"file\";\n\n/** Cleanup removed the listed media, but the OS keychain could not be verified or cleared. */\nexport class SessionClearError extends Error {\n constructor(\n readonly keychainError: KeychainUnavailableError,\n readonly removed: SessionMedium[],\n ) {\n super(keychainError.message);\n this.name = \"SessionClearError\";\n this.cause = keychainError;\n }\n}\n\nexport type SessionLoad =\n | { state: \"signed-out\"; keychainError?: KeychainUnavailableError }\n | { state: \"ok\"; source: SessionMedium; record: SessionRecord }\n | { state: \"corrupt\"; source: SessionMedium; reason: string };\n\nexport interface SessionStorage {\n load(): SessionLoad;\n /** Throws `KeychainUnavailableError` when `insecure` is false and the keychain cannot be used (R6). */\n save(record: SessionRecord, options: { insecure: boolean }): void;\n /** Removes the session from every medium. A partial cleanup error lists media actually removed (R3). */\n clear(): { removed: SessionMedium[] };\n}\n\nfunction isIsoDate(value: unknown): value is string {\n return typeof value === \"string\" && !Number.isNaN(Date.parse(value));\n}\n\nexport function parseSessionRecord(raw: string): SessionRecord | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n if (typeof parsed !== \"object\" || parsed === null) return null;\n const o = parsed as Record<string, unknown>;\n if (\n o[\"v\"] !== 1 ||\n (o[\"environment\"] !== \"production\" && o[\"environment\"] !== \"development\") ||\n typeof o[\"sub\"] !== \"string\" ||\n o[\"sub\"] === \"\" ||\n !(typeof o[\"email\"] === \"string\" || o[\"email\"] === null) ||\n typeof o[\"refreshToken\"] !== \"string\" ||\n o[\"refreshToken\"] === \"\" ||\n !isIsoDate(o[\"obtainedAt\"]) ||\n !isIsoDate(o[\"expiresAt\"])\n ) {\n return null;\n }\n return {\n v: 1,\n environment: o[\"environment\"],\n sub: o[\"sub\"],\n email: o[\"email\"],\n refreshToken: o[\"refreshToken\"],\n obtainedAt: o[\"obtainedAt\"],\n expiresAt: o[\"expiresAt\"],\n };\n}\n\n/**\n * A `SecretBackend` over one JSON file: the opt-in insecure medium. Writes are\n * atomic (temp file, then rename) and the file is created restricted to the\n * current account; when restricting fails the file is removed and the write\n * fails, so a world-readable session file never exists.\n */\nexport class FileSecretBackend implements SecretBackend {\n constructor(\n readonly filePath: string,\n private readonly restrict: (filePath: string) => void = restrictToCurrentUser,\n ) {}\n\n exists(): boolean {\n return existsSync(this.filePath);\n }\n\n private read(): Record<string, string> {\n if (!existsSync(this.filePath)) return {};\n const parsed: unknown = JSON.parse(readFileSync(this.filePath, \"utf8\"));\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new Error(\"the session file is not an object\");\n }\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof v === \"string\") out[k] = v;\n }\n return out;\n }\n\n private write(entries: Record<string, string>): void {\n if (Object.keys(entries).length === 0) {\n rmSync(this.filePath, { force: true });\n return;\n }\n mkdirSync(dirname(this.filePath), { recursive: true });\n const tmp = `${this.filePath}.${process.pid}.tmp`;\n writeFileSync(tmp, JSON.stringify(entries), { encoding: \"utf8\", mode: 0o600 });\n try {\n this.restrict(tmp);\n } catch (error) {\n rmSync(tmp, { force: true });\n throw new Error(\n \"Could not restrict the session file to your account, so it was not written.\",\n { cause: error },\n );\n }\n renameSync(tmp, this.filePath);\n }\n\n get(name: string): string | null {\n return this.read()[name] ?? null;\n }\n set(name: string, value: string): void {\n const entries = this.read();\n entries[name] = value;\n this.write(entries);\n }\n delete(name: string): void {\n const entries = this.read();\n if (!(name in entries)) return;\n delete entries[name];\n this.write(entries);\n }\n list(): string[] {\n return Object.keys(this.read());\n }\n /** Removes the file itself, strays and all. */\n remove(): void {\n rmSync(this.filePath, { force: true });\n }\n}\n\n/**\n * POSIX: the 0600 mode at creation already restricts the file. Windows has\n * no mode bits, so inherited access is removed and only the current account\n * is granted, through the OS's own `icacls`.\n */\nexport function restrictToCurrentUser(filePath: string): void {\n if (process.platform !== \"win32\") return;\n const account = userInfo().username;\n execFileSync(\"icacls\", [filePath, \"/inheritance:r\", \"/grant:r\", `${account}:F`], {\n stdio: \"ignore\",\n windowsHide: true,\n });\n}\n\nexport function insecureSessionFilePath(homeDir?: string): string {\n return join(userTierRoot(homeDir), INSECURE_SESSION_FILE_NAME);\n}\n\nexport interface SessionStorageOptions {\n keychainBackend?: SecretBackend;\n fileBackend?: FileSecretBackend;\n}\n\nexport function createSessionStorage(options: SessionStorageOptions = {}): SessionStorage {\n const keychain = new ChunkedSecretStore(options.keychainBackend ?? createKeychainBackend());\n const fileBackend = options.fileBackend ?? new FileSecretBackend(insecureSessionFilePath());\n const file = new ChunkedSecretStore(fileBackend);\n\n const interpret = (result: LoadResult, source: SessionMedium): SessionLoad | null => {\n if (result.state === \"signed-out\") return null;\n if (result.state === \"corrupt\") return { state: \"corrupt\", source, reason: result.reason };\n const record = parseSessionRecord(result.secret);\n if (record === null) return { state: \"corrupt\", source, reason: \"the stored session is not valid\" };\n return { state: \"ok\", source, record };\n };\n\n return {\n load() {\n let keychainError: KeychainUnavailableError | undefined;\n try {\n const fromKeychain = interpret(keychain.load(), \"keychain\");\n if (fromKeychain) return fromKeychain;\n } catch (error) {\n if (!(error instanceof KeychainUnavailableError)) throw error;\n keychainError = error;\n }\n if (fileBackend.exists()) {\n const fromFile = interpret(file.load(), \"file\");\n if (fromFile) return fromFile;\n }\n return keychainError ? { state: \"signed-out\", keychainError } : { state: \"signed-out\" };\n },\n\n save(record, { insecure }) {\n const serialized = JSON.stringify(record);\n if (insecure) {\n file.save(serialized);\n try {\n keychain.clear();\n } catch {\n // The keychain is unusable, which is why the file was chosen.\n }\n return;\n }\n keychain.save(serialized);\n fileBackend.remove();\n },\n\n clear() {\n const removed: SessionMedium[] = [];\n let keychainError: unknown;\n try {\n const hadKeychainSession = keychain.load().state !== \"signed-out\";\n keychain.clear();\n if (hadKeychainSession) removed.push(\"keychain\");\n } catch (error) {\n keychainError = error;\n }\n if (fileBackend.exists()) {\n fileBackend.remove();\n removed.push(\"file\");\n }\n if (keychainError instanceof KeychainUnavailableError) {\n throw new SessionClearError(keychainError, removed);\n }\n if (keychainError) throw keychainError;\n return { removed };\n },\n };\n}\n","/**\n * The OS keychain as a `SecretBackend` for the chunked credential store\n * (features/spell-login/PRD.md, R4a and R6): macOS Keychain, Windows\n * Credential Manager, or the Linux Secret Service, through @napi-rs/keyring.\n *\n * The native package is loaded on first use, not at import, so a platform\n * with no prebuilt binary or no keychain only fails the commands that need\n * one (those map the failure to `KeychainUnavailableError`, which is R6's\n * refusal), and every signed-out command keeps working (R7).\n */\n\nimport { createRequire } from \"node:module\";\nimport type { SecretBackend } from \"./credential-store.js\";\n\n/** The keychain \"service\" half of every entry Arcane writes. */\nexport const KEYCHAIN_SERVICE = \"arcane-cli\";\n\ninterface KeyringEntry {\n getPassword(): string | null;\n setPassword(password: string): void;\n deletePassword(): boolean | void;\n}\n\ninterface KeyringModule {\n Entry: new (service: string, username: string) => KeyringEntry;\n findCredentials(service: string): Array<{ account: string; password: string }>;\n}\n\nexport function createKeychainBackend(service: string = KEYCHAIN_SERVICE): SecretBackend {\n let loaded: KeyringModule | null = null;\n const keyring = (): KeyringModule => {\n loaded ??= createRequire(import.meta.url)(\"@napi-rs/keyring\") as KeyringModule;\n return loaded;\n };\n const entry = (name: string): KeyringEntry => new (keyring().Entry)(service, name);\n\n return {\n get: (name) => entry(name).getPassword() ?? null,\n set: (name, value) => {\n entry(name).setPassword(value);\n },\n delete: (name) => {\n entry(name).deletePassword();\n },\n list: () => keyring().findCredentials(service).map((credential) => credential.account),\n };\n}\n","import { printInfo, printSuccess, printWarning } from \"../modules/banner.js\";\nimport { KeychainUnavailableError } from \"../modules/credential-store.js\";\nimport {\n SIGN_IN_SCOPES,\n UnknownEnvironmentError,\n resolveIdentityEnvironment,\n type IdentityEnvironmentConfig,\n} from \"../modules/identity-config.js\";\nimport {\n IdentityUnreachableError,\n LoginDeniedError,\n LoginTimeoutError,\n browserLogin,\n deviceLogin,\n loadIdentityConfiguration,\n openInDefaultBrowser,\n type TokenResult,\n} from \"../modules/oidc.js\";\nimport { createSessionStorage, type SessionRecord, type SessionStorage } from \"../modules/session-store.js\";\nimport type { Configuration } from \"openid-client\";\n\nexport interface LoginOptions {\n deviceCode?: boolean;\n insecureStorage?: boolean;\n}\n\n/** Everything `runLogin` reaches outside itself, replaceable in tests. */\nexport interface LoginDeps {\n storage: SessionStorage;\n environment: () => IdentityEnvironmentConfig;\n loadConfiguration: (environment: IdentityEnvironmentConfig) => Promise<Configuration>;\n browserLogin: typeof browserLogin;\n deviceLogin: typeof deviceLogin;\n openUrl: (url: string) => Promise<boolean>;\n isInteractive: () => boolean;\n}\n\nexport function defaultLoginDeps(): LoginDeps {\n return {\n storage: createSessionStorage(),\n environment: () => resolveIdentityEnvironment(),\n loadConfiguration: (environment) => loadIdentityConfiguration(environment),\n browserLogin,\n deviceLogin,\n openUrl: openInDefaultBrowser,\n isInteractive: () => Boolean(process.stdin.isTTY && process.stdout.isTTY),\n };\n}\n\nexport const INSECURE_STORAGE_WARNING =\n \"Insecure storage: the session is kept in a file protected only by your account's file permissions, \" +\n \"not in the OS keychain. Run `spell logout` to remove it.\";\n\n/**\n * Error text that is safe to show (R10): the first line only, with anything\n * shaped like a callback query value removed.\n */\nexport function safeErrorText(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n return message.split(\"\\n\")[0]!.replace(/([?&](?:code|state|id_token|access_token|refresh_token)=)[^&\\s]+/gi, \"$1[redacted]\");\n}\n\nexport function toSessionRecord(result: TokenResult, environment: IdentityEnvironmentConfig): SessionRecord {\n return {\n v: 1,\n environment: environment.environment,\n sub: result.sub,\n email: result.email,\n refreshToken: result.refreshToken,\n obtainedAt: result.obtainedAt.toISOString(),\n expiresAt: result.expiresAt.toISOString(),\n };\n}\n\nexport function describeAccount(record: Pick<SessionRecord, \"sub\" | \"email\" | \"environment\">): string {\n const who = record.email ? `${record.email} (${record.sub})` : record.sub;\n return record.environment === \"development\" ? `${who} [development]` : who;\n}\n\n/**\n * Runs `spell login` (features/spell-login/PRD.md, R1, R2, R5, R6, R16):\n * browser flow by default, device code on request, the session stored only\n * after the sign-in succeeded so a failure leaves any previous session alone.\n */\nexport async function runLogin(options: LoginOptions, deps: LoginDeps = defaultLoginDeps()): Promise<void> {\n let environment: IdentityEnvironmentConfig;\n try {\n environment = deps.environment();\n } catch (error) {\n if (error instanceof UnknownEnvironmentError) {\n console.error(error.message);\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n throw error;\n }\n\n if (!options.deviceCode && !deps.isInteractive()) {\n console.error(\n \"No interactive terminal, so a browser sign-in cannot be started here. \" +\n \"Run `spell login --device-code` and finish the sign-in on another device.\",\n );\n process.exit(1);\n return;\n }\n\n let config: Configuration;\n try {\n config = await deps.loadConfiguration(environment);\n } catch (error) {\n console.error(\n error instanceof IdentityUnreachableError\n ? `${error.message} Check your network connection and try again.`\n : `Could not prepare the sign-in: ${safeErrorText(error)}`,\n );\n process.exit(1);\n return;\n }\n\n let record: SessionRecord | undefined;\n const saveValidatedSession = (result: TokenResult): void => {\n const next = toSessionRecord(result, environment);\n deps.storage.save(next, { insecure: Boolean(options.insecureStorage) });\n record = next;\n };\n try {\n if (options.deviceCode) {\n const result = await deps.deviceLogin(config, {\n scopes: SIGN_IN_SCOPES,\n prompt: (uri, code, seconds) => {\n printInfo(`Open ${uri} on any device and enter the code ${code}.`);\n printInfo(`Waiting up to ${Math.round(seconds / 60)} minutes for you to finish signing in.`);\n },\n });\n saveValidatedSession(result);\n } else {\n printInfo(\"Opening your browser to sign in to Arcane. Come back here when it says you are signed in.\");\n await deps.browserLogin(config, {\n scopes: SIGN_IN_SCOPES,\n openUrl: deps.openUrl,\n onValidated: saveValidatedSession,\n onCannotOpen: (url) => {\n printWarning(\"No browser could be opened. Open this address yourself, or run `spell login --device-code`:\");\n printInfo(url);\n },\n });\n if (!record) throw new Error(\"The browser sign-in ended without storing a session.\");\n }\n } catch (error) {\n if (error instanceof KeychainUnavailableError) {\n console.error(\n `${error.message} Nothing was stored. If this machine has no keychain, ` +\n \"run `spell login --insecure-storage` to keep the session in a file instead \" +\n \"(protected only by your account's file permissions).\",\n );\n } else if (error instanceof LoginTimeoutError || error instanceof LoginDeniedError) {\n console.error(`${error.message} Nothing was changed.`);\n } else {\n console.error(`The sign-in did not complete: ${safeErrorText(error)}. Nothing was changed.`);\n }\n process.exit(1);\n return;\n }\n\n if (!record) throw new Error(\"The sign-in completed without a stored session.\");\n printSuccess(`Signed in as ${describeAccount(record)}.`);\n if (options.insecureStorage) printWarning(INSECURE_STORAGE_WARNING);\n}\n","import { printInfo, printSuccess } from \"../modules/banner.js\";\nimport { KeychainUnavailableError } from \"../modules/credential-store.js\";\nimport { createSessionStorage, SessionClearError, type SessionStorage } from \"../modules/session-store.js\";\n\n/**\n * Runs `spell logout` (features/spell-login/PRD.md, R3): removes the session\n * from every medium `spell login` can write to and says what it removed.\n * Succeeds when nothing was stored. Local only: the session at the identity\n * service stays valid until it expires (R21 is a later release), and the\n * output says so rather than implying otherwise.\n */\nexport function runLogout(storage: SessionStorage = createSessionStorage()): void {\n let removed: (\"keychain\" | \"file\")[];\n try {\n removed = storage.clear().removed;\n } catch (error) {\n if (error instanceof SessionClearError) {\n const fileResult = error.removed.includes(\"file\") ? \"The insecure session file was removed. \" : \"\";\n console.error(\n `${fileResult}${error.keychainError.message} Any session in the OS keychain could not be verified or removed.`,\n );\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n if (error instanceof KeychainUnavailableError) {\n console.error(`${error.message} The session could not be removed from it.`);\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n throw error;\n }\n\n if (removed.length === 0) {\n printSuccess(\"Nothing was stored; you were already signed out.\");\n return;\n }\n const where = removed.map((m) => (m === \"keychain\" ? \"the OS keychain\" : \"the insecure session file\")).join(\" and \");\n printSuccess(`Signed out: removed the session from ${where}.`);\n printInfo(\"This signs out this machine only; the session at the identity service stays valid until it expires.\");\n}\n","import { printInfo, printSuccess, printWarning } from \"../modules/banner.js\";\nimport { KeychainUnavailableError } from \"../modules/credential-store.js\";\nimport {\n IDENTITY_ENVIRONMENTS,\n SIGN_IN_SCOPES,\n type IdentityEnvironmentConfig,\n} from \"../modules/identity-config.js\";\nimport { loadIdentityConfiguration, refreshSession } from \"../modules/oidc.js\";\nimport { createSessionStorage, type SessionStorage } from \"../modules/session-store.js\";\nimport { INSECURE_STORAGE_WARNING, describeAccount, safeErrorText, toSessionRecord } from \"./login.js\";\nimport type { Configuration } from \"openid-client\";\n\nexport interface WhoamiOptions {\n verify?: boolean;\n}\n\nexport interface WhoamiDeps {\n storage: SessionStorage;\n loadConfiguration: (environment: IdentityEnvironmentConfig) => Promise<Configuration>;\n refreshSession: typeof refreshSession;\n now: () => Date;\n}\n\nexport function defaultWhoamiDeps(): WhoamiDeps {\n return {\n storage: createSessionStorage(),\n loadConfiguration: (environment) => loadIdentityConfiguration(environment),\n refreshSession,\n now: () => new Date(),\n };\n}\n\n/**\n * Runs `spell whoami` (features/spell-login/PRD.md, R11): who is signed in,\n * from the stored session alone; `--verify` asks the identity service and\n * keeps the rotated refresh token it may return. Signed out is exit 1, so a\n * script can test for a session.\n */\nexport async function runWhoami(options: WhoamiOptions, deps: WhoamiDeps = defaultWhoamiDeps()): Promise<void> {\n let loaded;\n try {\n loaded = deps.storage.load();\n } catch (error) {\n if (error instanceof KeychainUnavailableError) {\n console.error(`${error.message} Cannot tell whether you are signed in.`);\n process.exit(1);\n return; // guard: process.exit is mocked in tests\n }\n throw error;\n }\n\n if (loaded.state === \"signed-out\") {\n console.error(\"Not signed in. Run `spell login`.\");\n if (loaded.keychainError) console.error(loaded.keychainError.message);\n process.exit(1);\n return;\n }\n if (loaded.state === \"corrupt\") {\n console.error(\n `The stored session is damaged (${loaded.reason}), so you are treated as signed out. ` +\n \"Run `spell logout` to clear it, then `spell login`.\",\n );\n process.exit(1);\n return;\n }\n\n const { record, source } = loaded;\n printSuccess(`Signed in as ${describeAccount(record)}.`);\n const expires = new Date(record.expiresAt);\n const expired = expires.getTime() <= deps.now().getTime();\n printInfo(`Signed in on ${new Date(record.obtainedAt).toLocaleString()}.`);\n printInfo(\n expired\n ? `The last token expired on ${expires.toLocaleString()}; the session refreshes on next use.`\n : `The current token is valid until ${expires.toLocaleString()}.`,\n );\n printInfo(`Stored in ${source === \"keychain\" ? \"the OS keychain\" : \"the insecure session file\"}.`);\n if (source === \"file\") printWarning(INSECURE_STORAGE_WARNING);\n\n if (!options.verify) return;\n\n const environment = IDENTITY_ENVIRONMENTS[record.environment];\n try {\n const config = await deps.loadConfiguration(environment);\n const fresh = await deps.refreshSession(config, record.refreshToken, SIGN_IN_SCOPES);\n deps.storage.save(toSessionRecord(fresh, environment), { insecure: source === \"file\" });\n printSuccess(`Verified with the identity service at ${environment.signInHost}; the session is active.`);\n } catch (error) {\n console.error(\n `The identity service did not accept the session (${safeErrorText(error)}). Run \\`spell login\\` again.`,\n );\n process.exit(1);\n }\n}\n","import { access } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport {\n discoverPrograms,\n programFromPlanPath,\n runReportGeneration,\n type ReportProgram,\n type SkippedPlan,\n} from \"../modules/show-report/generate.js\";\nimport { isShallowRepository } from \"../modules/show-report/sources.js\";\nimport { REPORT_THEMES, type ReportTheme } from \"../modules/show-report/theme.js\";\n\nexport interface ReportCliOptions {\n /** Report on one PLAN.md instead of auto-discovering docs/plans/*\\/PLAN.md. */\n plan?: string;\n /** Write show-report.{json,html} here instead of next to the plan. */\n out?: string;\n /** Accepted for forward compatibility; external snapshotting is not implemented yet. */\n refresh?: boolean;\n /** Force the toggle's initial state: auto | light | dark (default auto). */\n theme?: string;\n}\n\n/**\n * `spell report` (SR-03): generate a program's Show Report -- show-report.json\n * and show-report.html -- from its PLAN.md, OPERATOR-QUEUE.md, the\n * verification ledger and local git history. Offline by construction (ARC-042):\n * the template ships inside this package's dist/assets/, and nothing here\n * touches the network. Writes only files that differ from a fresh\n * regeneration, so re-running on an up-to-date repository changes nothing.\n */\nexport async function runReport(\n targetDir: string,\n options: ReportCliOptions = {},\n assetsDir: string,\n): Promise<void> {\n const templatePath = join(assetsDir, \"report\", \"show-report.template.html\");\n try {\n await access(templatePath);\n } catch {\n console.error(\n `✖ arcane: report template not found at ${templatePath} -- this arcane-cli build is missing dist/assets/report/. Reinstall the package (or run \\`npm run build\\` in a source checkout).`,\n );\n process.exitCode = 1;\n return;\n }\n\n if (options.refresh) {\n console.log(\n \"--refresh: external-data snapshotting (gh PR titles/merge dates) is not implemented yet; regenerating from local sources.\",\n );\n }\n\n const theme = (options.theme ?? \"auto\") as ReportTheme;\n if (!REPORT_THEMES.includes(theme)) {\n console.error(\n `✖ arcane: --theme must be one of ${REPORT_THEMES.join(\" | \")}, got \"${options.theme}\".`,\n );\n process.exitCode = 1;\n return;\n }\n\n // Degrade visibly, not silently: the page still renders in a shallow clone,\n // but its version span and cast come from history the clone cannot see.\n if (await isShallowRepository(targetDir)) {\n console.warn(\n \"⚠ arcane: shallow clone -- the report's version span and cast are derived from git history and will be omitted. Run `git fetch --unshallow` for a complete report.\",\n );\n }\n\n let programs: ReportProgram[];\n let skipped: SkippedPlan[];\n if (options.plan) {\n const result = await programFromPlanPath(targetDir, options.plan);\n programs = result.program ? [result.program] : [];\n skipped = result.skipped ? [result.skipped] : [];\n } else {\n const discovery = await discoverPrograms(targetDir);\n programs = discovery.programs;\n skipped = discovery.skipped;\n }\n\n for (const s of skipped) console.log(`Skipped ${s.slug} -- ${s.reason}.`);\n\n if (programs.length === 0) {\n console.error(\n \"✖ arcane: no program to report on -- expected docs/plans/<slug>/PLAN.md with `- [x] **ID — Title.**` epic bullets, or pass --plan <path>.\",\n );\n process.exitCode = 1;\n return;\n }\n\n const result = await runReportGeneration({\n rootDir: targetDir,\n templatePath,\n mode: \"write\",\n programs,\n outDir: options.out,\n theme,\n });\n\n for (const u of result.unwritten) {\n console.warn(\n `⚠ arcane: ${u.slug} has ${u.ids.length} epic(s) with no \\`**Report:**\\` line in PLAN.md -- they render as \"unwritten\": ${u.ids.join(\", \")}.`,\n );\n }\n\n for (const slug of result.themeSkipped) {\n console.warn(\n `⚠ arcane: ${slug} -- --theme ${theme} requested, but this template has no theme toggle to set (older vendored template?); left unchanged.`,\n );\n }\n\n if (result.repaired.length === 0) {\n console.log(`Show report: ${programs.length} program(s) already up to date.`);\n return;\n }\n console.log(`Show report: wrote ${result.repaired.length} file(s).`);\n for (const path of result.repaired) console.log(` ${path}`);\n}\n","/**\n * Show Report generation core (SR-02/SR-03): program discovery, the\n * deterministic compile date, JSON + HTML generation, and the check/write\n * parity loop. Shared by scripts/report.ts (this repository's --check/--fix\n * gate, template from src/assets/) and src/commands/report.ts (`spell report`\n * in a consumer repository, template from the installed package's\n * dist/assets/). Lives in src/ because scripts/ is not bundled into the\n * published CLI.\n */\n\nimport { mkdir, readdir, readFile, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, isAbsolute, join, relative, resolve } from \"node:path\";\nimport { buildShowReportModel } from \"./model.js\";\nimport { renderShowReport } from \"./render.js\";\nimport { applyReportTheme, type ReportTheme } from \"./theme.js\";\nimport { parseEpics, parseFrontmatter } from \"./plan-parser.js\";\nimport type { ShowReport } from \"./types.js\";\n\n/**\n * Recorded in provenance.templateVersion. \"v0-interim\" is the hand-CSS template\n * SR-02 ships; SR-06 replaces it with the arcane-ui version the compiled\n * template was built from.\n */\nexport const TEMPLATE_VERSION = \"v0-interim\";\n\nexport const NO_EPICS_REASON = \"no parseable `- [x] **ID — Title.**` epic bullets\";\n\nexport interface ReportProgram {\n slug: string;\n planRelPath: string;\n queueRelPath: string;\n ledgerProgramName: string;\n}\n\nexport interface SkippedPlan {\n slug: string;\n reason: string;\n}\n\nexport interface ReportDiscovery {\n programs: ReportProgram[];\n /** Plans found but skipped, with the reason -- reported, never silent. */\n skipped: SkippedPlan[];\n}\n\n/** `\"Lessons Hardening — Mechanical Enforcement ...\"` -> `\"Lessons Hardening\"`, the form ledger headings use. */\nexport function programNameFromTitle(title: string): string {\n return title.split(\" — \")[0]!.trim();\n}\n\n/**\n * Deterministic compile date: the most recent date the plan itself records.\n * Wall-clock would make --check fail on every run. Limitation, disclosed: for\n * a still-active program this is its activation/creation date, so the\n * ADRs-accepted window (model.ts) ends there until the plan gains a\n * `completed:` date.\n */\nexport function deterministicCompiledAt(frontmatter: {\n completed?: string;\n activated?: string;\n created?: string;\n}): string {\n return frontmatter.completed ?? frontmatter.activated ?? frontmatter.created ?? \"unknown\";\n}\n\nfunction toPosix(path: string): string {\n return path.replace(/\\\\/g, \"/\");\n}\n\nasync function programFromPlan(\n rootDir: string,\n slug: string,\n planRelPath: string,\n): Promise<{ program?: ReportProgram; skipped?: SkippedPlan }> {\n let planContent: string;\n try {\n planContent = await readFile(join(rootDir, planRelPath), \"utf8\");\n } catch {\n return { skipped: { slug, reason: `no PLAN.md at ${planRelPath}` } };\n }\n if (parseEpics(planContent).length === 0) {\n // A plan whose epics live in a table (like show-report's own) parses to\n // zero rows; an empty report for it would be noise, not information.\n return { skipped: { slug, reason: NO_EPICS_REASON } };\n }\n const frontmatter = parseFrontmatter(planContent);\n return {\n program: {\n slug,\n planRelPath,\n queueRelPath: toPosix(join(dirname(planRelPath), \"OPERATOR-QUEUE.md\")),\n ledgerProgramName: programNameFromTitle(frontmatter.title ?? slug),\n },\n };\n}\n\n/** Every `docs/plans/<slug>/PLAN.md` under `rootDir`, sorted by slug. */\nexport async function discoverPrograms(rootDir: string): Promise<ReportDiscovery> {\n const plansDir = join(rootDir, \"docs\", \"plans\");\n let entries;\n try {\n entries = (await readdir(plansDir, { withFileTypes: true })).filter((e) => e.isDirectory());\n } catch (error: unknown) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return { programs: [], skipped: [] };\n throw error;\n }\n\n const programs: ReportProgram[] = [];\n const skipped: SkippedPlan[] = [];\n for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n const result = await programFromPlan(rootDir, entry.name, `docs/plans/${entry.name}/PLAN.md`);\n if (result.program) programs.push(result.program);\n if (result.skipped) skipped.push(result.skipped);\n }\n return { programs, skipped };\n}\n\n/**\n * One program from an explicit PLAN.md path (absolute, or relative to\n * `rootDir`). The slug is the plan's parent directory name, matching the\n * `docs/plans/<slug>/` convention without requiring it.\n */\nexport async function programFromPlanPath(\n rootDir: string,\n planPath: string,\n): Promise<{ program?: ReportProgram; skipped?: SkippedPlan }> {\n const planAbs = isAbsolute(planPath) ? planPath : resolve(rootDir, planPath);\n const planRelPath = toPosix(relative(rootDir, planAbs));\n return programFromPlan(rootDir, basename(dirname(planAbs)), planRelPath);\n}\n\nfunction normalizeLineEndings(content: string): string {\n return content.replace(/\\r\\n?/g, \"\\n\");\n}\n\nasync function readOptionalFile(path: string): Promise<string | null> {\n try {\n return await readFile(path, \"utf8\");\n } catch (error: unknown) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n }\n}\n\nexport interface GeneratedReport {\n json: string;\n html: string;\n /** False when a non-\"auto\" `theme` was requested but the template has no toggle to set. */\n themeApplied: boolean;\n}\n\nexport async function generateReport(\n rootDir: string,\n program: ReportProgram,\n templateHtml: string,\n theme: ReportTheme = \"auto\",\n): Promise<GeneratedReport> {\n const planContent = await readFile(join(rootDir, program.planRelPath), \"utf8\");\n const frontmatter = parseFrontmatter(planContent);\n const model = await buildShowReportModel({\n rootDir,\n slug: program.slug,\n planRelPath: program.planRelPath,\n queueRelPath: program.queueRelPath,\n ledgerProgramName: program.ledgerProgramName,\n compiledAt: deterministicCompiledAt(frontmatter),\n templateVersion: TEMPLATE_VERSION,\n });\n const themed = applyReportTheme(renderShowReport(model, templateHtml), theme);\n return {\n json: `${JSON.stringify(model, null, 2)}\\n`,\n html: themed.html,\n themeApplied: themed.applied,\n };\n}\n\nexport type GenerationMode = \"check\" | \"write\";\n\nexport interface ReportGenerationOptions {\n rootDir: string;\n /** Absolute path to show-report.template.html. */\n templatePath: string;\n mode: GenerationMode;\n programs: ReportProgram[];\n /** Override the output directory (default: next to each program's PLAN.md). Absolute, or relative to `rootDir`. */\n outDir?: string;\n /** Force the toggle's initial state (SR-UP06). Default \"auto\" -- byte-for-byte unchanged from today, so every existing golden stays valid without this. */\n theme?: ReportTheme;\n}\n\nexport interface UnwrittenRow {\n slug: string;\n /** Epic ids whose `**Report:**` line is missing, so the report renders them as \"unwritten\". */\n ids: string[];\n}\n\nexport interface ReportGenerationResult {\n /** Outputs that do not match a regeneration (line-ending-normalized), as rootDir-relative paths. */\n drifted: string[];\n /** Outputs written in `write` mode, as rootDir-relative paths. */\n repaired: string[];\n /**\n * Epics rendering as `unwritten` (SR-04). Advisory, never a failure: the\n * capture point (`## For the record` at PR time) only applies going forward,\n * and a report is more honest showing a visible gap than blocking on one.\n */\n unwritten: UnwrittenRow[];\n /** Program slugs where a non-\"auto\" `theme` was requested but had no toggle to apply to (SR-UP06). */\n themeSkipped: string[];\n}\n\n/**\n * Regenerates every program's show-report.{json,html} in memory and compares\n * against what is on disk; `write` mode also writes the differing files.\n * Mirrors scripts/spell-catalog.ts's parity gate (ARC-012) so a committed\n * generated artifact can never silently drift from its sources.\n */\nexport async function runReportGeneration(options: ReportGenerationOptions): Promise<ReportGenerationResult> {\n const templateHtml = await readFile(options.templatePath, \"utf8\");\n const theme = options.theme ?? \"auto\";\n const drifted: string[] = [];\n const repaired: string[] = [];\n const unwritten: UnwrittenRow[] = [];\n const themeSkipped: string[] = [];\n\n for (const program of options.programs) {\n const generated = await generateReport(options.rootDir, program, templateHtml, theme);\n if (!generated.themeApplied) themeSkipped.push(program.slug);\n const missing = (JSON.parse(generated.json) as ShowReport).sections\n .flatMap((section) => section.rows)\n .filter((row) => row.descriptionState === \"unwritten\")\n .map((row) => row.id);\n if (missing.length > 0) unwritten.push({ slug: program.slug, ids: missing });\n const outAbs = options.outDir\n ? isAbsolute(options.outDir)\n ? options.outDir\n : resolve(options.rootDir, options.outDir)\n : resolve(options.rootDir, dirname(program.planRelPath));\n const outRel = toPosix(relative(options.rootDir, outAbs));\n const outputs: [string, string][] = [\n [`${outRel}/show-report.json`, generated.json],\n [`${outRel}/show-report.html`, generated.html],\n ];\n for (const [relPath, expected] of outputs) {\n const actual = await readOptionalFile(join(options.rootDir, relPath));\n if (actual === null || normalizeLineEndings(actual) !== normalizeLineEndings(expected)) {\n drifted.push(`${relPath} does not match a regeneration from its sources.`);\n if (options.mode === \"write\") {\n await mkdir(outAbs, { recursive: true });\n await writeFile(join(options.rootDir, relPath), expected, \"utf8\");\n repaired.push(relPath);\n }\n }\n }\n }\n\n return { drifted, repaired, unwritten, themeSkipped };\n}\n","/**\n * Builds a ShowReport (schema v2) from a program's real sources -- PLAN.md,\n * OPERATOR-QUEUE.md, docs/verification-ledger.md, and git history (SR-01).\n *\n * Deliberately does not populate `close` (DoD/drift verdicts): both existing\n * programs' close notes are free-form prose with no stable marker to extract\n * a verdict from without risking a misreport on exactly the field a reader\n * would trust most. Omitted rather than guessed, the same discipline the\n * schema's own `descriptionState: \"unwritten\"` convention already applies to\n * a missing `**Report:**` line -- absence stays visible instead of being\n * papered over. A future epic can add real close-note parsing once both\n * programs' close epics agree on a stable marker to key off.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type {\n ShowReport,\n ShowReportCorrections,\n ShowReportNeedsYou,\n ShowReportRow,\n ShowReportSection,\n ShowReportStat,\n ReportCategory,\n} from \"./types.js\";\nimport {\n parseFrontmatter,\n parseEpics,\n parseParkedSection,\n computeCoverageOutcome,\n type ParsedEpic,\n} from \"./plan-parser.js\";\nimport { parseOperatorQueue, type ParsedQueueEntry } from \"./queue-parser.js\";\nimport { parseVerificationLedgerSection, type ParsedLedgerRow } from \"./ledger-parser.js\";\nimport { parseAdrs, countAcceptedAdrsInWindow } from \"./decisions-parser.js\";\nimport { getVersionAtRef, getCloseCommit, getCast } from \"./sources.js\";\n\nexport interface BuildShowReportModelOptions {\n rootDir: string;\n slug: string;\n planRelPath: string;\n queueRelPath: string;\n ledgerProgramName: string;\n compiledAt: string;\n templateVersion: string;\n}\n\nasync function readOptional(path: string): Promise<string | null> {\n try {\n return await readFile(path, \"utf8\");\n } catch {\n return null;\n }\n}\n\n/** `baseline: b0992c1 (main)` -> `b0992c1`. */\nfunction extractSha(baselineField: string): string {\n return baselineField.trim().split(/\\s+/)[0]!;\n}\n\nfunction epicToRow(epic: ParsedEpic): ShowReportRow {\n const category: ReportCategory = (epic.report?.category as ReportCategory | undefined) ?? \"process\";\n const row: ShowReportRow = {\n id: epic.id,\n title: epic.report?.titleOverride ?? epic.title,\n description: epic.report?.description ?? null,\n descriptionState: epic.report ? \"authored\" : \"unwritten\",\n category,\n // Last-cited PR link is closest, in reading order, to the epic's actual\n // completion note (earlier links in a long Done note are often an ADR's\n // own PR or an intermediate fix, not the epic's own shipping PR).\n href: epic.prLinks.length > 0 ? epic.prLinks[epic.prLinks.length - 1] : undefined,\n refs: epic.prLinks,\n };\n return row;\n}\n\nfunction buildSections(epics: ParsedEpic[]): ShowReportSection[] {\n const waveOrder: string[] = [];\n const byWave = new Map<string, ParsedEpic[]>();\n for (const epic of epics) {\n const wave = epic.wave ?? \"Epics\";\n if (!byWave.has(wave)) {\n byWave.set(wave, []);\n waveOrder.push(wave);\n }\n byWave.get(wave)!.push(epic);\n }\n return waveOrder.map((wave, i) => ({\n id: `wave-${i + 1}`,\n title: wave,\n rows: byWave.get(wave)!.map(epicToRow),\n }));\n}\n\nfunction buildStats(\n epics: ParsedEpic[],\n versionSpan: { from: string; to: string } | undefined,\n adrsAccepted: number | null,\n): ShowReportStat[] {\n const done = epics.filter((e) => e.done).length;\n const allPrs = new Set(epics.flatMap((e) => e.prLinks));\n const stats: ShowReportStat[] = [\n { id: \"epics\", value: `${done}/${epics.length}`, label: \"epics shipped\", derived: true },\n { id: \"prs\", value: allPrs.size, label: \"pull requests\", derived: true },\n ];\n if (versionSpan) {\n stats.push({\n id: \"version-span\",\n value: `${versionSpan.from} → ${versionSpan.to}`,\n label: \"version span\",\n derived: true,\n });\n }\n if (adrsAccepted !== null) {\n stats.push({ id: \"adrs\", value: adrsAccepted, label: \"ADRs accepted\", derived: true });\n }\n return stats;\n}\n\nfunction buildNeedsYou(queueEntries: ParsedQueueEntry[]): ShowReportNeedsYou[] {\n return queueEntries\n .filter((e) => !e.done)\n .map((e) => {\n const entry: ShowReportNeedsYou = { id: e.id, title: e.title, reason: e.why ?? e.title };\n if (e.href) entry.href = e.href;\n return entry;\n });\n}\n\n/**\n * Shortens a ledger claim to title length at a word boundary. Cutting at a\n * fixed character offset split words mid-token on the live corpus (\"...own\n * 'Related' line, cor...\"), which reads as a rendering fault rather than an\n * abridgement. Falls back to a hard cut only when the first word alone is\n * already over the limit.\n */\nfunction truncateAtWord(text: string, limit: number): string {\n if (text.length <= limit) return text;\n const window = text.slice(0, limit - 1);\n const lastSpace = window.lastIndexOf(\" \");\n const head = lastSpace > 0 ? window.slice(0, lastSpace) : window;\n return `${head.replace(/[\\s,;:.—-]+$/, \"\")}…`;\n}\n\nfunction buildCorrections(\n ledgerRows: ParsedLedgerRow[],\n programName: string,\n): ShowReportCorrections | undefined {\n if (ledgerRows.length === 0) return undefined;\n const corrected = ledgerRows.filter((r) => r.result === \"corrected\");\n const unverifiable = ledgerRows.filter((r) => r.result === \"unverifiable\").length;\n const highlights: ShowReportRow[] = corrected.slice(0, 3).map((row, i) => ({\n id: `correction-${i + 1}`,\n title: truncateAtWord(row.claim, 80),\n description: row.correction,\n descriptionState: row.correction ? \"authored\" : \"unwritten\",\n category: \"fix\",\n refs: [],\n }));\n return {\n checked: ledgerRows.length,\n corrected: corrected.length,\n unverifiable,\n scopeNote: `${programName} corrections logged in docs/verification-ledger.md`,\n highlights,\n };\n}\n\nexport async function buildShowReportModel(options: BuildShowReportModelOptions): Promise<ShowReport> {\n const planAbsPath = join(options.rootDir, options.planRelPath);\n const queueAbsPath = join(options.rootDir, options.queueRelPath);\n const ledgerRelPath = \"docs/verification-ledger.md\";\n const ledgerAbsPath = join(options.rootDir, ledgerRelPath);\n const decisionsAbsPath = join(options.rootDir, \"DECISIONS.md\");\n\n const [planContent, queueContent, ledgerContent, decisionsContent] = await Promise.all([\n readFile(planAbsPath, \"utf8\"),\n readOptional(queueAbsPath),\n readOptional(ledgerAbsPath),\n readOptional(decisionsAbsPath),\n ]);\n\n const frontmatter = parseFrontmatter(planContent);\n const epics = parseEpics(planContent);\n const parked = parseParkedSection(planContent);\n const outcome = computeCoverageOutcome(planContent);\n const queueEntries = queueContent ? parseOperatorQueue(queueContent) : [];\n const ledgerRows = ledgerContent\n ? parseVerificationLedgerSection(ledgerContent, options.ledgerProgramName)\n : [];\n\n // \"ADRs accepted\" counts DECISIONS.md entries whose Date falls within this\n // program's own [created, completed] window (completed defaults to \"now\"\n // for a still-active program) and whose current Status is Accepted -- a\n // program with no `created` date at all has no window to count against.\n const adrsAccepted =\n decisionsContent && frontmatter.created\n ? countAcceptedAdrsInWindow(\n parseAdrs(decisionsContent),\n frontmatter.created,\n frontmatter.completed ?? options.compiledAt.slice(0, 10),\n )\n : null;\n\n let versionSpan: { from: string; to: string } | undefined;\n const cast = new Map<string, number>();\n if (frontmatter.baseline) {\n const baselineSha = extractSha(frontmatter.baseline);\n // A finished program's close is the commit `main` stood at when its own\n // `completed:` day ended (any path, by landing date) -- see getCloseCommit\n // for why neither \"last commit touching PLAN.md\" nor author dates hold up.\n // An in-progress program has no bound -- \"as of now\" -- except that its\n // own committed report must not count as \"something happened\": the\n // generated outputs next to the plan are excluded from the search.\n const reportDir = options.planRelPath.split(\"/\").slice(0, -1).join(\"/\");\n const generatedOutputs = [\"show-report.json\", \"show-report.html\"].map((f) =>\n reportDir ? `${reportDir}/${f}` : f,\n );\n const closeSha = await getCloseCommit(options.rootDir, frontmatter.completed, generatedOutputs);\n if (closeSha) {\n const [from, to, castMap] = await Promise.all([\n getVersionAtRef(options.rootDir, baselineSha),\n getVersionAtRef(options.rootDir, closeSha),\n // Regeneration-only commits are never part of the cast, for a closed\n // program as much as an active one.\n getCast(options.rootDir, baselineSha, closeSha, generatedOutputs),\n ]);\n if (from && to) versionSpan = { from, to };\n for (const [name, count] of castMap) cast.set(name, count);\n }\n }\n\n const done = epics.filter((e) => e.done).length;\n const title = frontmatter.title ?? options.slug;\n const corrections = buildCorrections(ledgerRows, options.ledgerProgramName);\n\n return {\n schemaVersion: 2,\n program: {\n id: options.slug,\n slug: options.slug,\n title,\n status: frontmatter.status ?? \"unknown\",\n baseline: frontmatter.baseline ?? \"unknown\",\n started: frontmatter.created ?? \"unknown\",\n ...(frontmatter.completed ? { completed: frontmatter.completed } : {}),\n ...(versionSpan ? { versionSpan } : {}),\n },\n masthead: {\n eyebrow: \"Show Report\",\n title,\n dek: `${frontmatter.status ?? \"unknown\"} — ${done} of ${epics.length} epics shipped`,\n },\n stats: buildStats(epics, versionSpan, adrsAccepted),\n ...(outcome ? { outcome } : {}),\n needsYou: buildNeedsYou(queueEntries),\n sections: buildSections(epics),\n ...(corrections ? { corrections } : {}),\n parked,\n cast: [...cast.entries()].map(([name, commits]) => ({ name, commits, source: \"commit-trailer\" as const })),\n provenance: {\n sources: [options.planRelPath, options.queueRelPath, ledgerRelPath],\n compiledAt: options.compiledAt,\n templateVersion: options.templateVersion,\n },\n };\n}\n","/**\n * Parses a program PLAN.md (Become Current / Lessons Hardening shape, and any\n * future program that follows the same conventions) into structured data for\n * show-report's model builder (SR-01).\n *\n * Deliberately does NOT attempt to extract a description from an epic's free\n * -form \"Done:\" prose -- that prose is written for an engineer re-verifying\n * the work, not for a report reader. The data contract (PLAN.md's own\n * \"Authored, once, at the source\" section) instead requires one short,\n * reader-facing `**Report:**` line per epic, authored directly in PLAN.md.\n * This parser only reads that line, plus the mechanically reliable bits\n * (epic ID/title/done-state, PR links already present in the epic's text).\n */\n\nexport interface PlanFrontmatter {\n title?: string;\n status?: string;\n created?: string;\n completed?: string;\n baseline?: string;\n owner?: string;\n executor?: string;\n}\n\nexport interface ParsedEpicReport {\n description: string;\n category: string;\n titleOverride?: string;\n}\n\nexport interface ParsedEpic {\n id: string;\n title: string;\n done: boolean;\n wave: string | null;\n report: ParsedEpicReport | null;\n prLinks: string[];\n block: string;\n}\n\nexport interface ParsedParkedItem {\n title: string;\n reason: string;\n}\n\n/** Reads `key: value` pairs out of a `---\\n...\\n---` frontmatter block. Values are left as raw strings. */\nexport function parseFrontmatter(content: string): PlanFrontmatter {\n const match = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(content);\n if (!match) return {};\n const fields: Record<string, string> = {};\n for (const line of match[1]!.split(/\\r?\\n/)) {\n const kv = /^([a-zA-Z][\\w-]*):\\s*(.*)$/.exec(line);\n if (kv) fields[kv[1]!] = kv[2]!.trim();\n }\n return fields;\n}\n\n// Only the OPEN of an epic bullet -- \"- [x] **BC-01 — \" -- is matched at the\n// line-start position. The title is extracted separately (see below) because\n// it is not always confined to that one line: at least one real epic (LH-11)\n// wraps its bold title across a line break before the closing `.**`, which a\n// single-line regex requiring both ends on one line silently fails to match\n// at all -- not a parse error, just an epic that never gets recognized.\nconst EPIC_START = /^- \\[([ x])\\] \\*\\*([A-Z]+-\\d+) — /gm;\nconst EPIC_TITLE = /^- \\[[ x]\\] \\*\\*[A-Z]+-\\d+ — ([\\s\\S]+?)\\.\\*\\*/;\nconst HEADING_LINE = /^(#{2,3}) (.+)$/gm;\n// `category:` is the last field this parser reads. A trailing `· glyph: <emoji>`\n// is no longer part of the convention (ARC-043) but appears on every `**Report:**`\n// line authored before it was dropped, so the pattern deliberately does not\n// anchor at end-of-line: those lines keep parsing, and the emoji is ignored\n// rather than rendered. Category alone now selects the row's icon.\nconst REPORT_LINE = /\\*\\*Report:\\*\\*\\s*([\\s\\S]*?)\\s*·\\s*category:\\s*([a-z]+)/;\nconst TITLE_OVERRIDE_LINE = /^\\s*title:\\s*(.+)$/m;\nconst PR_LINK = /\\[PR #(\\d+)\\]\\((https:\\/\\/github\\.com\\/[^)\\s]+\\/pull\\/\\d+)\\)/g;\n\ninterface Marker {\n index: number;\n type: \"epic\" | \"h2\" | \"h3\";\n id?: string;\n done?: boolean;\n waveTitle?: string;\n}\n\n/**\n * Splits PLAN.md into top-level epic blocks. A new epic always starts a line\n * with `- [ ]`/`- [x]` followed by `**<PREFIX>-<N> — `; nested detail (Done:\n * sub-bullets, numbered lists, bold lead-ins like \"BC-30 Batch B\") never\n * matches that shape, so it safely stays inside the current block. Position\n * -based (not line-by-line): a title, a Wave heading, or a `## ` section\n * boundary can in principle fall anywhere relative to line breaks, and only\n * comparing character offsets handles that uniformly.\n */\nexport function parseEpics(content: string): ParsedEpic[] {\n const markers: Marker[] = [];\n\n EPIC_START.lastIndex = 0;\n let epicMatch: RegExpExecArray | null;\n while ((epicMatch = EPIC_START.exec(content)) !== null) {\n markers.push({ index: epicMatch.index, type: \"epic\", id: epicMatch[2]!, done: epicMatch[1] === \"x\" });\n }\n\n HEADING_LINE.lastIndex = 0;\n let headingMatch: RegExpExecArray | null;\n while ((headingMatch = HEADING_LINE.exec(content)) !== null) {\n if (headingMatch[1] === \"##\") {\n markers.push({ index: headingMatch.index, type: \"h2\" });\n } else {\n markers.push({ index: headingMatch.index, type: \"h3\", waveTitle: headingMatch[2]!.trim() });\n }\n }\n\n markers.sort((a, b) => a.index - b.index);\n\n const epics: ParsedEpic[] = [];\n let currentWave: string | null = null;\n\n for (let i = 0; i < markers.length; i += 1) {\n const marker = markers[i]!;\n if (marker.type === \"h2\") {\n currentWave = null;\n continue;\n }\n if (marker.type === \"h3\") {\n currentWave = marker.waveTitle!;\n continue;\n }\n // marker.type === \"epic\": block runs to the next marker of any kind, or EOF.\n const blockEnd = i + 1 < markers.length ? markers[i + 1]!.index : content.length;\n const block = content.slice(marker.index, blockEnd);\n const titleMatch = EPIC_TITLE.exec(block);\n const reportMatch = REPORT_LINE.exec(block);\n const titleOverrideMatch = TITLE_OVERRIDE_LINE.exec(block);\n const prLinks = [...new Set([...block.matchAll(PR_LINK)].map((m) => m[2]!))];\n\n epics.push({\n id: marker.id!,\n // A title that still fails to match (e.g. a malformed bullet with no\n // closing `.**` at all) falls back to the raw id rather than throwing --\n // an epic missing from the report is far more visible, and therefore\n // more likely to get fixed, than a crashed report generator.\n title: titleMatch ? titleMatch[1]!.replace(/\\s+/g, \" \").trim() : marker.id!,\n done: marker.done!,\n wave: currentWave,\n report: reportMatch\n ? {\n description: reportMatch[1]!.replace(/\\s+/g, \" \").trim(),\n category: reportMatch[2]!,\n titleOverride: titleOverrideMatch ? titleOverrideMatch[1]!.trim() : undefined,\n }\n : null,\n prLinks,\n block,\n });\n }\n\n return epics;\n}\n\n/**\n * Extracts the body text of a `## <headingPrefix>...` section, up to (but\n * not including) the next `## ` heading or the end of the document.\n *\n * Deliberately line-array based rather than a single lookahead regex: a\n * lookahead built from `\\r?\\n?$` under the `m` flag matches at the END OF\n * EVERY LINE (not just end-of-string, since `m` makes `$` mean \"end of any\n * line\"), so a lazy `[\\s\\S]*?` stops after the very first blank line inside\n * the section instead of running to the next `## ` heading. Splitting into\n * lines and searching by index sidesteps that footgun entirely.\n */\nfunction extractSection(content: string, headingPrefix: string): string | null {\n const lines = content.split(/\\r?\\n/);\n const startIndex = lines.findIndex((line) => line.startsWith(headingPrefix));\n if (startIndex === -1) return null;\n let endIndex = lines.length;\n for (let i = startIndex + 1; i < lines.length; i += 1) {\n if (lines[i]!.startsWith(\"## \")) {\n endIndex = i;\n break;\n }\n }\n return lines.slice(startIndex + 1, endIndex).join(\"\\n\");\n}\n\n/**\n * Parked sections come in two known shapes across the two existing programs:\n * Lessons Hardening's bullet list (`- **Title** — reason`) and Become\n * Current's three-column table (`| Item | Source | reason |`). Both are\n * handled; an unrecognized shape degrades to an empty list rather than\n * throwing, since this field is optional in the schema.\n */\nexport function parseParkedSection(content: string): ParsedParkedItem[] {\n const body = extractSection(content, \"## Parked — Needs Operator\");\n if (body === null) return [];\n\n const tableRows = [...body.matchAll(/^\\|(?!---)([^|]+)\\|([^|]+)\\|([^|]+)\\|\\s*$/gm)];\n if (tableRows.length > 1) {\n // First matched row is the header (`| Item | Source | ... |`); skip it.\n return tableRows.slice(1).map((m) => ({\n title: stripMarkdown(m[1]!.trim()),\n reason: stripMarkdown(m[3]!.trim()),\n }));\n }\n\n // Bullets are unwrapped to one string each BEFORE matching. Matching the raw\n // text line-by-line under `m` lost data two ways on the live corpus, both\n // silently: a reason that wrapped to a second line was cut at the wrap\n // (\"fully solved (git-conventions.md → Content-Verified\", losing \"Branch\n // Deletion)\"), and an item whose `—` fell on the second line never matched at\n // all, dropping it from the report entirely (Lessons Hardening's\n // \"Mechanizing spell-check-drift's judgment-based detectors\" -- 8 of 9 items\n // rendered, with nothing to indicate one was missing).\n return unwrapBullets(body).flatMap((bullet) => {\n const match = /^\\*\\*(.+?)\\*\\*(.*?)\\s*—\\s*(.+)$/.exec(bullet);\n if (!match) return [];\n // Text between the bold lead-in and the `—` belongs to the title (LH's\n // \"…detectors **beyond** LH-07/LH-08's two mechanical inputs\"), except a\n // parenthetical, which is a source note rather than part of the name.\n const tail = match[2]!.replace(/\\([^)]*\\)/g, \"\").replace(/\\s+/g, \" \").trim();\n const title = tail ? `${match[1]!.trim()} ${tail}` : match[1]!.trim();\n return [{ title: stripMarkdown(title), reason: stripMarkdown(match[3]!.trim()) }];\n });\n}\n\n/**\n * Collapses a markdown bullet list into one string per bullet, joining each\n * bullet's wrapped continuation lines. A bullet ends at the next `- ` at\n * column 0, at a blank line, or at any other unindented line.\n */\nfunction unwrapBullets(body: string): string[] {\n const bullets: string[] = [];\n let current: string[] | null = null;\n const flush = (): void => {\n if (current) bullets.push(current.join(\" \").replace(/\\s+/g, \" \").trim());\n current = null;\n };\n for (const line of body.split(/\\r?\\n/)) {\n if (/^- /.test(line)) {\n flush();\n current = [line.slice(2).trim()];\n } else if (current !== null) {\n if (/^\\s+\\S/.test(line)) current.push(line.trim());\n else flush();\n }\n }\n flush();\n return bullets;\n}\n\n/**\n * Generic outcome summary from a \"## Coverage Map\" section: counts data rows\n * (skipping the header/separator rows every markdown table has) and how many\n * cells say \"Parked\", producing the same shape as the schema's own example\n * (\"12 of 12 patterns dispositioned — 9 mechanized, 3 parked\") regardless of\n * which program's own column layout produced them.\n */\nexport function computeCoverageOutcome(content: string): string | undefined {\n const body = extractSection(content, \"## Coverage Map\");\n if (body === null) return undefined;\n const rows = [...body.matchAll(/^\\|(.+)\\|\\s*$/gm)].filter((m) => !/^-+\\s*\\|/.test(m[1]!.trim()));\n if (rows.length < 2) return undefined;\n const dataRows = rows.slice(1); // first row is the header\n // Lessons Hardening's own Coverage Map says \"**Park**\"; Become Current's\n // says \"**Parked**\" -- same disposition, two spellings across the two\n // existing programs (an instance of the exact P-something inconsistency\n // this program's own corrections inventory is full of).\n const parkedCount = dataRows.filter((m) => /\\*\\*Park(?:ed)?\\*\\*/i.test(m[1]!)).length;\n const total = dataRows.length;\n const dispositioned = total - parkedCount;\n return `${total} of ${total} items dispositioned — ${dispositioned} routed to an epic, ${parkedCount} parked`;\n}\n\nfunction stripMarkdown(text: string): string {\n return text\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n .replace(/\\*\\*/g, \"\")\n .replace(/`/g, \"\")\n .trim();\n}\n","/**\n * Parses an OPERATOR-QUEUE.md (Become Current / Lessons Hardening / Show\n * Report shape) into structured entries for show-report's `needsYou` block\n * (SR-01). The loop appends `## Q-NNN — Title` sections, each ending in a\n * `- **Status:** [ ]`/`[x]` line -- that checkbox is the only mechanically\n * reliable \"is this still open\" signal; everything else in an entry is free\n * -form prose written for the operator, not for parsing.\n */\n\nconst LINK = /\\[[^\\]]+\\]\\((https?:\\/\\/[^)\\s]+)\\)/;\n\nexport interface ParsedQueueEntry {\n id: string;\n title: string;\n done: boolean;\n why: string | null;\n href?: string;\n}\n\nexport function parseOperatorQueue(content: string): ParsedQueueEntry[] {\n const sections = content.split(/^## /m).slice(1); // drop the doc's own leading title/preamble\n const entries: ParsedQueueEntry[] = [];\n\n for (const section of sections) {\n const headingMatch = /^(Q-\\d+)\\s*—\\s*(.+?)\\r?\\n/.exec(section);\n if (!headingMatch) continue; // not a Q-NNN section (shouldn't occur after the split, but stay defensive)\n const id = headingMatch[1]!;\n const title = headingMatch[2]!.trim();\n\n const statusMatch = /^-\\s*\\*\\*Status:\\*\\*\\s*\\[([ x])\\]/m.exec(section);\n const done = statusMatch ? statusMatch[1] === \"x\" : false;\n\n const whyMatch = /^-\\s*\\*\\*Why:\\*\\*\\s*([\\s\\S]*?)(?=\\r?\\n-\\s*\\*\\*|\\r?\\n\\r?\\n|$)/m.exec(section);\n const why = whyMatch ? whyMatch[1]!.replace(/\\s+/g, \" \").trim() : null;\n\n const linkMatch = LINK.exec(section);\n entries.push({ id, title, done, why, href: linkMatch ? linkMatch[1] : undefined });\n }\n\n return entries;\n}\n","/**\n * Parses a program's own dated section out of docs/verification-ledger.md\n * (SR-01). Sections are matched by a heading that names the program (e.g.\n * \"## 2026-09-02 — Lessons Hardening corrections (LH-02 → LH-12)\") -- a\n * program can have more than one dated section (Become Current's ledger\n * entry above is one example among what may become several over a program's\n * life), so this returns every row across all matching sections combined.\n */\n\nexport type LedgerResult = \"confirmed\" | \"corrected\" | \"unverifiable\";\n\nexport interface ParsedLedgerRow {\n claim: string;\n result: LedgerResult;\n correction: string | null;\n}\n\n/** `programNameWords` should be how the program is named in ledger headings, e.g. \"Lessons Hardening\". */\nexport function parseVerificationLedgerSection(content: string, programNameWords: string): ParsedLedgerRow[] {\n const sections = content.split(/^## /m).slice(1);\n const rows: ParsedLedgerRow[] = [];\n\n for (const section of sections) {\n const headingLine = section.slice(0, section.indexOf(\"\\n\"));\n if (!headingLine.toLowerCase().includes(programNameWords.toLowerCase())) continue;\n\n const tableRows = [...section.matchAll(/^\\|([^|]+)\\|([^|]+)\\|([^|]+)\\|([^|]+)\\|\\s*$/gm)];\n for (const m of tableRows.slice(1)) {\n // first match is the header row\n const claimCell = m[1]!.trim();\n const resultCell = m[3]!.trim().toLowerCase();\n const correctionCell = m[4]!.trim();\n if (claimCell === \"---\" || claimCell.startsWith(\"---\")) continue;\n if (resultCell !== \"confirmed\" && resultCell !== \"corrected\" && resultCell !== \"unverifiable\") continue;\n rows.push({\n claim: stripMarkdown(claimCell),\n result: resultCell,\n correction: correctionCell === \"—\" ? null : stripMarkdown(correctionCell),\n });\n }\n }\n\n return rows;\n}\n\nfunction stripMarkdown(text: string): string {\n return text\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n .replace(/\\*\\*/g, \"\")\n .replace(/`/g, \"\")\n .trim();\n}\n","/**\n * Parses DECISIONS.md for ADRs (SR-01), specifically to count how many\n * reached `Accepted` status within a program's active window -- the exact\n * stat this program's own research flagged as easy to get wrong by hand:\n * Become Current's hand-built ledger said \"4 ADRs accepted\" where a\n * mechanical recount found 5 (ARC-029/037/038/039/040). `**Status:**` lines\n * sometimes carry a parenthetical after the word itself (e.g. \"Accepted\n * (2026-09-01, operator accept call ...)\"); only the leading status word is\n * matched, so that detail doesn't need parsing.\n */\n\nexport interface ParsedAdr {\n id: string;\n date: string | null;\n status: string | null;\n}\n\nexport function parseAdrs(decisionsContent: string): ParsedAdr[] {\n const headingPattern = /^## ARC-(\\d{3})\\b/gm;\n const positions: { id: string; index: number }[] = [];\n let match: RegExpExecArray | null;\n while ((match = headingPattern.exec(decisionsContent)) !== null) {\n positions.push({ id: `ARC-${match[1]!}`, index: match.index });\n }\n\n return positions.map(({ id, index }, i) => {\n const end = i + 1 < positions.length ? positions[i + 1]!.index : decisionsContent.length;\n const section = decisionsContent.slice(index, end);\n const dateMatch = /^\\*\\*Date:\\*\\*\\s*(\\d{4}-\\d{2}-\\d{2})/m.exec(section);\n const statusMatch = /^\\*\\*Status:\\*\\*\\s*(\\S+)/m.exec(section);\n return { id, date: dateMatch ? dateMatch[1]! : null, status: statusMatch ? statusMatch[1]! : null };\n });\n}\n\n/** Counts ADRs whose `**Date:**` falls within `[fromDate, toDate]` (inclusive, ISO strings) and whose current Status is Accepted. */\nexport function countAcceptedAdrsInWindow(adrs: ParsedAdr[], fromDate: string, toDate: string): number {\n return adrs.filter(\n (adr) => adr.status === \"Accepted\" && adr.date !== null && adr.date >= fromDate && adr.date <= toDate,\n ).length;\n}\n","/**\n * Git-derived data for show-report (SR-01): package.json version at a given\n * ref, and commit-trailer-based cast attribution. Deliberately network-free\n * -- everything here reads from the local git history already on disk, so\n * `--check` (SR-02) never needs a network call. An optional `--refresh` path\n * that snapshots `gh pr list` data is out of SR-01's scope; `refs`/`href` are\n * populated instead from PR links already present in each epic's own\n * `**Report:**`-adjacent text (see plan-parser.ts), which needs no network\n * either.\n */\n\nimport { runGit } from \"../git.js\";\n\nasync function runGitTextOrNull(cwd: string, args: string[]): Promise<string | null> {\n try {\n const { stdout } = await runGit(cwd, args, { commandClass: \"read\" });\n return stdout.trim();\n } catch {\n return null;\n }\n}\n\n/**\n * True for a shallow clone (`--depth`). In one, getCloseCommit /\n * getVersionAtRef / getCast cannot see the history the version span and cast\n * derive from, so they silently omit those fields -- and a parity check then\n * reports the omission as \"drift\". Callers that gate on parity must treat a\n * shallow clone as \"cannot verify\", never as drifted. Found the hard way:\n * publish.yml's default fetch-depth: 1 checkout failed the golden test that\n * ci.yml's full-history checkout had just passed, and v0.34.3 never reached\n * npm because of it.\n */\nexport async function isShallowRepository(cwd: string): Promise<boolean> {\n const out = await runGitTextOrNull(cwd, [\"rev-parse\", \"--is-shallow-repository\"]);\n return out === \"true\";\n}\n\n/** Reads `package.json`'s `version` field as it existed at `ref`, without touching the working tree. */\nexport async function getVersionAtRef(cwd: string, ref: string): Promise<string | null> {\n const content = await runGitTextOrNull(cwd, [\"show\", `${ref}:package.json`]);\n if (content === null) return null;\n try {\n const parsed: unknown = JSON.parse(content);\n const version = (parsed as { version?: unknown }).version;\n return typeof version === \"string\" ? version : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The commit `main` stood at when a program's `completed` day ended: the most\n * recent commit whose COMMITTER date falls on or before that day, ignoring the\n * program's own report files. Without a `completed` date (a program still in\n * progress) the close is the most recent commit that touched anything else --\n * \"as of now\".\n *\n * Measured, not assumed, against both finished programs: Become Current's last\n * PLAN.md-touching commit on its completed day sat at 0.33.0, while two\n * version bumps landed later that same day without touching the plan -- the\n * human record and the hand ledger both say 0.33.2, and only a date-based\n * definition reproduces it (and Lessons Hardening's 0.34.1). Anchoring to\n * PLAN.md at all was the wrong idea: a finished plan gets edited again\n * (SR-01's own backfill did it), and a program's version moves on commits that\n * never touch it.\n *\n * Committer date, not author date, because it is the LANDING date: this repo\n * rebase-merges every PR, so it is the merge time and monotonic along the log,\n * while author dates are not (a PR authored at 03:19 can land above one\n * authored at 06:46). `%cs` renders it in the commit's own recorded offset,\n * so the calendar-day comparison gives the same answer on the operator's\n * machine and on a UTC runner, where a `--until=<instant>` filter would not.\n * Author date was tried for R-CLOSE and rejected: on upstream-intake-2026-09 it\n * moved a published report (cast 55 -> 56), because a commit authored on the\n * `completed:` day landed the next day. A rebase rewrites the committer date,\n * so a PR authored on the completion day and merged after midnight can still\n * make a report differ between its branch and main (PR #305's 56-vs-55 is\n * INFERRED to be that, not reproduced); that stays open in TODO.md.\n *\n * The generated outputs (`generatedOutputs`, repository-relative) are excluded\n * from the search in both branches: a commit that touched only them, a\n * regeneration, is never the close. For an in-progress program without that,\n * committing a regenerated report moved the close onto the very commit that\n * carried the report, whose own `Agent:` trailer the cast then counted, so a\n * freshly committed report was stale the instant it landed (TODO.md, \"the\n * golden-parity gate has no notion of an in-progress program\"). For a\n * completed program the exclusion changes which commit is the close but, today,\n * nothing in the report: getCast already ignores report-only commits and a\n * report-only commit never moves the version. It does NOT help a commit that\n * regenerates the report AND edits something else (that commit is still its own\n * close and joins the cast; PR #304's `b43df06` was one) -- the standing rule\n * that a regeneration is its own report-only commit is what makes it converge.\n * Anchoring on paths rather than on a recorded SHA survives this repo's\n * rebase-merges, which rewrite every SHA on the way to main.\n */\nfunction excludeOutputs(generatedOutputs: string[]): string[] {\n return generatedOutputs.length === 0 ? [] : [\"--\", \".\", ...generatedOutputs.map((p) => `:(exclude)${p}`)];\n}\n\nexport async function getCloseCommit(\n cwd: string,\n completedDate?: string,\n generatedOutputs: string[] = [],\n): Promise<string | null> {\n if (!completedDate) {\n const args =\n generatedOutputs.length === 0\n ? [\"rev-parse\", \"HEAD\"]\n : [\"log\", \"-1\", \"--format=%H\", \"HEAD\", ...excludeOutputs(generatedOutputs)];\n const head = await runGitTextOrNull(cwd, args);\n return head === null || head === \"\" ? null : head;\n }\n const log = await runGitTextOrNull(cwd, [\"log\", \"--format=%H%x09%cs\", \"HEAD\", ...excludeOutputs(generatedOutputs)]);\n if (log === null || log === \"\") return null;\n for (const line of log.split(\"\\n\")) {\n const [sha, landed] = line.split(\"\\t\");\n if (sha && landed && landed <= completedDate) return sha;\n }\n return null;\n}\n\n/**\n * Cast list for the `fromRef..toRef` commit range: one entry per distinct\n * contributor name, counted by commits. Name resolution prefers a commit's\n * `Persona:` trailer (a roster identity) and falls back to its `Agent:`\n * trailer (a runtime/tool name) when no persona was assigned that commit;\n * a commit with neither trailer (ordinary human commits) is not counted --\n * this list is specifically the self-reported AI cast, per the schema's\n * `source: \"commit-trailer\"` / \"self-reported\" note, not full authorship.\n *\n * `generatedOutputs` (repository-relative) are the program's own report\n * files: a commit that touched nothing but them -- a regeneration -- is not\n * part of the cast at all, whatever trailer it carries. Anchoring only the\n * close (getCloseCommit) was not enough: it kept a regeneration from counting\n * itself while it was the tip, but the moment real content landed after it,\n * the earlier regeneration fell inside `fromRef..toRef` and was counted after\n * all -- caught by the regression in test/report-cli.test.ts.\n */\nexport async function getCast(\n cwd: string,\n fromRef: string,\n toRef: string,\n generatedOutputs: string[] = [],\n): Promise<Map<string, number>> {\n const ETX = \"\\x03\";\n const log = await runGitTextOrNull(cwd, [\n \"log\",\n \"--no-merges\",\n `--format=%B${ETX}`,\n `${fromRef}..${toRef}`,\n ...(generatedOutputs.length > 0\n ? [\"--\", \".\", ...generatedOutputs.map((p) => `:(exclude)${p}`)]\n : []),\n ]);\n const cast = new Map<string, number>();\n if (log === null || log === \"\") return cast;\n\n for (const body of log.split(ETX)) {\n const personaMatch = /^Persona:\\s*(.+)$/m.exec(body);\n const agentMatch = /^Agent:\\s*(.+)$/m.exec(body);\n const name = personaMatch?.[1]?.trim() || agentMatch?.[1]?.trim();\n if (!name) continue;\n cast.set(name, (cast.get(name) ?? 0) + 1);\n }\n return cast;\n}\n","/**\n * Renders a ShowReport into HTML through a Mustache template (SR-02).\n *\n * Mustache is logic-less, so anything the template cannot compute -- a\n * category's display label, whether a row has a link, the safe HTML form of\n * a description -- is precomputed here into a view model first. Every plain\n * field goes through Mustache's default `{{x}}` HTML escaping; the ONLY\n * triple-mustache (`{{{x}}}`) field the template may use is `descriptionHtml`,\n * which `inlineMarkupToHtml` produces by escaping everything and then\n * re-admitting exactly two constructs: `` `code` `` and `[text](https-url)`.\n */\n\nimport Mustache from \"mustache\";\nimport type { ReportCategory, ShowReport, ShowReportRow } from \"./types.js\";\n\nexport const CATEGORY_LABELS: Record<ReportCategory, string> = {\n spell: \"New Spell\",\n feature: \"New Feature\",\n governance: \"Governance\",\n decision: \"Decision (ADR)\",\n fix: \"Bug Fix\",\n process: \"Process\",\n docs: \"Docs\",\n platform: \"Platform\",\n};\n\nconst CATEGORY_ORDER: ReportCategory[] = [\n \"spell\",\n \"feature\",\n \"governance\",\n \"decision\",\n \"fix\",\n \"process\",\n \"docs\",\n \"platform\",\n];\n\nexport function escapeHtml(text: string): string {\n return text\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\n/**\n * Escape first, then re-admit only `code` spans and `[text](http(s)://...)`\n * links. Because `\"` and `'` are already entity-escaped by the time the link\n * regex runs, a URL cannot break out of the `href` attribute, and `<`/`>` in\n * link text cannot open a tag -- the two constructs are the whole allowlist.\n */\nexport function inlineMarkupToHtml(text: string): string {\n const escaped = escapeHtml(text);\n const withLinks = escaped.replace(\n /\\[([^\\]]+)\\]\\((https?:\\/\\/[^)\\s]+)\\)/g,\n '<a href=\"$2\">$1</a>',\n );\n return withLinks.replace(/`([^`]+)`/g, \"<code>$1</code>\");\n}\n\nexport interface ShowReportRowView extends ShowReportRow {\n categoryLabel: string;\n descriptionHtml: string | null;\n isUnwritten: boolean;\n hasHref: boolean;\n}\n\nfunction toRowView(row: ShowReportRow): ShowReportRowView {\n return {\n ...row,\n categoryLabel: CATEGORY_LABELS[row.category] ?? row.category,\n descriptionHtml: row.description === null ? null : inlineMarkupToHtml(row.description),\n isUnwritten: row.descriptionState === \"unwritten\",\n hasHref: typeof row.href === \"string\" && row.href.length > 0,\n };\n}\n\nexport function buildShowReportView(model: ShowReport): Record<string, unknown> {\n const corrections = model.corrections\n ? { ...model.corrections, highlights: model.corrections.highlights.map(toRowView) }\n : undefined;\n return {\n ...model,\n // Same reason as parkedIsSingular: the template needs a flag to write \"commit\"\n // rather than \"commits\" for a contributor with exactly one.\n cast: model.cast.map((member) => ({ ...member, isSingular: member.commits === 1 })),\n sections: model.sections.map((section) => ({\n ...section,\n hasNote: typeof section.note === \"string\" && section.note.length > 0,\n // Mustache cannot count a list, so any \"N items\" label in a template needs\n // the count precomputed here. Added for the compiled arcane-ui template,\n // which asks for a per-section row count and a parked total (SR-06).\n rowCount: section.rows.length,\n rows: section.rows.map(toRowView),\n })),\n corrections,\n hasCorrections: corrections !== undefined,\n hasParked: model.parked.length > 0,\n parkedCount: model.parked.length,\n // Mustache has no conditional on a VALUE -- only truthiness of a name -- so a\n // template cannot choose \"item\" vs \"items\" from the number itself. It needs a\n // flag, and only this side can derive one. Without it a one-item program reads\n // \"1 items\", which is what the compiled template shipped.\n parkedIsSingular: model.parked.length === 1,\n hasCast: model.cast.length > 0,\n hasOutcome: typeof model.outcome === \"string\" && model.outcome.length > 0,\n hasVersionSpan: model.program.versionSpan !== undefined,\n needsYouCount: model.needsYou.length,\n compiledAtDate: model.provenance.compiledAt.slice(0, 10),\n legend: CATEGORY_ORDER.map((key) => ({ key, label: CATEGORY_LABELS[key] })),\n sourcesJoined: model.provenance.sources.join(\", \"),\n };\n}\n\nexport function renderShowReport(model: ShowReport, templateHtml: string): string {\n // Mustache's built-in escaper also entity-encodes `/`, `=` and backticks\n // (`1/1` -> `1/1`), which is safe but needlessly ugly in the shipped\n // source. The standard five-character set above is the sufficient HTML\n // escape for text content and quoted attributes, so it is used for both\n // the inline-markup path and every `{{x}}` tag -- one escaper, one rule.\n // Passed per call rather than assigned to `Mustache.escape` so this module\n // never mutates a shared global. Mustache hands the RAW interpolated value\n // to the escaper -- a number for `{{needsYouCount}}`, `{{commits}}` or a\n // numeric stat -- so coerce to string first, as mustache's own escaper does.\n return Mustache.render(templateHtml, buildShowReportView(model), undefined, {\n escape: (value: unknown) => escapeHtml(String(value)),\n });\n}\n","/**\n * Applies a build-time theme selection to a rendered Show Report page (SR-UP06,\n * closes arcane#271) by moving the `checked` attribute in the vendored\n * template's theme toggle (arcane-ui ARCUI-021, three radios sharing\n * `name=\"arcane-report-theme\"`, ids `arcane-report-theme-<mode>`) onto the\n * requested radio.\n *\n * \"auto\" is a deliberate no-op: arcane-ui ships the template with\n * `arcane-report-theme-auto` pre-checked and no CSS override tied to it, so\n * the unmodified template already reproduces today's `prefers-color-scheme`\n * behaviour byte-for-byte (OPERATOR-QUEUE.md Q-006's requirement). This\n * never touches the html when `theme` is \"auto\" -- including the default\n * codepath used by every existing `show-report.html` golden, which must\n * stay unaffected.\n */\n\nexport type ReportTheme = \"auto\" | \"light\" | \"dark\";\n\nexport const REPORT_THEMES: readonly ReportTheme[] = [\"auto\", \"light\", \"dark\"];\n\nexport interface ApplyReportThemeResult {\n html: string;\n /** False when `theme` isn't \"auto\" but the template has no matching toggle to set (older template). */\n applied: boolean;\n}\n\nfunction radioTagPattern(id: ReportTheme): RegExp {\n return new RegExp(`<input\\\\b[^>]*\\\\bid=\"arcane-report-theme-${id}\"[^>]*?/?>`);\n}\n\nfunction withoutChecked(tag: string): string {\n return tag.replace(/\\s+checked(?:=\"[^\"]*\")?/, \"\");\n}\n\nfunction withChecked(tag: string): string {\n return /\\bchecked\\b/.test(tag) ? tag : tag.replace(/(\\/?>)$/, \" checked$1\");\n}\n\nexport function applyReportTheme(html: string, theme: ReportTheme): ApplyReportThemeResult {\n if (theme === \"auto\") return { html, applied: true };\n if (!radioTagPattern(theme).test(html)) return { html, applied: false };\n\n let result = html;\n // Move `checked` onto the target radio: strip it from every other radio in the\n // group first (arcane-ui ships \"auto\" pre-checked by default), so exactly one\n // radio ends up checked rather than leaving two marked at once.\n for (const id of REPORT_THEMES) {\n const match = radioTagPattern(id).exec(result);\n if (!match) continue;\n const original = match[0];\n const replaced = id === theme ? withChecked(original) : withoutChecked(original);\n if (replaced !== original) {\n result = result.slice(0, match.index) + replaced + result.slice(match.index + original.length);\n }\n }\n return { html: result, applied: true };\n}\n"],"mappings":"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AAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;A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","names":["createRequire","dirname","join","select","confirm","input","dirname","join","chalk","readFile","writeFile","path","manifestPath","mkdir","readdir","readFile","rm","rmdir","writeFile","path","path","readFile","readdir","rmdir","client","mkdir","writeFile","rm","resolve","label","mkdir","writeFile","copyFile","dirname","join","readFile","readdir","access","join","isRecord","path","client","parseYaml","join","readFile","join","readFile","parseYaml","access","writeFile","mkdir","readdir","readFile","join","homedir","readFile","writeFile","rm","path","path","readFile","writeFile","rm","readFile","join","readdir","homedir","mkdir","writeFile","dirname","label","r","join","mkdir","writeFile","copyFile","select","input","readdir","writeFile","join","resolve","stdout","select","input","manifestPath","join","readdir","writeFile","mkdir","readFile","writeFile","access","rm","readdir","join","resolve","dirname","resolve","dirname","join","mkdir","writeFile","join","resolve","readdir","readFile","access","rm","join","chalk","confirm","select","profileComponents","input","dirname","client","readFile","writeFile","join","join","readFile","writeFile","printDryRun","dirname","join","readFile","writeFile","i","j","join","join","checkbox","checkbox","join","dirname","fileHashes","readFile","writeFile","dirname","readFile","writeFile","mkdir","dirname","join","homedir","require","pkg","label","dirname","rm","dirname","join","confirm","confirm","rm","join","dirname","access","readdir","readFile","mkdir","join","dirname","readFile","readdir","join","execFile","resolve","path","readdir","readFile","join","FRONTMATTER_PATTERN","isRecord","label","join","readdir","manifestPath","access","readFile","mkdir","dirname","readFile","readdir","readFile","join","relative","readFile","readdir","join","relative","findings","readFile","basename","resolve","input","basename","resolve","input","createHash","execFile","resolve","readFileSync","existsSync","dirname","join","createRequire","existsSync","readFileSync","dirname","join","access","join","mkdir","readdir","readFile","writeFile","basename","dirname","join","relative","resolve","readFile","join","stripMarkdown","path","readFile","join","path","readFile","join","dirname","readdir","resolve","relative","basename","normalizeLineEndings","program","mkdir","writeFile","join","access","result","path","require","createRequire","join","dirname"]}
|