saasaloy 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/commands/add.ts","../src/lib/applier.ts","../src/lib/exit.ts","../src/lib/collisions.ts","../src/lib/fs-utils.ts","../src/lib/manifest.ts","../src/lib/schema.ts","../src/lib/patch/chained-route.ts","../src/lib/patch/ts-ast.ts","../src/lib/patch/const-array.ts","../src/lib/patch/diff.ts","../src/lib/patch/jsonc.ts","../src/lib/patch/pkg-json.ts","../src/lib/patch/pkg-json-script.ts","../src/lib/patch/ts-module.ts","../src/lib/patch/index.ts","../src/lib/saasaloy-config.ts","../src/lib/semver.ts","../src/lib/cli-requires.ts","../src/lib/conflicts.ts","../src/lib/design.ts","../src/lib/dev-vars.ts","../src/lib/diff.ts","../src/lib/lock.ts","../src/lib/pkg-json.ts","../src/lib/project.ts","../src/lib/registry.ts","../src/lib/requires.ts","../src/lib/resolve.ts","../src/lib/tui.ts","../src/lib/ui-blocks.ts","../src/lib/usage.ts","../src/version.ts","../src/commands/descriptions.ts","../src/commands/doctor.ts","../src/lib/base.ts","../src/lib/scaffold.ts","../package.json","../src/lib/doctor.ts","../src/commands/env.ts","../src/lib/env-vars.ts","../src/lib/gitignore.ts","../src/commands/init.ts","../src/lib/logger.ts","../src/commands/list.ts","../src/commands/new.ts","../src/lib/new-module.ts","../src/commands/outdated.ts","../src/lib/updater.ts","../src/lib/remover.ts","../src/commands/remove.ts","../src/commands/update.ts","../src/lib/merge-plan.ts","../src/commands/index.ts","../src/index.ts"],"sourcesContent":["import {\n cancel,\n isCancel as clackIsCancel,\n select as clackSelect,\n} from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport type { CommandRegistry } from \"./commands/index.js\";\nimport { COMMANDS } from \"./commands/index.js\";\nimport { EXIT_FAILURE, EXIT_OK, EXIT_REFUSED } from \"./lib/exit.js\";\nimport { isInteractive } from \"./lib/tui.js\";\nimport { readVersion } from \"./version.js\";\n\n// Re-exported so `saasaloy --version` and this module's tests keep one import site; the\n// reader itself lives in version.ts, which commands/add.ts also imports (#50).\nexport { readVersion } from \"./version.js\";\n\n// Argument parsing and dispatch. Kept out of index.ts — which only bootstraps this and\n// maps the resolved code onto process.exit — so a test can import it without running the\n// CLI. Everything the picker needs from the outside world (the registry, the prompt) is a\n// defaulted parameter, which is the test seam: this package mocks no modules anywhere.\n\n/** The slice of clack's `select` the picker uses, narrowed to string-valued options. */\nexport type SelectPrompt = (opts: {\n message: string;\n options: { value: string; label: string; hint?: string }[];\n}) => Promise<string | symbol>;\n\nexport interface CliDeps {\n registry: CommandRegistry;\n select: SelectPrompt;\n /** clack's cancel sentinel is module-private, so recognising it is injectable too. */\n isCancel: (value: unknown) => value is symbol;\n}\n\nexport function printHelp(registry: CommandRegistry): void {\n console.log(\n `${pc.bold(\"saasaloy\")} ${pc.dim(\"— composable SaaS accelerator for Cloudflare\")}\\n`\n );\n console.log(\n `${pc.bold(\"Usage:\")} saasaloy ${pc.cyan(\"<command>\")} [options]\\n`\n );\n console.log(pc.bold(\"Commands:\"));\n for (const [name, command] of Object.entries(registry)) {\n console.log(` ${pc.cyan(name.padEnd(8))} ${pc.dim(command.describe)}`);\n }\n console.log(`\\n${pc.bold(\"Options:\")}`);\n console.log(\n ` ${pc.cyan(\"-h, --help\".padEnd(14))} ${pc.dim(\"show this help, or a command's own usage\")}`\n );\n console.log(\n ` ${pc.cyan(\"-v, --version\".padEnd(14))} ${pc.dim(\"print the installed saasaloy version\")}`\n );\n console.log(`\\n${pc.bold(\"Exit codes:\")}`);\n console.log(\n ` ${pc.cyan(\"0\".padEnd(14))} ${pc.dim(\"done, or you answered no to the confirm\")}`\n );\n console.log(\n ` ${pc.cyan(\"1\".padEnd(14))} ${pc.dim(\"something failed, or you cancelled\")}`\n );\n console.log(\n ` ${pc.cyan(\"2\".padEnd(14))} ${pc.dim(\"saasaloy refused: bad usage, a conflict, an unmet requirement\")}`\n );\n console.log(\n `\\n${pc.dim(\"Set SAASALOY_DEBUG=1 to print the full cause chain behind a failure.\")}`\n );\n}\n\n// Bare invocation on a terminal: show what the tool can do and run the choice, rather\n// than making a newcomer read a list, quit, and retype. Options are mapped from the\n// registry, never listed by hand, and each carries its `describe` as a hint — which is\n// why no help is printed above the picker; that would render the same list twice.\n// No intro()/outro() here either: the chosen command opens its own, so the handoff reads\n// as one continuous clack rail instead of two stacked boxes.\nasync function pickCommand(deps: CliDeps): Promise<number> {\n const { registry, select, isCancel } = deps;\n const picked = await select({\n message: \"What would you like to do?\",\n options: Object.entries(registry).map(([name, command]) => ({\n value: name,\n label: name,\n hint: command.describe,\n })),\n });\n if (isCancel(picked)) {\n cancel(\"cancelled\");\n return EXIT_FAILURE;\n }\n // The options were mapped from this registry's own keys, so a miss can't happen; the\n // guard is what lets TypeScript narrow the lookup.\n const command = registry[picked];\n if (!command) {\n return EXIT_FAILURE;\n }\n // Hand off with an empty argv: every command already asks for what it needs (`init`\n // prompts for a name, `add`/`remove` open their module pickers), so the picker\n // deliberately teaches itself nothing about any command's argument shape.\n return command.run([]);\n}\n\nexport async function main(\n argv: string[],\n deps: Partial<CliDeps> = {}\n): Promise<number> {\n const {\n registry = COMMANDS,\n select = clackSelect,\n isCancel = clackIsCancel,\n } = deps;\n const [name, ...rest] = argv;\n\n // Explicit help is never the picker, on a TTY as much as anywhere.\n if (name === \"--help\" || name === \"-h\" || name === \"help\") {\n printHelp(registry);\n return EXIT_OK;\n }\n\n // `--version` before dispatch, so it answers even when the project is unscaffolded.\n if (name === \"--version\" || name === \"-v\" || name === \"version\") {\n console.log(await readVersion());\n return EXIT_OK;\n }\n\n if (!name) {\n // No TTY (CI, piped stdin, `saasaloy | cat`) → the exact help and exit code this\n // printed before the picker existed. A prompt nobody can answer would hang.\n if (!isInteractive()) {\n printHelp(registry);\n return EXIT_OK;\n }\n return pickCommand({ registry, select, isCancel });\n }\n\n // Own keys only: a plain object inherits `toString`, `constructor`, `valueOf` and the\n // rest from Object.prototype, and every one of them is truthy — so a bare `registry[name]`\n // lets `saasaloy toString` past this guard and dies on `.run` instead of saying what it\n // should, that the command is unknown.\n const command = Object.hasOwn(registry, name) ? registry[name] : undefined;\n if (!command) {\n console.error(`${pc.red(\"Unknown command:\")} ${name}\\n`);\n printHelp(registry);\n // Bad usage is a refusal, not a failure — nothing broke, the input was wrong.\n return EXIT_REFUSED;\n }\n\n return command.run(rest);\n}\n","import {\n cancel,\n confirm,\n intro,\n isCancel,\n log,\n note,\n outro,\n select,\n} from \"@clack/prompts\";\nimport { posix } from \"node:path\";\nimport pc from \"picocolors\";\nimport { buildPlan, executePlan } from \"../lib/applier.js\";\nimport type {\n ApplyResult,\n FileAction,\n Plan,\n PlannedFile,\n} from \"../lib/applier.js\";\nimport {\n detectCliMismatches,\n formatCliMismatches,\n} from \"../lib/cli-requires.js\";\nimport { describeStaleOwner } from \"../lib/collisions.js\";\nimport { detectConflicts, formatConflicts } from \"../lib/conflicts.js\";\nimport {\n EXIT_FAILURE,\n EXIT_OK,\n EXIT_REFUSED,\n exitCodeFor,\n formatFailure,\n} from \"../lib/exit.js\";\nimport { planWritesUi } from \"../lib/design.js\";\nimport { DEV_VARS_EXAMPLE, writeDevVarsExample } from \"../lib/dev-vars.js\";\nimport { lineDiff } from \"../lib/diff.js\";\nimport type { DiffLine } from \"../lib/diff.js\";\nimport { loadLock, LOCK_FILE, saveLock, upsertLock } from \"../lib/lock.js\";\nimport type { Lockfile } from \"../lib/lock.js\";\nimport { loadManifest, MANIFEST_FILE, saveManifest } from \"../lib/manifest.js\";\nimport type { Manifest } from \"../lib/manifest.js\";\nimport { planDeps, readRootPackageJson, writeDeps } from \"../lib/pkg-json.js\";\nimport { findProjectRoot } from \"../lib/project.js\";\nimport {\n createRegistrySource,\n DEFAULT_OWNER,\n DEFAULT_REPO,\n parseCoordinate,\n REGISTRY_ENV,\n} from \"../lib/registry.js\";\nimport type { Coordinate, RegistrySource } from \"../lib/registry.js\";\nimport {\n detectMissingRequirements,\n formatMissingRequirements,\n} from \"../lib/requires.js\";\nimport { mergeGraph, resolveGraph } from \"../lib/resolve.js\";\nimport type { Graph } from \"../lib/resolve.js\";\nimport { CONFIG_FILE, loadConfig, saveConfig } from \"../lib/saasaloy-config.js\";\nimport type { LoadedConfig } from \"../lib/saasaloy-config.js\";\nimport { isInteractive, wrapForNote } from \"../lib/tui.js\";\nimport { uiBlockFiles } from \"../lib/ui-blocks.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { readVersion } from \"../version.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\n\n// `saasaloy add <module>` — the local applier. Resolve the dependsOn graph, show the\n// plan behind a confirmation prompt, then drop files into their convention-based\n// targets, record them in .saasaloy/manifest.json with content hashes, and merge npm\n// deps (build spec §2.4/§2.7/§2.9). `--dry-run`/`--diff` preview without mutating.\n\nexport interface AddOptions {\n name?: string;\n dryRun: boolean;\n diff: boolean;\n yes: boolean;\n force: boolean;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\n \"--dry-run\",\n \"--diff\",\n \"--yes\",\n \"-y\",\n \"--force\",\n \"--help\",\n \"-h\",\n]);\nconst USAGE =\n \"saasaloy add [<module>|<owner/repo[@ref]/module>|<owner/repo>] [--dry-run] [--diff] [--yes] [--force]\";\nconst HELP: CommandHelp = {\n name: \"add\",\n describe: DESCRIPTIONS.add,\n usage: USAGE,\n flags: {\n \"--dry-run\": \"show the plan and write nothing\",\n \"--diff\": \"show a per-file diff and write nothing\",\n \"-y, --yes\": \"skip the confirmation prompt\",\n \"--force\": \"re-apply a module that is already installed\",\n },\n};\n\n/**\n * Split `add`'s argv into options. Exported for its own tests: the rejection rules here\n * (an unknown flag, a second positional) are the difference between `--forse` doing\n * nothing and `--forse` being reported, and they are worth pinning without driving a\n * whole apply to reach them.\n */\nexport function parseArgs(argv: string[]): AddOptions {\n const positional: string[] = [];\n const unknown: string[] = [];\n for (const arg of argv) {\n if (!arg.startsWith(\"-\")) {\n positional.push(arg);\n } else if (!KNOWN_FLAGS.has(arg)) {\n // A typo'd flag (`--forse`) silently running without force is worse than an error.\n unknown.push(arg);\n }\n }\n unknown.push(...positional.slice(1));\n return {\n diff: argv.includes(\"--diff\"),\n dryRun: argv.includes(\"--dry-run\"),\n force: argv.includes(\"--force\"),\n name: positional[0],\n unknown,\n yes: argv.includes(\"--yes\") || argv.includes(\"-y\"),\n };\n}\n\n/**\n * Pin a coordinate to the SHA the lock recorded for it, so a re-install reproduces\n * identical bytes (ADR 0012). Three conditions hold it back, and each one means the user\n * asked for something else: `SAASALOY_REGISTRY_DIR` points at a working copy, an explicit\n * `@ref` names the ref to move to, or the entry was installed from a local checkout and\n * carries no SHA to pin to. Returns `coord` unchanged when none applies.\n *\n * A separate function from `runAdd` because reaching this branch through the command\n * means resolving a remote source, and the rule is worth testing on its own.\n */\nexport function pinToLock(coord: Coordinate, lock: Lockfile): Coordinate {\n if (process.env[REGISTRY_ENV] || !coord.module || coord.ref) {\n return coord;\n }\n const slug = `${coord.owner ?? DEFAULT_OWNER}/${coord.repo ?? DEFAULT_REPO}`;\n const pinned = lock.modules[coord.module];\n if (!pinned || pinned.source !== slug || pinned.resolved === \"local\") {\n return coord;\n }\n return { ...coord, ref: pinned.resolved };\n}\n\nconst ACTION_LABEL: Record<FileAction, string> = {\n conflict: pc.yellow(\"conflict → merge\"),\n create: pc.green(\"create\"),\n drift: pc.yellow(\"drift → merge\"),\n overwrite: pc.cyan(\"overwrite\"),\n unchanged: pc.dim(\"unchanged\"),\n};\n\n// Cap a single file's diff so a big generated file can't flood the terminal.\nconst MAX_DIFF_LINES = 60;\n\n// How many `requiresOneOf` prompts one `add` may open. Each pick settles one requirement,\n// so this is only reached by a chain of modules that each require the next.\nconst MAX_REQUIREMENT_PROMPTS = 8;\n\n// Keyed by DiffLine[\"kind\"], so adding a kind to lib/diff.ts is a type error here\n// rather than a silently unstyled line.\nconst DIFF_LINE_STYLE: Record<DiffLine[\"kind\"], (text: string) => string> = {\n add: (text) => pc.green(`+ ${text}`),\n context: (text) => pc.dim(` ${text}`),\n del: (text) => pc.red(`- ${text}`),\n};\n\nfunction renderDiff(file: PlannedFile): string {\n const lines = lineDiff(file.oldContent ?? \"\", file.content);\n const shown = lines\n .slice(0, MAX_DIFF_LINES)\n .map((line) => DIFF_LINE_STYLE[line.kind](line.text));\n if (lines.length > MAX_DIFF_LINES) {\n shown.push(pc.dim(` … ${lines.length - MAX_DIFF_LINES} more lines`));\n }\n return shown.join(\"\\n\");\n}\n\n/**\n * How the plan names one file. A target alone identifies it, until `onlyWith` lets two\n * descriptor entries share one target (#99) — then the plan has to say which variant it\n * picked, because that is the whole decision the user would want to check. An\n * unconditional file's line is unchanged, so the common plan reads as it always did.\n */\nfunction fileLabel(file: PlannedFile): string {\n return file.onlyWith === undefined\n ? file.target\n : `${file.target} ${pc.dim(`(${file.from})`)}`;\n}\n\nfunction summarizePlan(plan: Plan, requested: string, prereqs: string[]): void {\n if (prereqs.length > 0) {\n note(\n wrapForNote(\n `${pc.bold(requested)} requires: ${prereqs.map((p) => pc.cyan(p)).join(\", \")}`\n ),\n \"Dependencies\"\n );\n }\n const willInstall = plan.install.map((m) => pc.cyan(m)).join(pc.dim(\" → \"));\n const lines = [`will install: ${willInstall}`];\n if (plan.alreadyInstalled.length > 0) {\n lines.push(\n pc.dim(`already installed (skipped): ${plan.alreadyInstalled.join(\", \")}`)\n );\n }\n\n const writable = plan.files.filter(\n (f) => f.action !== \"drift\" && f.action !== \"conflict\"\n );\n const held = plan.files.filter(\n (f) => f.action === \"drift\" || f.action === \"conflict\"\n );\n lines.push(\"\");\n for (const file of plan.files) {\n lines.push(` ${ACTION_LABEL[file.action]} ${fileLabel(file)}`);\n }\n lines.push(\n \"\",\n pc.dim(`${writable.length} file(s) to apply, ${held.length} needing merge`)\n );\n if (plan.dependencies.length > 0) {\n lines.push(pc.dim(`deps: ${plan.dependencies.join(\", \")}`));\n }\n if (plan.devDependencies.length > 0) {\n lines.push(pc.dim(`devDeps: ${plan.devDependencies.join(\", \")}`));\n }\n note(wrapForNote(lines.join(\"\\n\")), \"Plan\");\n\n if (Object.keys(plan.envVars).length > 0) {\n const envLines = Object.entries(plan.envVars).map(\n ([k, v]) => `${pc.cyan(k)} ${pc.dim(`— ${v}`)}`\n );\n note(wrapForNote(envLines.join(\"\\n\")), \"Env vars to set\");\n }\n if (Object.keys(plan.aliases).length > 0) {\n const aliasLines = Object.entries(plan.aliases).map(\n ([a, p]) => `${pc.cyan(a)} ${pc.dim(`→ ${p}`)}`\n );\n note(\n wrapForNote(\n `${aliasLines.join(\"\\n\")}\\n\\n${pc.dim(\"New workspace(s) — run `pnpm install` to link them.\")}`\n ),\n \"Aliases registered\"\n );\n }\n for (const conflict of plan.aliasConflicts) {\n log.warn(\n `Alias redefinition: ${conflict} ${pc.dim(\"(scaffold overrides the existing alias)\")}.`\n );\n }\n // The sentence lives in collisions.ts beside the ownership refusal it mirrors, so the\n // two ownership messages cannot drift apart; the path is accented here the way every\n // other `add` warning accents one (#107).\n for (const stale of plan.staleOwners) {\n log.warn(describeStaleOwner({ ...stale, target: pc.cyan(stale.target) }));\n }\n const newLinks = plan.links.filter((l) => l.action !== \"conflict\");\n if (newLinks.length > 0) {\n const linkLines = newLinks.map(\n (l) => `${pc.cyan(l.path)} ${pc.dim(`→ ${l.target}`)}`\n );\n note(\n wrapForNote(\n `${linkLines.join(\"\\n\")}\\n\\n${pc.dim(\"Symlinked for Claude Code — the skill files live in `.agents/skills/`.\")}`\n ),\n \"Skill links\"\n );\n }\n for (const link of plan.links) {\n if (link.action === \"conflict\") {\n log.warn(\n `Skill link ${pc.cyan(link.path)} already exists and isn't ours — ` +\n `left untouched ${pc.dim(\"(remove it to let `add` link the skill)\")}.`\n );\n }\n }\n const applyPatches = plan.patches.filter((p) => p.action === \"apply\");\n if (applyPatches.length > 0) {\n const patchLines = applyPatches.map(\n (p) => `${pc.cyan(p.file)} ${pc.dim(`— ${p.patch.kind}`)}`\n );\n note(wrapForNote(patchLines.join(\"\\n\")), \"Config patches\");\n }\n for (const p of plan.patches) {\n if (p.action === \"missing\") {\n log.warn(\n `Config patch target ${pc.cyan(p.file)} is missing — ${p.patch.kind} not applied ` +\n `${pc.dim(`(is ${p.module}'s prerequisite installed?)`)}.`\n );\n }\n }\n}\n\n/**\n * A block the applier wrote into the ui package renders nothing until the owner imports\n * it, because `add` never edits a page — where a block belongs is the owner's decision,\n * not filename sort's (ADR 0030). One line per module: which file arrived, and the skill\n * command whose Wire-up section carries the import and the tag.\n *\n * The `@ui/blocks/` target is the whole signal, so a module that ships one without a\n * skill has nowhere to keep those steps. That is the module author's bug, and it warns\n * rather than refuses — the file itself is fine and the user asked for it.\n */\nfunction wireUpSteps(\n result: ApplyResult,\n aliases: Record<string, string>\n): string[] {\n const blocks = uiBlockFiles(\n [...result.written, ...result.refreshed],\n aliases\n );\n if (blocks.length === 0) {\n return [];\n }\n\n const skillByModule = new Map(\n result.links.map((link) => [link.module, posix.basename(link.path)])\n );\n const byModule = new Map<string, string[]>();\n for (const block of blocks) {\n byModule.set(block.module, [\n ...(byModule.get(block.module) ?? []),\n block.target,\n ]);\n }\n\n const lines: string[] = [];\n for (const module of [...byModule.keys()].toSorted()) {\n const moduleTargets = (byModule.get(module) ?? []).toSorted();\n const targets = moduleTargets.map((target) => pc.cyan(target)).join(\", \");\n const verb = moduleTargets.length === 1 ? \"is\" : \"are\";\n const skill = skillByModule.get(module);\n if (skill === undefined) {\n log.warn(\n `${pc.cyan(module)} wrote ${targets} but ships no skill — its wire-up steps are ` +\n `missing. Import the block yourself ${pc.dim(\"(a module that ships a block should ship a skill with a Wire-up section)\")}.`\n );\n continue;\n }\n lines.push(\n `Manual wire-up needed — ${targets} ${verb} on disk but on no page yet. ` +\n `In Claude Code, run ${pc.cyan(`/${skill}`)} and follow its Wire-up section.`\n );\n }\n return lines;\n}\n\n/**\n * The environment half of the next-steps box.\n *\n * The variable names still print — they are what a reader scans for. What changed in #50\n * is the instruction under them. `add` used to say \"copy `.dev.vars.example` to\n * `.dev.vars` and fill it in\", which was the whole procedure and did nothing for a\n * `PUBLIC_*` value, whose home is a frontend `.env` the example file never described.\n * `saasaloy env` does both, so this points at it and leaves the example file as what it\n * is: the checked-in record of what each variable means.\n *\n * Exported so the wording is pinned by a test — it is the one line telling a new user\n * the next command exists.\n */\nexport function envSteps(\n names: string[],\n devVarsPath: string | undefined\n): string[] {\n if (names.length === 0) {\n return [];\n }\n const keys = names.toSorted();\n const where = devVarsPath\n ? ` ${pc.dim(`${devVarsPath} keeps the checked-in description of each.`)}`\n : \"\";\n return [\n `Set ${keys.map((key) => pc.cyan(key)).join(\", \")}.`,\n `Run ${pc.cyan(\"saasaloy env\")} — it prompts for each unset one and writes it to the ${pc.cyan(\".dev.vars\")} or ${pc.cyan(\".env\")} that reads it.${where}`,\n ];\n}\n\n/**\n * The line an incomplete apply ends on (#49). A module whose file was edited between the\n * plan and the write is not installed, and staying quiet about it is the bug: the state\n * files say one thing and the closing \"Applied\" would say another. Name the modules, and\n * name the command that finishes the job — a plain re-run, because they are absent from\n * `config.installed` and so get planned again, drift and all, for a fresh approval.\n *\n * `requested` is the module the user typed, not each incomplete name: re-running it\n * re-plans the whole graph, including an incomplete dependency.\n */\nexport function formatIncomplete(\n incomplete: string[],\n requested: string\n): string {\n const names = incomplete.map((name) => pc.cyan(name)).join(\", \");\n const verb = incomplete.length === 1 ? \"is\" : \"are\";\n const object = incomplete.length === 1 ? \"it\" : \"them\";\n return (\n `${names} ${verb} not installed — files changed while the plan was open. ` +\n `Re-run ${pc.cyan(`saasaloy add ${requested}`)} to complete ${object}.`\n );\n}\n\n/**\n * The line a thrown apply ends on (#49). `add` has no rollback by design: whatever\n * landed before the throw stays, and the manifest, config and lock are written to\n * describe it. That is only honest if the user is told, so state the model and name the\n * command that finishes the job — the same plain re-run an incomplete run asks for,\n * since the module never reached `config.installed`.\n */\nexport function formatRecovery(requested: string): string {\n return (\n \"Partial apply — the manifest, config and lock describe what landed. \" +\n `Re-run ${pc.cyan(`saasaloy add ${requested}`)} to complete it.`\n );\n}\n\n/** What `persistState` needs to write the three state files. */\nexport interface PersistInput {\n config: LoadedConfig;\n /** The modules `executePlan` reported complete; `[]` when the apply threw. */\n completed: string[];\n graph: Graph;\n lock: Lockfile;\n manifest: Manifest;\n root: string;\n source: Pick<RegistrySource, \"provenance\">;\n}\n\n/**\n * Write the manifest, the config and the lock, and never throw (#49).\n *\n * This runs on the way out of an apply, successful or not, so one broken step here must\n * not take the others with it — nor replace the apply error that sent us this way. Each\n * step is attempted on its own, a failure is reported, and the failures are returned for\n * the caller to decide about.\n *\n * The lock is skipped when the source has no commit SHA to pin to. `provenance()` throws\n * in that case, and it used to throw first, before any save ran: an unresolvable remote\n * cost the user the manifest as well, and reported itself instead of the real error. A\n * lock entry is a pin, not a record of disk, so having none is the correct answer.\n *\n * A separate function from `runAdd` because these rules are what the bug was, and they\n * need injectable failures to test.\n */\nexport async function persistState(input: PersistInput): Promise<unknown[]> {\n const failures: unknown[] = [];\n\n // Pin the source + ref + commit SHA per module (ADR 0012). Only the freshly installed\n // ones: an already-installed dependency keeps the SHA it was fetched at, so the lock\n // never misstates on-disk provenance.\n try {\n upsertLock(\n input.lock,\n input.source.provenance(),\n input.completed,\n input.graph\n );\n } catch (error) {\n failures.push(error);\n log.warn(`Couldn't pin ${LOCK_FILE} — ${formatFailure(error)}.`);\n }\n\n // Record whatever actually landed even if a mid-plan write failed — a written file the\n // manifest doesn't know about would classify as a conflict next run.\n const saves: [string, () => Promise<void>][] = [\n [MANIFEST_FILE, () => saveManifest(input.root, input.manifest)],\n [CONFIG_FILE, () => saveConfig(input.root, input.config)],\n [LOCK_FILE, () => saveLock(input.root, input.lock)],\n ];\n for (const [file, save] of saves) {\n try {\n await save();\n } catch (error) {\n failures.push(error);\n log.warn(`Couldn't write ${file} — ${formatFailure(error)}.`);\n }\n }\n\n return failures;\n}\n\n/** What `applyAndPersist` needs beyond the state files `persistState` writes. */\nexport interface ApplyInput extends Omit<PersistInput, \"completed\"> {\n /**\n * Called once the apply itself is over, before any state file is written (#49). This\n * is the edge of the window where a failure can leave files on disk, and the caller\n * needs it to decide whether the run owes the user a recovery line.\n */\n onApplied?: () => void;\n plan: Plan;\n}\n\n/**\n * Run the plan and record what it did, whichever way it goes (#98, #49).\n *\n * All three state files leave `add` through this one path, so no exit skips one of them.\n * They pin what the apply reported complete rather than what the plan intended, so a run\n * that threw pins nothing and `config.installed` and the lock never disagree, not even\n * under `--force`, where the module was already installed before the run.\n *\n * A failed apply propagates its own error; a failed save on an otherwise clean apply\n * propagates that instead, because a run whose state files didn't land is not a success.\n * The two are not the same failure, though, so `onApplied` marks the boundary between\n * them for the caller.\n */\nexport async function applyAndPersist(input: ApplyInput): Promise<ApplyResult> {\n let result: ApplyResult;\n try {\n result = await executePlan(\n input.plan,\n input.root,\n input.config,\n input.manifest\n );\n } catch (error) {\n await persistState({ ...input, completed: [] });\n throw error;\n }\n input.onApplied?.();\n\n const failures = await persistState({\n ...input,\n completed: result.completed,\n });\n if (failures.length > 0) {\n throw failures[0];\n }\n return result;\n}\n\n/**\n * The closing box: where the module's procedure is written down, and what the project\n * still needs from the operator. `add waitlist` used to end at \"Applied\" while the\n * project 500s until `db:generate` and `db:migrate:local` run, and the env vars it\n * printed at plan time had scrolled away behind the confirmation (#98).\n *\n * There is deliberately no `nextSteps` field on the descriptor. The skill is the single\n * source of a module's procedure; a second copy in the descriptor would drift from it,\n * and this box points at the skill rather than paraphrasing it.\n */\nfunction printNextSteps(\n plan: Plan,\n result: ApplyResult,\n devVarsPath: string | undefined,\n aliases: Record<string, string>\n): void {\n const lines: string[] = [...wireUpSteps(result, aliases)];\n\n const skills = result.links\n .map((link) => posix.basename(link.path))\n .toSorted();\n if (skills.length > 0) {\n lines.push(\n `Read the module's own procedure — in Claude Code, run ${skills\n .map((name) => pc.cyan(`/${name}`))\n .join(\" or \")}.`\n );\n }\n\n lines.push(...envSteps(Object.keys(plan.envVars), devVarsPath));\n\n if (lines.length === 0) {\n return;\n }\n note(wrapForNote(lines.join(\"\\n\\n\")), \"Next steps\");\n}\n\nexport async function runAdd(argv: string[]): Promise<number> {\n // Parse before anything answers, so `--help --forse` reports the typo instead of\n // printing help over it. Help is then answered before the intro rail opens, so it reads\n // as plain output a person can pipe into a pager rather than as a clack box.\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n intro(pc.bgCyan(pc.black(\" saasaloy add \")));\n\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n\n let coord: ReturnType<typeof parseCoordinate>;\n try {\n coord = parseCoordinate(opts.name);\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n }\n\n // The \"Proceed?\" confirm below reads stdin. With no terminal there it can never be\n // answered, so `saasaloy add x </dev/null` printed the whole plan, took the closed\n // stream as \"no\", and exited 0 having written nothing — a silent success. `update`\n // already refuses in this situation; both commands now answer it the same way (#98).\n // A preview writes nothing and needs no confirmation, so it is exempt. With no module\n // named the picker's own non-interactive refusal below is the more specific message,\n // so leave that case to it.\n if (\n coord.module &&\n !opts.yes &&\n !opts.dryRun &&\n !opts.diff &&\n !process.stdin.isTTY\n ) {\n cancel(\n \"No terminal to confirm in — re-run with `--yes` to apply, or `--dry-run` to preview.\"\n );\n return EXIT_REFUSED;\n }\n\n let root: string;\n let config: Awaited<ReturnType<typeof loadConfig>>;\n try {\n root = await findProjectRoot();\n config = await loadConfig(root);\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n }\n\n let plan: Plan;\n let prereqs: string[];\n let source: RegistrySource | undefined;\n // Set to the requested module once the apply starts. It tells the catch below whether\n // a failure could have left files on disk, so only those failures carry the re-run\n // instruction — a bad coordinate or a missing descriptor writes nothing (#49).\n let partial: string | undefined;\n try {\n // Load the lock up front so a named remote add can pin to the SHA it recorded — a\n // re-install then reproduces identical bytes (ADR 0012). Explicit `@ref` or the\n // `update` flow (#17) are the sanctioned ways to move off the lock.\n const lock = await loadLock(root);\n coord = pinToLock(coord, lock);\n\n source = createRegistrySource(coord);\n if (process.env[REGISTRY_ENV] && (coord.owner || coord.repo)) {\n log.warn(\n `Ignoring source \"${coord.owner}/${coord.repo}\" — ${REGISTRY_ENV} override is set.`\n );\n }\n\n // No module named (bare `add`, or `owner/repo` with no module) → pick from the source.\n let requested = coord.module;\n if (!requested) {\n // No terminal to pick in (CI, piped stdin, `saasaloy add | cat`) means the prompt\n // below could never be answered — it would hang the pipeline. Say what was missing\n // instead, before the registry is even fetched.\n if (!isInteractive()) {\n cancel(\n `No module named and no terminal to pick one in — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n const offered = await source.listModules();\n if (offered.length === 0) {\n cancel(`No modules found in ${source.label}.`);\n return EXIT_REFUSED;\n }\n // Picking something already installed took the user through a plan preview to\n // \"Nothing to do\", which reads as a bug in the picker rather than an answer.\n // `--force` re-applies one, and that path names the module explicitly (#98).\n const installed = new Set(config.installed);\n const available = offered.filter((n) => !installed.has(n));\n if (available.length === 0) {\n note(\n `Every module in ${source.label} is already installed.`,\n \"Nothing to add\"\n );\n outro(pc.dim(\"use `saasaloy add <module> --force` to re-apply one\"));\n return EXIT_OK;\n }\n const picked = await select({\n message: `Pick a module to add ${pc.dim(`(from ${source.label}, ${installed.size} already installed)`)}`,\n options: available.map((n) => ({ label: n, value: n })),\n });\n if (isCancel(picked)) {\n cancel(\"add cancelled\");\n return EXIT_FAILURE;\n }\n requested = picked;\n }\n\n let graph = await resolveGraph(source, requested);\n\n // `requires.saasaloy` — the descriptor's floor on the CLI reading it (#50). Checked\n // before the driver prompt and before a single file is planned, because a CLI too old\n // to understand the descriptor cannot be trusted to resolve the rest of it either.\n // The graph holds every transitive prerequisite, so a range declared three levels down\n // refuses here and the message names that module, not the one the user typed.\n const cliVersion = await readVersion();\n const cliCheckFails = (): boolean => {\n const mismatches = detectCliMismatches({ cliVersion, graph });\n if (mismatches.length === 0) {\n return false;\n }\n cancel(formatCliMismatches(mismatches, requested, cliVersion));\n return true;\n };\n if (cliCheckFails()) {\n return EXIT_REFUSED;\n }\n\n // `requiresOneOf` — a capability naming its mutually exclusive drivers. Settle it\n // before the conflict check below, so that check reads the graph a picked driver is\n // already part of (#98).\n let unmet = detectMissingRequirements({ config, graph });\n if (unmet.length > 0 && (!isInteractive() || opts.yes)) {\n // `--yes` means \"don't ask me\", not \"choose a driver for my project\", and a\n // pipeline has no terminal to answer in. Both refuse and name the options.\n cancel(formatMissingRequirements(unmet, requested));\n return EXIT_REFUSED;\n }\n // One prompt per unmet requirement, re-checked after each pick: a single driver can\n // satisfy two capabilities, and a picked module may declare a requirement of its own.\n // The round cap keeps a descriptor that requires its way in a circle from prompting\n // forever; whatever is still unmet when it runs out is refused below.\n for (\n let round = 0;\n unmet.length > 0 && round < MAX_REQUIREMENT_PROMPTS;\n round++\n ) {\n const requirement = unmet[0];\n if (!requirement) {\n break;\n }\n const picked = await select({\n message: `${pc.cyan(requirement.declaredBy)} needs one of these — pick one`,\n options: requirement.options.map((n) => ({ label: n, value: n })),\n });\n if (isCancel(picked)) {\n cancel(\"add cancelled\");\n return EXIT_FAILURE;\n }\n graph = mergeGraph(graph, await resolveGraph(source, picked));\n unmet = detectMissingRequirements({ config, graph });\n }\n if (unmet.length > 0) {\n cancel(formatMissingRequirements(unmet, requested));\n return EXIT_REFUSED;\n }\n\n // A driver the user just picked is a descriptor this run never saw above, so its own\n // `requires` is checked here too — still before anything is planned.\n if (cliCheckFails()) {\n return EXIT_REFUSED;\n }\n\n prereqs = graph.order.filter((n) => n !== requested);\n\n // Mutually exclusive modules are refused before anything is written, and `--force`\n // doesn't bypass it — force means \"re-apply this module\", not \"install it anyway\".\n const manifest = await loadManifest(root);\n const conflicts = detectConflicts({ graph, config, lock });\n if (conflicts.missingLockEntries.length > 0) {\n log.warn(\n `No lock entry for ${conflicts.missingLockEntries.map((m) => pc.cyan(m)).join(\", \")} — ` +\n `any conflict they declare can't be checked ${pc.dim(\"(re-add them to record it)\")}.`\n );\n }\n if (conflicts.conflicts.length > 0) {\n cancel(formatConflicts(conflicts.conflicts, requested));\n return EXIT_REFUSED;\n }\n\n const install = graph.order.filter(\n (n) => !config.installed.includes(n) || (opts.force && n === requested)\n );\n // Installed and not being (re-)applied — a forced module belongs to `install`, not here.\n const alreadyInstalled = graph.order.filter(\n (n) => config.installed.includes(n) && !install.includes(n)\n );\n\n if (install.length === 0) {\n note(\n `${pc.cyan(requested)} and its dependencies are already installed.`,\n \"Nothing to do\"\n );\n outro(pc.dim(\"use --force to re-apply\"));\n return EXIT_OK;\n }\n\n plan = await buildPlan({\n alreadyInstalled,\n config,\n install,\n manifest,\n modules: graph.modules,\n requested,\n root,\n });\n\n summarizePlan(plan, requested, prereqs);\n\n if (planWritesUi(plan)) {\n log.info(\n `This module writes ${pc.cyan(\"packages/ui/\")}. Run ${pc.cyan(\"/saasaloy-design update\")} after it applies.`\n );\n }\n\n if (opts.diff) {\n for (const file of plan.files) {\n if (file.action === \"unchanged\") {\n continue;\n }\n note(\n renderDiff(file),\n `${ACTION_LABEL[file.action]} ${fileLabel(file)}`\n );\n }\n for (const p of plan.patches) {\n if (p.action !== \"apply\") {\n continue;\n }\n note(p.diff, `${pc.green(\"patch\")} ${p.file}`);\n }\n }\n\n // --dry-run and --diff both preview only; nothing is written.\n if (opts.dryRun || opts.diff) {\n outro(\n pc.dim(\n opts.diff\n ? \"diff only — nothing applied\"\n : \"dry run — nothing applied\"\n )\n );\n return EXIT_OK;\n }\n\n if (!opts.yes) {\n const proceed = await confirm({ message: \"Proceed?\" });\n if (isCancel(proceed)) {\n cancel(\"add cancelled\");\n return EXIT_FAILURE;\n }\n if (!proceed) {\n outro(pc.dim(\"aborted — nothing applied\"));\n return EXIT_OK;\n }\n }\n\n // For the length of the apply a failure can leave files on disk, so the catch below\n // owes the user the re-run that completes them (#49). `onApplied` closes that window\n // the moment the apply is over: a save that fails afterwards is its own story, and\n // the re-run the recovery line advises would have nothing left to complete.\n partial = requested;\n const result = await applyAndPersist({\n config,\n graph,\n lock,\n manifest,\n onApplied: () => {\n partial = undefined;\n },\n plan,\n root,\n source,\n });\n\n // Merge npm deps into the project root package.json (best-effort — never blocks the\n // apply). This trails `executePlan` on purpose (#98): a mid-plan failure throws past\n // here, so a run that wrote no files leaves no package.json advertising packages\n // nothing installed. Rolling back what `executePlan` itself wrote is #49.\n const pkg = await readRootPackageJson(root);\n let depsAdded: string[] = [];\n const allDeps = [...plan.dependencies, ...plan.devDependencies];\n if (allDeps.length > 0) {\n if (pkg) {\n const {\n added,\n devAdded,\n conflicts: depConflicts,\n } = planDeps(pkg, plan.dependencies, plan.devDependencies);\n await writeDeps(root, pkg, added, devAdded);\n depsAdded = [...added, ...devAdded].map((d) => d.name);\n for (const conflict of depConflicts) {\n log.warn(`Dependency version conflict — ${conflict}.`);\n }\n } else {\n // Best-effort means \"don't block\", not \"fail silently\".\n log.warn(\n `No package.json at the project root — add ${allDeps.join(\", \")} yourself.`\n );\n }\n }\n\n for (const file of result.written) {\n log.step(`${ACTION_LABEL[file.action]} ${file.target}`);\n }\n for (const file of result.refreshed) {\n log.step(`${ACTION_LABEL[file.action]} ${file.target}`);\n }\n for (const link of result.links) {\n const label =\n link.action === \"create\" ? pc.green(\"link\") : pc.dim(\"link\");\n log.step(`${label} ${link.path} ${pc.dim(`→ ${link.target}`)}`);\n }\n for (const link of result.linkConflicts) {\n log.warn(\n `Skill link ${pc.cyan(link.path)} left untouched — a non-saasaloy path already occupies it.`\n );\n }\n for (const p of result.patched) {\n log.step(\n `${pc.green(\"patch\")} ${p.file} ${pc.dim(`— ${p.patch.kind}`)}`\n );\n }\n for (const p of result.patchConflicts) {\n log.warn(\n `Config patch target ${pc.cyan(p.file)} missing — ${p.patch.kind} skipped.`\n );\n }\n // A refusal is not an idempotent no-op: the patch would have written something wrong,\n // so nothing was written and nothing was tracked. Name the file and the reason.\n for (const r of result.patchRefusals) {\n log.warn(\n `Config patch on ${pc.cyan(r.patch.file)} skipped — ${r.reason}. Wire it by hand.`\n );\n }\n if (result.lateDrift.length > 0) {\n const lines = result.lateDrift\n .map((f) => ` ${pc.yellow(\"kept\")} ${f.target}`)\n .join(\"\\n\");\n note(\n wrapForNote(\n `${lines}\\n\\n${pc.dim(\"Changed while the plan was open — left alone rather than overwritten.\")}`\n ),\n \"Changed under us\"\n );\n }\n // Which of those kept files cost their module its install, stated plainly. The run\n // still exits 0: nothing failed, the user's own edit simply outranked the plan.\n const incomplete = plan.install.filter(\n (name) => !result.completed.includes(name)\n );\n if (incomplete.length > 0) {\n log.warn(formatIncomplete(incomplete, requested));\n }\n if (result.heldBack.length > 0) {\n const merges = result.heldBack\n .map((f) => ` ${ACTION_LABEL[f.action]} ${f.target}`)\n .join(\"\\n\");\n note(\n wrapForNote(\n `${merges}\\n\\n${pc.dim(\"These were left untouched. Hand them to an agent with `--diff` to merge.\")}`\n ),\n \"Needs merge\"\n );\n }\n if (depsAdded.length > 0) {\n note(\n wrapForNote(\n `${depsAdded.map((d) => pc.cyan(d)).join(\", \")}\\n\\n${pc.dim(\"Run `pnpm install` to fetch them.\")}`\n ),\n \"Dependencies added\"\n );\n }\n\n // The env vars printed at plan time, several boxes and a confirmation ago. Write\n // them where the project can keep them, and say where that is (#98).\n let devVarsPath: string | undefined;\n try {\n devVarsPath = await writeDevVarsExample({\n aliases: config.aliases,\n devVars: plan.devVars,\n envVars: plan.envVars,\n root,\n });\n } catch (error) {\n // Best-effort, like the dependency merge: an unwritable example file must not\n // undo an apply that already landed.\n log.warn(`Couldn't write ${DEV_VARS_EXAMPLE} — ${formatFailure(error)}.`);\n }\n\n printNextSteps(plan, result, devVarsPath, config.aliases);\n\n // The closing line names what was installed, not what was planned (#49). When\n // everything landed the two lists are the same and this reads as it always did.\n outro(\n result.completed.length > 0\n ? pc.green(\n `Applied ${result.completed.map((m) => pc.bold(m)).join(\", \")} ${pc.dim(`(${result.written.length} files)`)}`\n )\n : pc.yellow(\n `Nothing installed ${pc.dim(`(${result.written.length} files written)`)}`\n )\n );\n return EXIT_OK;\n } catch (error) {\n // A failure after the apply started leaves part of the module on disk. Nothing is\n // rolled back, so say what the state files now describe and how to finish the job\n // (#49) before the error itself closes the run.\n if (partial) {\n log.warn(formatRecovery(partial));\n }\n cancel(formatFailure(error));\n return exitCodeFor(error);\n } finally {\n // A remote source extracts each module to a temp dir; drop them once applied.\n await source?.cleanup?.();\n }\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, posix } from \"node:path\";\nimport { RefusalError } from \"./exit.js\";\nimport {\n detectCollisions,\n detectOwnedCollisions,\n formatCollisions,\n formatOwnedCollisions,\n mayShareTarget,\n} from \"./collisions.js\";\nimport type { OwnedCollision, StaleOwner } from \"./collisions.js\";\nimport {\n assertNoSymlinkPath,\n classifyLink,\n createDirLink,\n hashContent,\n joinModulePath,\n listFilesRelative,\n pathExists,\n readIfPresent,\n resolveWithinRoot,\n} from \"./fs-utils.js\";\nimport { samePatchEntry } from \"./manifest.js\";\nimport type { Manifest, ManifestPatch } from \"./manifest.js\";\nimport { applyPatch } from \"./patch/index.js\";\nimport type { PatchMatch } from \"./patch/index.js\";\nimport type { LoadedModule } from \"./registry.js\";\nimport { resolveTarget } from \"./saasaloy-config.js\";\nimport type { RegistryPatch, SaasaloyConfig } from \"./schema.js\";\n\n// The deterministic core of `saasaloy add`: turn the modules-to-install into a plan of\n// concrete file writes, classify each against the manifest's content hashes, then\n// execute the safe ones (build spec §2.9, §3.2). Convention-based file-drop is the\n// whole spine here; structural AST patches are the separate 10% (issue #7).\n\n// How a planned file relates to what's already on disk:\n// create — target doesn't exist yet\n// overwrite — tracked by us and untouched since (hash matches manifest) → safe update\n// unchanged — on-disk content already equals what we'd write\n// drift — tracked by us but hand-edited (hash ≠ manifest) → route to AI-merge, don't clobber\n// conflict — exists but we never wrote it (untracked) → don't clobber\nexport type FileAction =\n \"create\" | \"overwrite\" | \"unchanged\" | \"drift\" | \"conflict\";\n\n// Actions safe to apply without a human in the loop; drift/conflict are held back\n// for a merge. This is also the set whose content the patch preview reads, since each\n// of these leaves the planned bytes on disk.\nconst SAFE: ReadonlySet<FileAction> = new Set<FileAction>([\n \"create\",\n \"overwrite\",\n \"unchanged\",\n]);\n\n// Of the safe actions, the ones that need an actual write. `unchanged` means disk\n// already holds these exact bytes, so rewriting it would churn the file's mtime for\n// nothing — its manifest entry is still refreshed (#98, matching `executeUpdatePlan`).\nconst WRITABLE: ReadonlySet<FileAction> = new Set<FileAction>([\n \"create\",\n \"overwrite\",\n]);\n\nexport interface PlannedFile {\n module: string;\n /** Absolute path of the source file inside the module folder. */\n source: string;\n /** Module-relative POSIX source path (`files/lib/x.ts`) — recorded as the manifest's `from`. */\n from: string;\n /** Project-relative POSIX path (manifest key + display). */\n target: string;\n /** Absolute destination path. */\n targetAbs: string;\n content: string;\n newHash: string;\n action: FileAction;\n /** Present when the target already exists — used by `--diff`. */\n oldContent?: string;\n /** True for files copied from an `agent.skills` folder (vs. `files[]`). */\n isSkill: boolean;\n /** The `onlyWith` condition that chose this variant — the plan names it (#99). */\n onlyWith?: string;\n}\n\n// How a skill's `.claude/skills/<name>` symlink relates to what's already on disk:\n// create — nothing there, we'll make the link\n// exists — the correct link is already present (idempotent re-add)\n// conflict — a real dir/file or a link elsewhere sits there → don't clobber\nexport type LinkAction = \"create\" | \"exists\" | \"conflict\";\n\n// How a planned config patch relates to its target file:\n// apply — target resolvable and the patch changes it → write the result\n// unchanged — the patch is already present (idempotent no-op) → skip the write\n// missing — no target file (not scaffolded this run, not on disk) → can't patch\nexport type PatchAction = \"apply\" | \"unchanged\" | \"missing\";\n\n// A structural config patch bound to a concrete target file (ADR 0019). Unlike a\n// PlannedFile it is never manifest-tracked as a managed file — a patch mutates a\n// file another module owns, so it isn't a clean managed copy. `executePlan` records\n// each applied patch in `manifest.patches`, which is what `remove` reads back: it\n// reverses the four kinds that edit a config file (`chained-route`, `wrangler-binding`,\n// `plugin-array`, `const-array`) and warns about the two `package.json` kinds, which stay\n// the user's to undo (#36).\nexport interface PlannedPatch {\n module: string;\n /** Project-relative POSIX path of the file being patched. */\n file: string;\n /** Absolute path of the target file. */\n fileAbs: string;\n /** The op as authored (kind + payload + file) — re-applied at execute time. */\n patch: RegistryPatch;\n action: PatchAction;\n /** Unified diff of the would-be change; `\"\"` when unchanged or missing. */\n diff: string;\n /** The would-be content after the patch; undefined when the target is missing. */\n content?: string;\n /** Set when the patch's identity already exists at a different value — the user edited\n * what we would have written. `update` reports these into its merge plan; `add`\n * ignores it (issue #48, decision 1). */\n matched?: PatchMatch;\n}\n\n// A `.claude/skills/<name>` symlink pointing at the real, committed `.agents/skills/<name>`\n// folder, so Claude Code discovers the skill while every other agent reads the files directly.\nexport interface PlannedLink {\n module: string;\n /** Project-relative POSIX path of the symlink (under `.claude/skills`). */\n path: string;\n /** Absolute path of the symlink. */\n pathAbs: string;\n /** Project-relative POSIX path the link points at (under `.agents/skills`). */\n target: string;\n /** Absolute path of the link target. */\n targetAbs: string;\n action: LinkAction;\n}\n\nexport interface Plan {\n /** Modules being applied, in topological order. */\n install: string[];\n /** Requested modules already installed (skipped). */\n alreadyInstalled: string[];\n files: PlannedFile[];\n /** `.claude/skills/<name>` symlinks the installed skills register (created by executePlan). */\n links: PlannedLink[];\n /** Union of npm `dependencies[]` declared across the installed modules. */\n dependencies: string[];\n /** Union of npm `devDependencies[]` declared across the installed modules. */\n devDependencies: string[];\n /** Union of env vars declared (name → description) — reported, not written. */\n envVars: Record<string, string>;\n /** Union of the local-dev values declared for those vars — written into `.dev.vars.example`. */\n devVars: Record<string, string>;\n /** Aliases the installed scaffolds register into saasaloy.json (applied by executePlan). */\n aliases: Record<string, string>;\n /** Human-readable notes where a scaffold alias would redefine an existing one to a new path. */\n aliasConflicts: string[];\n /** Files a module reclaims whose manifest owner is a different installed module that is now stale (#107). */\n staleOwners: StaleOwner[];\n /** Structural config patches to apply (previewed against the would-be on-disk state). */\n patches: PlannedPatch[];\n /** Removal warnings keyed by the module that supplied them. */\n removeWarnings: Record<string, string[]>;\n}\n\n/** A file one revision of a module ships: where it comes from and where it lands. */\nexport interface ModuleFileRef {\n /** Module-relative POSIX source path (`files/lib/x.ts`) — the manifest's `from`. */\n from: string;\n /** Project-relative POSIX target path. */\n target: string;\n /** Absolute path of the source file inside the module folder. */\n abs: string;\n /** The `onlyWith` condition that selected this variant; absent on an unconditional file. */\n onlyWith?: string;\n}\n\n/** One `onlyWith` entry that could have filled a target, as the refusal names it. */\nexport interface FileCandidate {\n from: string;\n onlyWith: string;\n}\n\n/** A target every one of whose entries is conditional, none of them matching this project. */\nexport interface UnmatchedTarget {\n module: string;\n target: string;\n /** Every entry that declared this target, in descriptor order. */\n candidates: FileCandidate[];\n}\n\n/** What a module ships once its conditions are applied: the files, and the targets left empty. */\nexport interface ModuleFileSelection {\n files: Map<string, ModuleFileRef>;\n unmatched: UnmatchedTarget[];\n}\n\n/**\n * Every file a module ships, keyed by its project-relative target — `files[]`, each\n * scaffold's workspace files, and each `agent.skills` folder expanded — with each entry's\n * `onlyWith` condition applied against the resolved install set.\n *\n * The one place those four rules live. `buildPlan` walks it to plan an `add`, and\n * `buildUpdatePlan` walks it at two SHAs to diff a module against itself, so `update`\n * always sees exactly the set `add` would have written. Each engine carried its own copy\n * of these rules until #98, which is how the write-path guards drifted apart.\n *\n * `onlyWith` (#99) lets one module ship a dialect-bound file twice, once per database\n * driver, both entries naming the same `target`. Selection has to sit here rather than in\n * `buildPlan`: `auth`'s per-driver file lives in a scaffold rather than in `files[]`, and\n * an `update` that filtered differently would diff against a set `add` never wrote and\n * reinstate the variant this project does not hold.\n *\n * `installed` is this run's resolved graph unioned with what the project already records.\n * A target whose entries are *all* conditional and none matches is not an error here —\n * this stays a pure lister, and a project that predates a variant must not make `update`\n * throw. It comes back in `unmatched`, and `buildPlan` alone refuses on it.\n */\nexport async function selectModuleFiles(\n mod: LoadedModule,\n aliases: Record<string, string>,\n installed: ReadonlySet<string>\n): Promise<ModuleFileSelection> {\n // Gather every entry per target first, then resolve: a target is only unmatched once\n // all of its candidates are known, and the last eligible entry still wins, which is the\n // precedence `record` had before conditions existed.\n const byTarget = new Map<\n string,\n { chosen?: ModuleFileRef; candidates: FileCandidate[] }\n >();\n const record = (from: string, target: string, onlyWith?: string): void => {\n let slot = byTarget.get(target);\n if (!slot) {\n slot = { candidates: [] };\n byTarget.set(target, slot);\n }\n if (onlyWith !== undefined) {\n slot.candidates.push({ from, onlyWith });\n if (!installed.has(onlyWith)) {\n return;\n }\n }\n slot.chosen = {\n from,\n target,\n abs: joinModulePath(mod.dir, from),\n ...(onlyWith === undefined ? {} : { onlyWith }),\n };\n };\n\n for (const file of mod.item.files ?? []) {\n record(file.path, resolveTarget(aliases, file.target), file.onlyWith);\n }\n for (const scaffold of mod.item.scaffolds ?? []) {\n for (const file of scaffold.files) {\n record(\n file.path,\n posix.join(scaffold.workspace, file.target),\n file.onlyWith\n );\n }\n }\n // A skill folder is expanded from disk, not declared file by file, so there is no entry\n // to hang a condition on. Its files are unconditional by construction (#99).\n for (const skillRel of mod.item.agent?.skills ?? []) {\n const folderName = posix.basename(skillRel);\n for (const rel of await listFilesRelative(\n joinModulePath(mod.dir, skillRel)\n )) {\n record(\n posix.join(skillRel, rel),\n posix.join(\".agents/skills\", folderName, rel)\n );\n }\n }\n\n const files = new Map<string, ModuleFileRef>();\n const unmatched: UnmatchedTarget[] = [];\n for (const [target, slot] of byTarget) {\n if (slot.chosen) {\n files.set(target, slot.chosen);\n } else {\n unmatched.push({\n module: mod.item.name,\n target,\n candidates: slot.candidates,\n });\n }\n }\n return { files, unmatched };\n}\n\n/** {@link selectModuleFiles} without the unmatched report — what `update` needs. */\nexport async function listModuleFiles(\n mod: LoadedModule,\n aliases: Record<string, string>,\n installed: ReadonlySet<string>\n): Promise<Map<string, ModuleFileRef>> {\n return (await selectModuleFiles(mod, aliases, installed)).files;\n}\n\n/**\n * The refusal `add` prints when a target's every variant was filtered out. Same shape as\n * `formatConflicts`/`formatMissingRequirements`: a heading, then one line per target\n * naming each candidate and the module that would have selected it. `requiresOneOf`\n * should make this unreachable, so it reads as a descriptor bug rather than user error.\n */\nexport function formatUnmatchedTargets(unmatched: UnmatchedTarget[]): string {\n const heading = `Cannot apply — no file variant matches this project for ${unmatched.length} target${unmatched.length > 1 ? \"s\" : \"\"}:`;\n const lines = unmatched.map((u) => {\n const candidates = u.candidates\n .map((c) => `${c.from} (onlyWith ${c.onlyWith})`)\n .join(\", \");\n return ` ${u.target} — ${u.module} ships ${candidates}, and none of those modules is installed.`;\n });\n return [heading, ...lines].join(\"\\n\");\n}\n\n/** True for a target under the `.agents/skills/` namespace an `agent.skills` folder fills. */\nfunction isSkillTarget(target: string): boolean {\n return target.startsWith(\".agents/skills/\");\n}\n\n/**\n * A file this run plans to lay down, as the patch preview needs to see it: the content,\n * and whether that content actually reaches disk. A held-back file keeps whatever is\n * there, so its planned content is only a last resort.\n */\nexport interface PatchSourceFile {\n /** The content this run would write; `undefined` when the module dropped the file. */\n content?: string;\n /** True when this run leaves that content on disk. */\n landsOnDisk: boolean;\n}\n\nexport interface PreviewPatchesArgs {\n root: string;\n /** Module that authored these ops — recorded on each preview. */\n module: string;\n ops: RegistryPatch[];\n /** Files this run plans, keyed by project-relative target. */\n planned: ReadonlyMap<string, PatchSourceFile>;\n}\n\n/**\n * Preview a module's config patches against the state this run will leave behind.\n *\n * Shared by `buildPlan` and `buildUpdatePlan` (#98): both resolve a patch's source the\n * same three ways — a file this run writes, else what is on disk, else a held-back\n * file's planned content — and both need the preview to be pure, because `--dry-run`\n * and `--diff` render it without writing. Only genuinely-absent targets are `missing`.\n * Both engines re-apply against fresh disk state at execute time; this is the preview,\n * never the source of truth for the write.\n */\nexport async function previewPatches(\n args: PreviewPatchesArgs\n): Promise<PlannedPatch[]> {\n const { root, module, ops, planned } = args;\n const out: PlannedPatch[] = [];\n for (const op of ops) {\n const fileAbs = resolveWithinRoot(root, op.file);\n const plannedFile = planned.get(op.file);\n const source = plannedFile?.landsOnDisk\n ? plannedFile.content\n : ((await readIfPresent(fileAbs)) ?? plannedFile?.content);\n if (source === undefined) {\n out.push({\n module,\n file: op.file,\n fileAbs,\n patch: op,\n action: \"missing\",\n diff: \"\",\n });\n continue;\n }\n const { content, changed, diff, matched } = applyPatch(source, op, op.file);\n out.push({\n module,\n file: op.file,\n fileAbs,\n patch: op,\n action: changed ? \"apply\" : \"unchanged\",\n diff,\n content,\n ...(matched ? { matched } : {}),\n });\n }\n return out;\n}\n\nasync function classify(\n targetAbs: string,\n target: string,\n newHash: string,\n manifest: Manifest,\n /** The module planning this write, compared against the manifest's recorded owner. */\n module: string\n): Promise<{\n action: FileAction;\n oldContent?: string;\n /**\n * The installed module that owns this file, when it is not the one writing now. The\n * cross-module fact only; `buildPlan` decides whether the claim is legal, because the\n * answer needs the run's `dependsOn` map and a per-file fatal action would leave a plan\n * that must not execute (plan decision Q6).\n */\n ownedBy?: string;\n /**\n * The module the manifest still records as this target's owner, when the file itself is\n * gone and the owner is not the module writing now. Reported, never refused (#107).\n */\n staleOwner?: string;\n}> {\n if (!(await pathExists(targetAbs))) {\n // Nothing on disk to lose, so a stale `managed` entry pointing at another module is\n // not a collision — the file this run writes is the only copy there will be. The\n // entry is still a fact worth saying out loud: the recorded owner keeps its\n // `installed` slot and its applied patches while owning none of the bytes. #107\n // settles that as warn-and-instruct — name the owner, tell the user to run\n // `saasaloy remove <owner>`, and never edit the bookkeeping on their behalf.\n const stale = manifest.managed[target];\n return stale && stale.module !== module\n ? { action: \"create\", staleOwner: stale.module }\n : { action: \"create\" };\n }\n const oldContent = await readFile(targetAbs, \"utf-8\");\n const oldHash = hashContent(oldContent);\n const managed = manifest.managed[target];\n // Reported before the hash branches, because identical bytes are no defence: the\n // manifest entry is refreshed on `unchanged` too, so ownership would still flip (#91).\n const ownedBy =\n managed && managed.module !== module ? { ownedBy: managed.module } : {};\n if (oldHash === newHash) {\n return { action: \"unchanged\", oldContent, ...ownedBy };\n }\n if (managed) {\n return {\n action: managed.hash === oldHash ? \"overwrite\" : \"drift\",\n oldContent,\n ...ownedBy,\n };\n }\n return { action: \"conflict\", oldContent };\n}\n\nasync function planModuleFile(\n module: LoadedModule,\n ref: ModuleFileRef,\n root: string,\n manifest: Manifest\n): Promise<{ file: PlannedFile; ownedBy?: string; staleOwner?: string }> {\n const content = await readFile(ref.abs, \"utf-8\");\n const newHash = hashContent(content);\n // `add` is the one engine whose input is an untrusted remote descriptor, so the target\n // is resolved under the same guard `remover` and `updater` use on their state files\n // (#98). `join()` normalizes a `..` away silently; this refuses it instead.\n const targetAbs = resolveWithinRoot(root, ref.target);\n const { action, oldContent, ownedBy, staleOwner } = await classify(\n targetAbs,\n ref.target,\n newHash,\n manifest,\n module.item.name\n );\n return {\n file: {\n module: module.item.name,\n source: ref.abs,\n from: ref.from,\n target: ref.target,\n targetAbs,\n content,\n newHash,\n action,\n oldContent,\n isSkill: isSkillTarget(ref.target),\n ...(ref.onlyWith === undefined ? {} : { onlyWith: ref.onlyWith }),\n },\n ...(ownedBy === undefined ? {} : { ownedBy }),\n ...(staleOwner === undefined ? {} : { staleOwner }),\n };\n}\n\nexport interface BuildPlanArgs {\n root: string;\n install: string[];\n alreadyInstalled: string[];\n modules: Map<string, LoadedModule>;\n config: SaasaloyConfig;\n manifest: Manifest;\n /**\n * What the user asked for, named in a refusal so a collision raised by a transitive\n * prerequisite still says which command hit it. Omitted by callers that plan a set with\n * no single requested module (`update`'s prerequisite plan).\n */\n requested?: string;\n}\n\nexport async function buildPlan(args: BuildPlanArgs): Promise<Plan> {\n const {\n root,\n install,\n alreadyInstalled,\n modules,\n config,\n manifest,\n requested,\n } = args;\n // Keyed by project-relative target, because two modules in one run can ship the same\n // file: `database` and its driver `database-d1`/`database-postgres` both scaffold\n // `packages/db/tsconfig.json`. Planned as two entries they both classify `create` off\n // an empty disk, the core's copy lands first, and `stillMatches` then reads this run's\n // own bytes and drops the driver's copy as late drift — so the driver's tsconfig never\n // arrived and `packages/db` lost the `types` the driver needs (#98). `install` is\n // topological, so the last planner is the most specific one: let it win, and plan one\n // entry per target.\n const byTarget = new Map<string, PlannedFile>();\n // Every file this run would write that an already-installed *other* module owns, as\n // `classify` reports them. Collected across the whole loop and judged once below, so a\n // module contesting three of another's files raises one refusal naming all three (#91).\n const claims: OwnedCollision[] = [];\n // The same fact about a file that is no longer on disk. Nothing is at risk, so these\n // never refuse: they are collected here and reported by `add` so the user knows a\n // module is left installed owning nothing (#107).\n const staleClaims: OwnedCollision[] = [];\n const links: PlannedLink[] = [];\n const dependencies: string[] = [];\n const devDependencies: string[] = [];\n const envVars: Record<string, string> = {};\n const devVars: Record<string, string> = {};\n const removeWarnings: Record<string, string[]> = {};\n\n // Collect the aliases every scaffold in this run registers up front, so a feature's\n // files[] can resolve against a capability's brand-new alias even when both install in\n // the same run. Topo order already lands the capability first; this makes resolution\n // order-independent and keeps the target-resolving view (below) complete (ADR 0013).\n const aliases: Record<string, string> = {};\n for (const name of install) {\n for (const scaffold of modules.get(name)?.item.scaffolds ?? []) {\n for (const [alias, prefix] of Object.entries(scaffold.aliases ?? {})) {\n aliases[alias] = prefix;\n }\n }\n }\n const aliasConflicts: string[] = [];\n for (const [alias, prefix] of Object.entries(aliases)) {\n const existing = config.aliases[alias];\n if (existing !== undefined && existing !== prefix) {\n aliasConflicts.push(`${alias} → ${existing} redefined as ${prefix}`);\n }\n }\n // Scaffold aliases win over the on-disk map when resolving this run's file targets.\n const aliasView = { ...config.aliases, ...aliases };\n\n // What an `onlyWith` condition is tested against (#99): everything this run installs,\n // plus everything the project already holds. A feature added long after its driver has\n // only the second half; a feature that drags its driver in has only the first.\n const installedSet = new Set([\n ...install,\n ...alreadyInstalled,\n ...config.installed,\n ]);\n const unmatched: UnmatchedTarget[] = [];\n\n // Enumerate every module's files first, so the collision check sees the whole run\n // before a single file is classified. One enumeration for all three rules — `files[]`,\n // each scaffold's workspace files, and each `agent.skills` folder — shared with\n // `update` (#98), so `files[]` and `scaffolds[].files[]` are covered alike here.\n const refsByModule = new Map<string, ModuleFileRef[]>();\n for (const name of install) {\n const mod = modules.get(name);\n if (!mod) {\n continue;\n }\n const selection = await selectModuleFiles(mod, aliasView, installedSet);\n unmatched.push(...selection.unmatched);\n refsByModule.set(name, [...selection.files.values()]);\n }\n\n // Two modules writing the same target is legal only when one reaches the other through\n // `dependsOn` — core-plus-driver keeps last-planner-wins below, an unrelated pair is\n // refused here, before anything is planned (#91).\n const collisions = detectCollisions({\n planned: [...refsByModule].map(([module, refs]) => ({\n module,\n targets: refs.map((ref) => ref.target),\n })),\n modules,\n });\n if (collisions.length > 0) {\n throw new RefusalError(formatCollisions(collisions, requested));\n }\n\n for (const name of install) {\n const mod = modules.get(name);\n if (!mod) {\n continue;\n }\n const { item } = mod;\n\n // A scaffold file's target is workspace-relative and a skill file's lands under\n // `.agents/skills/`; from here every one of them is an ordinary managed file,\n // classified and recorded alike, so create/drift/conflict and `remove` all come\n // for free.\n for (const ref of refsByModule.get(name) ?? []) {\n const {\n file: planned,\n ownedBy,\n staleOwner,\n } = await planModuleFile(mod, ref, root, manifest);\n if (ownedBy !== undefined) {\n claims.push({\n target: planned.target,\n owner: ownedBy,\n claimant: name,\n });\n }\n if (staleOwner !== undefined) {\n staleClaims.push({\n target: planned.target,\n owner: staleOwner,\n claimant: name,\n });\n }\n // Re-key rather than overwrite in place, so the surviving entry is listed under\n // the module that actually writes it.\n byTarget.delete(planned.target);\n byTarget.set(planned.target, planned);\n }\n\n // The skill files themselves are real, committed files under `.agents/skills/<folder>/…`,\n // readable by every AI agent rather than only Claude Code. A `.claude/skills/<folder>`\n // symlink points back at them so Claude Code still discovers the skill (ADR 0015).\n for (const skillRel of item.agent?.skills ?? []) {\n const folderName = posix.basename(skillRel);\n const linkPath = posix.join(\".claude/skills\", folderName);\n const linkTarget = posix.join(\".agents/skills\", folderName);\n // Both sides are engine-built from a descriptor-supplied folder name, so both go\n // through the guard — the name is the untrusted part (#98).\n const pathAbs = resolveWithinRoot(root, linkPath);\n const targetAbs = resolveWithinRoot(root, linkTarget);\n const state = await classifyLink(pathAbs, targetAbs);\n links.push({\n module: name,\n path: linkPath,\n pathAbs,\n target: linkTarget,\n targetAbs,\n action:\n state === \"missing\"\n ? \"create\"\n : state === \"correct\"\n ? \"exists\"\n : \"conflict\",\n });\n }\n\n for (const dep of item.dependencies ?? []) {\n dependencies.push(dep);\n }\n for (const dep of item.devDependencies ?? []) {\n devDependencies.push(dep);\n }\n for (const [key, value] of Object.entries(item.devVars ?? {})) {\n devVars[key] = value;\n }\n for (const [key, value] of Object.entries(item.envVars ?? {})) {\n envVars[key] = value;\n }\n if (item.removeWarnings && item.removeWarnings.length > 0) {\n removeWarnings[name] = [...item.removeWarnings];\n }\n }\n\n // A module may take a file another one owns only when it reaches that owner through\n // `dependsOn`. Judged after every file is classified so the refusal names every\n // contested path at once, and before the plan is returned so `add` never executes it.\n // `--force` cannot reach this: it changes what goes into `install`, not this rule\n // (plan decision Q3).\n const owned = detectOwnedCollisions(claims, modules);\n if (owned.length > 0) {\n throw new RefusalError(formatOwnedCollisions(owned, requested));\n }\n\n // The missing-file half of the same rule, reported rather than refused (#107). Two\n // filters keep the note honest. `mayShareTarget` drops the driver reclaiming a file its\n // `dependsOn` capability applied, because telling the user to remove a module the new\n // one depends on would be wrong. The owner also has to still be installed: an owner\n // absent from `installed` is the mirror state `doctor` reports (#49), not this one.\n const priorInstalled = new Set([...alreadyInstalled, ...config.installed]);\n // Every target this plan writes changes hands, so an owner is left stale only when its\n // remaining `managed` entries are all in that set. That is the same arithmetic `doctor`\n // does, which is what keeps the two messages from contradicting each other: an owner\n // that keeps a file is not flagged there and is not told to `remove` here (#107).\n const changingHands = new Set(byTarget.keys());\n const keepsFiles = (owner: string): boolean =>\n Object.entries(manifest.managed).some(\n ([target, entry]) => entry.module === owner && !changingHands.has(target)\n );\n const staleOwners: StaleOwner[] = staleClaims\n .filter(\n (claim) =>\n priorInstalled.has(claim.owner) &&\n !mayShareTarget(claim.claimant, claim.owner, modules)\n )\n .map((claim) => ({ ...claim, ownerKeepsFiles: keepsFiles(claim.owner) }));\n\n // A target every one of whose variants was filtered out would otherwise be a file that\n // silently never arrived — an app missing its schema, discovered at typecheck. Refuse\n // the whole plan instead, before `executePlan` writes anything, and name what would\n // have satisfied it. A refusal, not a failure: `add` exits 2 (#98's code scheme).\n if (unmatched.length > 0) {\n throw new RefusalError(formatUnmatchedTargets(unmatched));\n }\n\n const files = [...byTarget.values()];\n\n // Plan patches after every file is collected, so an op targeting a file another module\n // scaffolds this same run previews against that file's *would-be* content, not disk.\n const planned = new Map<string, PatchSourceFile>(\n files.map((f) => [\n f.target,\n { content: f.content, landsOnDisk: SAFE.has(f.action) },\n ])\n );\n const patches: PlannedPatch[] = [];\n for (const name of install) {\n patches.push(\n ...(await previewPatches({\n root,\n module: name,\n ops: modules.get(name)?.item.patches ?? [],\n planned,\n }))\n );\n }\n\n return {\n install,\n alreadyInstalled,\n files,\n links,\n dependencies,\n devDependencies,\n devVars,\n envVars,\n aliases,\n aliasConflicts,\n staleOwners,\n patches,\n removeWarnings,\n };\n}\n\nexport interface ApplyResult {\n /** Modules of `plan.install` whose planned file writes all landed — the list\n * `config.installed` and the lock's freshly-installed entries derive from (#49). */\n completed: string[];\n written: PlannedFile[];\n /** Files already byte-identical to what we'd write — manifest refreshed, disk untouched. */\n refreshed: PlannedFile[];\n /** Planned-writable files edited between planning and the write — left alone. */\n lateDrift: PlannedFile[];\n /** drift + conflict files, held back for the merge path. */\n heldBack: PlannedFile[];\n /** `.claude/skills` symlinks created or already correct, recorded in the manifest. */\n links: PlannedLink[];\n /** Symlinks left untouched because something else already occupies their path. */\n linkConflicts: PlannedLink[];\n /** Config patches that actually changed their target file. */\n patched: PlannedPatch[];\n /** Patches whose target file was absent — reported, not applied. */\n patchConflicts: PlannedPatch[];\n /** Patches the codemod declined to apply, each with the reason it gave. Distinct from\n * an idempotent no-op, which changes nothing and has nothing to report. */\n patchRefusals: PatchRefusal[];\n}\n\n/** A patch left unapplied on purpose, and the codemod's own account of why. */\nexport interface PatchRefusal {\n patch: PlannedPatch;\n reason: string;\n}\n\n/**\n * Whether a planned file still looks the way it did when the plan was built. The\n * confirmation prompt sits between the two and can stay open indefinitely, so the\n * verdict is re-earned immediately before the write — the same reason `remover.ts`\n * re-checks before deleting and `updater.ts` before overwriting (#98). A planned\n * `create` matches only while its path is still empty.\n */\nasync function stillMatches(file: PlannedFile): Promise<boolean> {\n return (await readIfPresent(file.targetAbs)) === file.oldContent;\n}\n\n// Write the safe files, record each in the manifest with its content hash, and mark\n// the modules installed. Drift/conflict files are left on disk untouched and returned\n// so the caller can emit an AI-merge plan (the non-deterministic seam, build spec §2.9).\nexport async function executePlan(\n plan: Plan,\n root: string,\n config: SaasaloyConfig,\n manifest: Manifest\n): Promise<ApplyResult> {\n const written: PlannedFile[] = [];\n const refreshed: PlannedFile[] = [];\n const lateDrift: PlannedFile[] = [];\n const heldBack: PlannedFile[] = [];\n const links: PlannedLink[] = [];\n const linkConflicts: PlannedLink[] = [];\n const patched: PlannedPatch[] = [];\n const patchConflicts: PlannedPatch[] = [];\n const patchRefusals: PatchRefusal[] = [];\n\n for (const file of plan.files) {\n if (!SAFE.has(file.action)) {\n heldBack.push(file);\n continue;\n }\n if (!(await stillMatches(file))) {\n // Edited while the confirmation was up — the plan the user approved described\n // different bytes, so it doesn't authorize overwriting these.\n lateDrift.push(file);\n continue;\n }\n if (WRITABLE.has(file.action)) {\n // `resolveWithinRoot` proved the path is lexically inside the project; `writeFile`\n // still follows links, so a planted symlink on any component would carry the write\n // outside it. Refuse before creating the parent dirs (#98), matching `remover`.\n await assertNoSymlinkPath(root, file.targetAbs);\n await mkdir(dirname(file.targetAbs), { recursive: true });\n await writeFile(file.targetAbs, file.content, \"utf-8\");\n written.push(file);\n } else {\n // `unchanged`: disk already holds these exact bytes. Its manifest entry is still\n // refreshed — that is how a file installed before `from` existed acquires one.\n refreshed.push(file);\n }\n manifest.managed[file.target] = {\n module: file.module,\n hash: file.newHash,\n from: file.from,\n };\n }\n\n // Apply structural patches after the file writes, so an op targeting a freshly-scaffolded\n // file (e.g. `database`'s D1 binding into api's just-written wrangler.jsonc) reads the real\n // content. Re-read disk here rather than trusting the plan's preview — the file may have been\n // held back as drift/conflict. The engine is idempotent, so a re-apply is a clean no-op, and a\n // patched file is deliberately *not* re-tracked in the manifest (it's another module's copy).\n for (const p of plan.patches) {\n if (!(await pathExists(p.fileAbs))) {\n patchConflicts.push(p);\n continue;\n }\n // The read below follows links just as the write does, so guard before either (#98).\n await assertNoSymlinkPath(root, p.fileAbs);\n const source = await readFile(p.fileAbs, \"utf-8\");\n const { content, changed, reason } = applyPatch(source, p.patch, p.file);\n if (changed) {\n await writeFile(p.fileAbs, content, \"utf-8\");\n patched.push(p);\n // Record for `remove` (which reverses the four config kinds and warns for the two\n // `package.json` kinds) — deduped so a `--force` re-apply that lands the same op\n // again doesn't duplicate the entry.\n const entry: ManifestPatch = {\n module: p.module,\n file: p.file,\n patch: p.patch,\n };\n if (\n !manifest.patches.some((existing) => samePatchEntry(existing, entry))\n ) {\n manifest.patches.push(entry);\n }\n } else if (reason !== undefined) {\n // The codemod refused rather than no-op'd: nothing is written and nothing is\n // tracked, so the manifest keeps saying `remove` has no business in this file.\n patchRefusals.push({ patch: p, reason });\n }\n }\n\n // Point `.claude/skills/<name>` at the real `.agents/skills/<name>` folder written above so\n // Claude Code discovers the skill. The native link (junction on Windows, symlink elsewhere) is\n // regenerated per-machine and git-ignored; the manifest tracks source→link for a clean `remove`.\n for (const link of plan.links) {\n if (link.action === \"conflict\") {\n linkConflicts.push(link);\n continue;\n }\n if (link.action === \"create\") {\n // Only the `create` leg writes. An `exists` link is our own symlink at the final\n // component, which the guard would (correctly) refuse, so it is not asked (#98).\n await assertNoSymlinkPath(root, link.pathAbs);\n await createDirLink(link.pathAbs, link.targetAbs);\n }\n manifest.links[link.target] = link.path;\n links.push(link);\n }\n\n // Register the aliases the scaffolds declared so the first feature targeting them\n // resolves against a real path (ADR 0013). Merge is idempotent on re-apply; a conflicting\n // redefinition was surfaced at plan time (plan.aliasConflicts) — last write wins here.\n for (const [alias, prefix] of Object.entries(plan.aliases)) {\n config.aliases[alias] = prefix;\n }\n\n // A module counts as installed once its clean files have landed — completeness comes\n // from what this run did, never from what it intended (#49). The gate is authorization,\n // not bytes: a `lateDrift` file was edited after the user approved the plan, so that\n // approval doesn't describe the module now on disk and the module stays uninstalled,\n // which is what lets a plain re-run re-plan it and ask again. A plan-time `heldBack`\n // file was shown as held *before* the approval, so it never blocks, and patch failures\n // are reported on their own — file writes alone decide this. A module that plans no\n // file writes has nothing that could fail, so it completes.\n const drifted = new Set(lateDrift.map((file) => file.module));\n const completed = plan.install.filter((name) => !drifted.has(name));\n\n // Preserve insertion order and dedupe against what's already there.\n for (const name of completed) {\n if (!config.installed.includes(name)) {\n config.installed.push(name);\n }\n }\n // Warnings track the files that did land, so they follow `plan.install`: an incomplete\n // module still wrote some of its files, and `remove` must warn about those.\n for (const name of plan.install) {\n const warnings = plan.removeWarnings[name];\n if (warnings) {\n manifest.removeWarnings[name] = [...warnings];\n } else {\n delete manifest.removeWarnings[name];\n }\n }\n\n return {\n completed,\n written,\n refreshed,\n lateDrift,\n heldBack,\n links,\n linkConflicts,\n patched,\n patchConflicts,\n patchRefusals,\n };\n}\n","// The CLI's exit-code vocabulary, plus the error formatter every command's catch block\n// shares. Before #98 every failure exited 1, so a script could not tell \"saasaloy\n// refused because the driver conflicts\" from \"the network broke mid-fetch\". Three codes\n// now carry that difference:\n//\n// 0 — the command did what it was asked, or the user answered \"no\" to a confirm.\n// 1 — something failed or the user cancelled: a fetch died, a write threw, Ctrl-C.\n// 2 — saasaloy refused by design: bad usage, a module conflict, an unmet requirement,\n// an invalid manifest, or a prompt with no terminal to answer it in.\n//\n// A wrapper script reads 2 as \"the input is wrong, do not retry\" and 1 as \"transient,\n// a retry may work\". Nothing else may take on a third meaning; add a code here first.\n\nexport const EXIT_OK = 0;\nexport const EXIT_FAILURE = 1;\nexport const EXIT_REFUSED = 2;\n\n/**\n * A failure saasaloy chose, thrown from a library that has no exit code to return.\n * `loadConfig` rejecting an invalid `saasaloy.json` is the shape: the command above it\n * catches, sees this type, and exits 2 instead of 1. A plain `Error` stays a 1.\n */\nexport class RefusalError extends Error {\n override readonly name = \"RefusalError\";\n}\n\n/** True when this error is a refusal saasaloy made, rather than a failure it hit. */\nexport function isRefusal(error: unknown): error is RefusalError {\n return error instanceof RefusalError;\n}\n\n/** `EXIT_REFUSED` for a refusal, `EXIT_FAILURE` for anything else. */\nexport function exitCodeFor(error: unknown): number {\n return isRefusal(error) ? EXIT_REFUSED : EXIT_FAILURE;\n}\n\n/**\n * Set this to any non-empty value to print the whole `cause` chain under the message.\n * `registry.ts` wraps a fetch failure with `{ cause: error }` and the top level printed\n * only `error.message`, so the underlying `ECONNREFUSED` never reached a bug report.\n */\nexport const DEBUG_ENV = \"SAASALOY_DEBUG\";\n\nfunction isDebug(): boolean {\n return (process.env[DEBUG_ENV] ?? \"\") !== \"\";\n}\n\n// Guard against a cause cycle (`a.cause = b; b.cause = a`), which a hand-built error\n// can produce and which would otherwise loop forever.\nconst MAX_CAUSE_DEPTH = 10;\n\n/** Every `cause` under `error`, outermost first. Empty when nothing wrapped anything. */\nexport function causeChain(error: unknown): unknown[] {\n const chain: unknown[] = [];\n const seen = new Set<unknown>();\n let current: unknown = error;\n while (chain.length < MAX_CAUSE_DEPTH) {\n if (!(current instanceof Error) || current.cause === undefined) {\n break;\n }\n if (seen.has(current)) {\n break;\n }\n seen.add(current);\n current = current.cause;\n chain.push(current);\n }\n return chain;\n}\n\nfunction describe(value: unknown): string {\n if (value instanceof Error) {\n return value.stack ?? `${value.name}: ${value.message}`;\n }\n return String(value);\n}\n\n/**\n * The one-line message a command shows, plus the cause chain and stack when\n * `SAASALOY_DEBUG` is set. Every `catch` in the commands formats through this, so the\n * debug switch reaches all of them rather than the one that remembered it.\n */\nexport function formatFailure(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n if (!isDebug()) {\n return message;\n }\n const lines = [describe(error)];\n for (const [index, cause] of causeChain(error).entries()) {\n lines.push(`${\" \".repeat(index + 1)}caused by: ${describe(cause)}`);\n }\n return lines.join(\"\\n\");\n}\n","import type { LoadedModule } from \"./registry.js\";\n\n// Cross-module file collisions for `saasaloy add` — the deterministic core, kept beside\n// conflicts.ts and out of applier.ts's planning loop so it can be unit-tested.\n//\n// Two modules in one run may legitimately write the same target: `database` scaffolds\n// `packages/db/tsconfig.json` and its driver `database-d1` scaffolds a more specific one\n// over it, which is why buildPlan plans one entry per target and lets the last planner win\n// (#98). Two unrelated modules picking the same target is the opposite case: nothing says\n// which copy the user wanted, and the silent overwrite is a data-loss bug (#91).\n//\n// The rule that separates them is `dependsOn`. An overlap is legal when one of the two\n// modules reaches the other through `dependsOn`, because that edge is the author saying\n// \"I am built on top of that module and I know what it ships\". Everything else is refused.\n// No new descriptor field: `dependsOn` is the declaration (plan decision Q4).\n//\n// A collision is reported, never resolved. `buildPlan` raises one RefusalError naming\n// every contested path, before it plans a single write.\n\nexport interface FileCollision {\n /** Project-relative POSIX path both modules plan to write. */\n target: string;\n /** The module that claimed the target first — earlier in the run's install order. */\n module: string;\n /** The later module claiming the same target. */\n other: string;\n}\n\n/**\n * One file already on disk that a module in this run wants to write, and the installed\n * module `.saasaloy/manifest.json` says owns it. `classify` reports the pair; the rule\n * below decides whether the claimant may take it (#91 phase 2).\n */\nexport interface OwnedCollision {\n /** Project-relative POSIX path of the contested file. */\n target: string;\n /** The installed module that applied the file, per `manifest.managed[target].module`. */\n owner: string;\n /** The module this run would have overwrite it. */\n claimant: string;\n}\n\n/** One module's planned targets, as `listModuleFiles` enumerates them. */\nexport interface ModuleTargets {\n module: string;\n /** Project-relative POSIX targets — `files[]`, `scaffolds[].files[]` and skills alike. */\n targets: readonly string[];\n}\n\nexport interface DetectCollisionsArgs {\n /** Every module in this run with its planned targets, in install (topological) order. */\n planned: readonly ModuleTargets[];\n /**\n * Every descriptor this run resolved, keyed by name — the map `buildPlan` already\n * receives. Reachability is read off `dependsOn` here rather than off a `Graph`,\n * because `BuildPlanArgs` carries no graph.\n */\n modules: ReadonlyMap<string, LoadedModule>;\n}\n\n/** True when `from` reaches `to` by following `dependsOn`, at any depth. */\nfunction reaches(\n from: string,\n to: string,\n modules: ReadonlyMap<string, LoadedModule>\n): boolean {\n const seen = new Set<string>([from]);\n const stack: string[] = [from];\n while (stack.length > 0) {\n const name = stack.pop() ?? \"\";\n for (const dep of modules.get(name)?.item.dependsOn ?? []) {\n if (dep === to) {\n return true;\n }\n // A name with no descriptor in this map ends the walk there rather than throwing:\n // resolveGraph visits every `dependsOn` recursively, so a gap means a caller built\n // a partial map, and a missing edge can only make the check stricter.\n if (!seen.has(dep)) {\n seen.add(dep);\n stack.push(dep);\n }\n }\n }\n return false;\n}\n\n/**\n * True when these two modules may share a target: one of them declares the other in\n * `dependsOn`, directly or transitively. The relation is checked both ways — the driver\n * usually depends on the capability, but either side may be the one that declares it.\n */\nexport function mayShareTarget(\n a: string,\n b: string,\n modules: ReadonlyMap<string, LoadedModule>\n): boolean {\n return a === b || reaches(a, b, modules) || reaches(b, a, modules);\n}\n\n/**\n * Every illegal same-run overlap, in install order and then target order, so the refusal\n * reads the same on every run. Each claimant of a target reports one pair per earlier\n * claimant, so three unrelated modules on one target report three pairs, because each\n * pair is its own decision to fix.\n */\nexport function detectCollisions(args: DetectCollisionsArgs): FileCollision[] {\n const { planned, modules } = args;\n const claimants = new Map<string, string[]>();\n const collisions: FileCollision[] = [];\n\n for (const { module, targets } of planned) {\n for (const target of targets) {\n const prior = claimants.get(target);\n if (!prior) {\n claimants.set(target, [module]);\n continue;\n }\n for (const other of prior) {\n if (!mayShareTarget(module, other, modules)) {\n collisions.push({ target, module: other, other: module });\n }\n }\n prior.push(module);\n }\n }\n\n return collisions;\n}\n\n// One collision as a sentence: both module names, the contested path, and the two ways\n// out. `conflictsWith` is named because a deliberate either-or pair is the case this\n// refusal must not stand in the way of.\nfunction describe(collision: FileCollision): string {\n const { target, module, other } = collision;\n return (\n `${module} and ${other} both write ${target}, and neither declares the other in \\`dependsOn\\`. ` +\n `Give one of them a different target, or declare \\`conflictsWith\\` between them if they are deliberately exclusive.`\n );\n}\n\n/** The refusal `add` prints. One line per contested path, under a heading. */\nexport function formatCollisions(\n collisions: FileCollision[],\n /** What the user asked for; a caller planning a set with no single request omits it. */\n requested = \"these modules\"\n): string {\n const heading = `Cannot add ${requested} — file collision${collisions.length > 1 ? \"s\" : \"\"}:`;\n return [heading, ...collisions.map((c) => ` ${describe(c)}`)].join(\"\\n\");\n}\n\n/**\n * Every claim on an installed file that crosses module ownership illegally, in the order\n * `buildPlan` classified them. The rule is `mayShareTarget`, the same one the same-run\n * check uses: a module may take a file owned by a module it reaches through `dependsOn`,\n * and nothing else (plan decision Q4).\n *\n * The relation stays symmetric here, as it is in the same-run case. `add database --force`\n * on a project where `database-d1` took over `packages/db/tsconfig.json` is the capability\n * rewriting its own driver's file, and refusing it would tell the user to remove a driver\n * that depends on the very module being installed.\n *\n * An owner with no descriptor in `modules` reads as unrelated, which refuses the claim.\n * That is the safe answer: the run resolved every module it could reach, so an owner it\n * never saw is one nothing in this run depends on.\n */\nexport function detectOwnedCollisions(\n claims: readonly OwnedCollision[],\n modules: ReadonlyMap<string, LoadedModule>\n): OwnedCollision[] {\n return claims.filter(\n (claim) => !mayShareTarget(claim.claimant, claim.owner, modules)\n );\n}\n\n// One owned-file claim as a sentence. `--force` is named because it is the flag a user\n// reaches for next, and it deliberately does not cross ownership — `remove` does, because\n// it is the one path that leaves the manifest consistent (plan decision Q3).\nfunction describeOwned(collision: OwnedCollision): string {\n const { target, owner, claimant } = collision;\n return (\n `${owner} owns ${target}, and ${claimant} does not declare it in \\`dependsOn\\`. ` +\n `\\`--force\\` does not cross module file ownership: run \\`saasaloy remove ${owner}\\` first.`\n );\n}\n\n/**\n * A stale-ownership note: the manifest owner of a file that is gone, plus whether that\n * owner still owns manifest entries this run does not take over. `remove` deletes every\n * file a module owns, so the advice to run it is only safe when the owner is left owning\n * nothing (#107).\n */\nexport interface StaleOwner extends OwnedCollision {\n /** True when the owner keeps at least one managed entry after this run writes. */\n ownerKeepsFiles: boolean;\n}\n\n/**\n * One stale-ownership note as a sentence. The file is gone, so nothing is refused: the\n * claim is legal and the run continues. What is left behind is an installed module that\n * no longer owns the file it applied, and only `remove` clears that (#107).\n *\n * An owner that keeps other files is not stale once the run ends — `doctor` would not\n * flag it — so that sentence states the transfer and stops. Naming `remove` there would\n * tell the user to delete files they still use.\n */\nexport function describeStaleOwner(collision: StaleOwner): string {\n const { target, owner, claimant, ownerKeepsFiles } = collision;\n const head = `${owner} still owns ${target} in the manifest, but the file is gone — ${claimant} now writes it.`;\n return ownerKeepsFiles\n ? `${head} ${owner} keeps its other files, so leave it installed.`\n : `${head} Run \\`saasaloy remove ${owner}\\` to drop the stale module.`;\n}\n\n/** The refusal `add` prints for files another installed module owns. */\nexport function formatOwnedCollisions(\n collisions: OwnedCollision[],\n /** What the user asked for; a caller planning a set with no single request omits it. */\n requested = \"these modules\"\n): string {\n const noun = collisions.length > 1 ? \"files\" : \"file\";\n const heading = `Cannot add ${requested} — ${noun} owned by another module:`;\n return [heading, ...collisions.map((c) => ` ${describeOwned(c)}`)].join(\n \"\\n\"\n );\n}\n","import { createHash } from \"node:crypto\";\nimport {\n access,\n lstat,\n mkdir,\n readdir,\n readFile,\n readlink,\n symlink,\n} from \"node:fs/promises\";\nimport {\n dirname,\n isAbsolute,\n join,\n posix,\n relative,\n resolve,\n sep,\n} from \"node:path\";\nimport { RefusalError } from \"./exit.js\";\n\n/** sha256 hex digest of a string — used to fingerprint managed files in the manifest. */\nexport function hashContent(content: string): string {\n return createHash(\"sha256\").update(content, \"utf-8\").digest(\"hex\");\n}\n\n/**\n * Resolve a project-relative POSIX path (a manifest key, a manifest link value, a\n * descriptor target) to an absolute path that is guaranteed to sit inside `root`.\n *\n * The state files this resolves from are persisted JSON, so their keys are only as\n * trustworthy as the last process that wrote them. `join()` silently normalizes a\n * `..` segment, which is how a corrupt or hand-edited manifest turns into a delete\n * outside the project. Reject the shape up front rather than trusting the result.\n *\n * @param hint appended to the refusal, naming where the bad path came from.\n * @throws {Error} if the path is absolute, contains a `..`/`.`/empty segment, carries a\n * platform-specific separator, or otherwise escapes `root`.\n */\nexport function resolveWithinRoot(\n root: string,\n relPosixPath: string,\n hint = \"This usually means the state file that recorded it is corrupt or was hand-edited.\"\n): string {\n const reject = (why: string): never => {\n throw new RefusalError(\n `Refusing to resolve ${JSON.stringify(relPosixPath)}: ${why}. ${hint}`\n );\n };\n\n // A backslash also covers `C:\\…`, so drive-letter paths never reach the resolve below.\n if (relPosixPath.includes(\"\\\\\")) {\n reject(\"paths must use '/' separators\");\n }\n if (isAbsolute(relPosixPath) || /^[a-zA-Z]:/.test(relPosixPath)) {\n reject(\"path must be project-relative\");\n }\n\n const segments = relPosixPath.split(\"/\");\n if (segments.some((s) => s === \"\" || s === \".\" || s === \"..\")) {\n reject(\"path must not contain empty, '.' or '..' segments\");\n }\n\n const rootAbs = resolve(root);\n const abs = resolve(rootAbs, ...segments);\n // Belt and braces: catches anything the segment checks above missed (symlinked\n // roots, exotic normalization) before a caller deletes what comes back.\n if (abs !== rootAbs && !abs.startsWith(rootAbs + sep)) {\n reject(\"path escapes the project root\");\n }\n\n return abs;\n}\n\n/**\n * Reject a path that reaches its target through a symlink, at any component below `root`.\n *\n * `resolveWithinRoot` proves only that the *lexical* path stays inside the project. Every\n * `readFile`/`writeFile` that follows resolves links, so a symlink planted anywhere along\n * the path turns an in-root state-file entry into a read or write outside the project.\n * Walk the components from `root` down and refuse the first link found.\n *\n * A component that doesn't exist yet ends the walk: there is no link to follow, and\n * whether the caller may create it is its own question.\n *\n * @throws {Error} if any component of `abs` below `root` is a symlink.\n */\nexport async function assertNoSymlinkPath(\n root: string,\n abs: string\n): Promise<void> {\n const rootAbs = resolve(root);\n const rel = relative(rootAbs, resolve(abs));\n if (rel === \"\") {\n return;\n }\n\n let current = rootAbs;\n for (const segment of rel.split(sep)) {\n current = join(current, segment);\n let stat: Awaited<ReturnType<typeof lstat>>;\n try {\n stat = await lstat(current);\n } catch {\n return; // not on disk yet — nothing here can be followed\n }\n if (stat.isSymbolicLink()) {\n throw new RefusalError(\n `Refusing to touch ${JSON.stringify(rel.split(sep).join(\"/\"))}: ${JSON.stringify(segment)} is a symlink, and following it would leave the project root. This usually means the state file that recorded the path is corrupt or was hand-edited.`\n );\n }\n }\n}\n\n/**\n * Recursively list the files under `dir` as POSIX paths relative to it. Used to expand\n * an `agent.skills` folder into the individual files a module ships, by both the\n * applier (at add time) and the updater (comparing two SHAs of the same folder).\n */\nexport async function listFilesRelative(\n dir: string,\n prefix = \"\"\n): Promise<string[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const out: string[] = [];\n for (const entry of entries) {\n const rel = prefix ? posix.join(prefix, entry.name) : entry.name;\n if (entry.isDirectory()) {\n out.push(...(await listFilesRelative(join(dir, entry.name), rel)));\n } else if (entry.isFile()) {\n out.push(rel);\n }\n }\n return out;\n}\n\n/**\n * Names of the immediate subdirectories of `dir` (skips files); `[]` if `dir` is missing\n * or isn't a directory at all.\n *\n * Anything else — a permission error, an I/O failure — is rethrown rather than folded\n * into that empty array. Both callers loop over the result, so a swallowed error reads\n * as \"there was nothing here\", and the caller reports neither the work nor the reason\n * it didn't happen.\n *\n * @throws {Error} if `dir` exists but cannot be read.\n */\nexport async function readDirNames(dir: string): Promise<string[]> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.filter((e) => e.isDirectory()).map((e) => e.name);\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\" || code === \"ENOTDIR\") {\n return [];\n }\n throw error;\n }\n}\n\n/**\n * Resolve a descriptor-authored POSIX source path under a module folder.\n *\n * A module folder is a temp dir or a local checkout, so it is the root here rather than\n * the project. The path itself comes from an untrusted `registry-item.json`: Phase 1\n * guarded every write *target* and left the read side on a bare `join`, which normalizes\n * a `..` away silently, so `\"path\": \"../../../etc/passwd\"` with an in-root target copied\n * a host file into the project. Same guard, module folder as the root (#98).\n *\n * @throws {RefusalError} if the source path escapes the module folder.\n */\nexport function joinModulePath(dir: string, relPosix: string): string {\n return resolveWithinRoot(\n dir,\n relPosix,\n \"A module's source paths must stay inside the module folder; this descriptor is malformed or hostile.\"\n );\n}\n\n/** The file's content, or `undefined` when nothing is at that path. */\nexport async function readIfPresent(abs: string): Promise<string | undefined> {\n return (await pathExists(abs)) ? readFile(abs, \"utf-8\") : undefined;\n}\n\n/** True if the path exists (file, dir, or symlink). */\nexport async function pathExists(target: string): Promise<boolean> {\n try {\n await access(target);\n return true;\n } catch {\n return false;\n }\n}\n\n// How a would-be directory symlink relates to what's already at its path:\n// missing — nothing there, safe to create\n// correct — already a symlink/junction resolving to the intended target (idempotent no-op)\n// conflict — a real file/dir, or a symlink pointing elsewhere (don't clobber)\nexport type LinkState = \"missing\" | \"correct\" | \"conflict\";\n\n/** Classify a would-be directory symlink at `linkAbs` that should point to `targetAbs`. */\nexport async function classifyLink(\n linkAbs: string,\n targetAbs: string\n): Promise<LinkState> {\n let stat: Awaited<ReturnType<typeof lstat>>;\n try {\n stat = await lstat(linkAbs);\n } catch {\n return \"missing\";\n }\n if (!stat.isSymbolicLink()) {\n return \"conflict\";\n }\n // POSIX links read back relative to the link's own dir; Windows junctions read back absolute.\n const dest = await readlink(linkAbs);\n const resolved = resolve(dirname(linkAbs), dest);\n return resolved === resolve(targetAbs) ? \"correct\" : \"conflict\";\n}\n\n/**\n * Create a directory symlink that works cross-platform: a junction on Windows (needs no admin\n * rights and takes an absolute target) and a relative `dir` symlink elsewhere (so the project\n * stays portable when moved). Creates the parent directory first.\n */\nexport async function createDirLink(\n linkAbs: string,\n targetAbs: string\n): Promise<void> {\n await mkdir(dirname(linkAbs), { recursive: true });\n await (process.platform === \"win32\"\n ? symlink(resolve(targetAbs), linkAbs, \"junction\")\n : symlink(relative(dirname(linkAbs), targetAbs), linkAbs, \"dir\"));\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { RefusalError } from \"./exit.js\";\nimport { pathExists } from \"./fs-utils.js\";\nimport { validateManifest } from \"./schema.js\";\nimport type { RegistryPatch } from \"./schema.js\";\n\n// `.saasaloy/manifest.json` records every file a module applied — copied source\n// files and copied skill files — by owning module and content hash. On update the\n// tool re-hashes a managed file: match → safe overwrite; drift → route to AI-merge.\n// This replaces in-file `// saasaloy:managed` markers (see build spec §2.9 / §3.2).\n// Shape is validated by schemas/manifest.schema.json (see lib/schema.ts).\n\nexport interface ManagedEntry {\n /** Name of the module that applied this file. */\n module: string;\n hash: string;\n /**\n * Module-relative source path this file was copied from (e.g. `files/lib/email.ts`).\n * `update` needs it to fetch the same file at two commit SHAs, which the hash alone\n * can't express. Optional: entries written before this shipped don't carry it, and\n * `update` falls back to re-deriving the target from the descriptor (issue #48).\n */\n from?: string;\n}\n\n// A structural config patch that actually landed on disk, recorded so `remove` can undo\n// it — or, for a kind with no inverse, warn the user which file it can't clean up (see\n// the `PlannedPatch` comment in applier.ts). `patch` is the op as authored, which is all\n// `reversePatch` reads: the four config kinds are reversible from this record alone, and\n// the record carries no pre-patch content or hash, so drift is detected structurally\n// against the recorded payload rather than by comparison (#36).\nexport interface ManifestPatch {\n /** Name of the module that applied this patch. */\n module: string;\n /** Project-relative POSIX path of the file that was patched. */\n file: string;\n /** The op as authored (kind + payload + file). */\n patch: RegistryPatch;\n}\n\nexport interface Manifest {\n managed: Record<string, ManagedEntry>;\n links: Record<string, string>;\n patches: ManifestPatch[];\n /** Module name to warnings shown before removal. */\n removeWarnings: Record<string, string[]>;\n}\n\nexport const MANIFEST_FILE = join(\".saasaloy\", \"manifest.json\");\n\n/**\n * Structural equality on the (module, file, patch) triple — good enough since both sides\n * are parsed from the same registry-item.json descriptor, so key order is stable across a\n * `--force` re-apply. `add` dedupes new entries with it; `remove` untracks with it, and\n * needs the structural form rather than reference identity because the plan it walks and\n * the manifest it edits can be separate loads of the same file.\n */\nexport function samePatchEntry(a: ManifestPatch, b: ManifestPatch): boolean {\n return (\n a.module === b.module &&\n a.file === b.file &&\n JSON.stringify(a.patch) === JSON.stringify(b.patch)\n );\n}\n\nexport function emptyManifest(): Manifest {\n return { links: {}, managed: {}, patches: [], removeWarnings: {} };\n}\n\nexport async function loadManifest(root: string): Promise<Manifest> {\n const file = join(root, MANIFEST_FILE);\n if (!(await pathExists(file))) {\n return emptyManifest();\n }\n // A half-written file parses as nothing at all, which is as invalid as a bad hash and\n // takes the same exit code. Left to throw, `SyntaxError` reaches `exitCodeFor` as a\n // plain failure (1) and never names the file it came from.\n let raw: unknown;\n try {\n raw = JSON.parse(await readFile(file, \"utf-8\"));\n } catch (error) {\n throw new RefusalError(\n `${MANIFEST_FILE} is invalid:\\n ${error instanceof Error ? error.message : String(error)}`,\n { cause: error }\n );\n }\n // This file decides whether a target is safe to overwrite, so a hash that isn't a\n // digest or a half-written entry has to stop the command rather than reach the applier\n // as a typed object that lies about its shape (#98). Same posture as `loadConfig`.\n const result = await validateManifest(raw);\n if (!result.valid) {\n throw new RefusalError(\n `${MANIFEST_FILE} is invalid:\\n ${result.errors.join(\"\\n \")}`\n );\n }\n const parsed = raw as Partial<Manifest>;\n return {\n links: parsed.links ?? {},\n managed: parsed.managed ?? {},\n patches: parsed.patches ?? [],\n removeWarnings: parsed.removeWarnings ?? {},\n };\n}\n\nexport async function saveManifest(\n root: string,\n manifest: Manifest\n): Promise<void> {\n const file = join(root, MANIFEST_FILE);\n await mkdir(dirname(file), { recursive: true });\n await writeFile(file, `${JSON.stringify(manifest, null, 2)}\\n`, \"utf-8\");\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport AjvDefault from \"ajv/dist/2020.js\";\nimport type { ErrorObject, ValidateFunction } from \"ajv\";\nimport { pathExists } from \"./fs-utils.js\";\nimport type { Patch } from \"./patch/index.js\";\n\n// ajv is CJS (`module.exports = Ajv2020`); under NodeNext the default import is typed\n// as the module namespace, so re-point it to the class it actually is at runtime.\nconst Ajv2020 = AjvDefault as unknown as typeof AjvDefault.default;\n\n// $schema-validated forcing functions for the three Saasaloy descriptors: the\n// consumer manifest (saasaloy.json), the managed-file manifest (.saasaloy/manifest.json),\n// and the module descriptor (registry-item.json). Authored descriptors validate\n// against the JSON Schema documents in ../schemas so a typo fails fast with a clear\n// error rather than surfacing as a mysterious applier crash later (build spec §3.2/§3.3).\n\nexport type SchemaName =\n \"saasaloy\" | \"manifest\" | \"registry-item\" | \"saasaloy-lock\";\n\nconst SCHEMA_FILES: Record<SchemaName, string> = {\n manifest: \"manifest.schema.json\",\n \"registry-item\": \"registry-item.schema.json\",\n saasaloy: \"saasaloy.schema.json\",\n \"saasaloy-lock\": \"saasaloy-lock.schema.json\",\n};\n\n// Schemas ship beside dist/ (see package.json \"files\"). At runtime import.meta.url is\n// <pkg>/dist/index.js so ../schemas resolves to <pkg>/schemas; under vitest it's\n// <pkg>/src/lib/schema.ts, so the schemas sit one level further up. Try both and cache.\nconst SCHEMA_DIR_CANDIDATES = [\"../schemas\", \"../../schemas\"];\nlet schemaDirPromise: Promise<string> | undefined;\n\nasync function schemaDir(): Promise<string> {\n schemaDirPromise ??= (async () => {\n for (const candidate of SCHEMA_DIR_CANDIDATES) {\n const dir = fileURLToPath(new URL(candidate, import.meta.url));\n if (await pathExists(join(dir, \"registry-item.schema.json\"))) {\n return dir;\n }\n }\n // Fall back to the packaged location for a sensible ENOENT message.\n return fileURLToPath(new URL(\"../schemas\", import.meta.url));\n })();\n return schemaDirPromise;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n /** Human-readable, one-per-line messages; empty when valid. */\n errors: string[];\n}\n\n// One Ajv instance, validators compiled lazily and cached — schemas are read off\n// disk on first use so the CLI pays nothing for schemas it never touches.\nconst ajv = new Ajv2020({ allErrors: true });\nconst validators = new Map<SchemaName, ValidateFunction>();\n\nasync function getValidator(name: SchemaName): Promise<ValidateFunction> {\n const cached = validators.get(name);\n if (cached) {\n return cached;\n }\n const schema = JSON.parse(\n await readFile(join(await schemaDir(), SCHEMA_FILES[name]), \"utf-8\")\n ) as object;\n const validator = ajv.compile(schema);\n validators.set(name, validator);\n return validator;\n}\n\n/** Validate `data` against a named schema, returning clear messages on failure. */\nexport async function validate(\n name: SchemaName,\n data: unknown\n): Promise<ValidationResult> {\n const fn = await getValidator(name);\n const valid = fn(data);\n return { errors: valid ? [] : (fn.errors ?? []).map(formatError), valid };\n}\n\n// Turn an Ajv error into a single readable line. The default `message` omits the\n// offending key for `additionalProperties`/`required`, which is exactly what an\n// author needs to spot a typo, so we splice it back in.\nfunction formatError(err: ErrorObject): string {\n const where = err.instancePath || \"(root)\";\n switch (err.keyword) {\n case \"additionalProperties\": {\n return `${where}: unexpected property \"${String(err.params.additionalProperty)}\"`;\n }\n case \"required\": {\n return `${where}: missing required property \"${String(err.params.missingProperty)}\"`;\n }\n case \"enum\": {\n const allowed = (err.params.allowedValues as unknown[]).join(\", \");\n return `${where}: ${err.message ?? \"is invalid\"} (${allowed})`;\n }\n default: {\n return `${where}: ${err.message ?? \"is invalid\"}`;\n }\n }\n}\n\n// --- Typed views + convenience validators the applier (issue #6) builds on. ---\n\nexport interface SaasaloyConfig {\n aliases: Record<string, string>;\n /**\n * The base app `saasaloy init` scaffolded (`web`). It is not a module: it has no\n * descriptor and is never in `installed`. Its files record under the reserved manifest\n * module name `base`, and the lock's `base` object carries its provenance (#120). Until #98\n * the template listed it in `installed[]` and every engine carried an excuse for the\n * one name in that list it could say nothing about. Optional so a project scaffolded\n * before the field existed still validates; `loadConfig` migrates it.\n */\n base?: string;\n installed: string[];\n}\n\nexport interface RegistryFile {\n path: string;\n target: string;\n /**\n * Install this file only when the named module is in the resolved install set (#99).\n * Two entries may share one `target` under disjoint conditions, which is how a module\n * ships a sqlite and a pg variant of one dialect-bound file. `RegistryScaffold.files`\n * is this same type, so the keyword covers a scaffold's workspace files too.\n */\n onlyWith?: string;\n}\n\n// A new workspace a capability births. Its `files[].target`s are workspace-root-relative\n// (no `@alias` — the alias root doesn't exist until this scaffold lands), and it declares\n// the aliases the applier registers into saasaloy.json (ADR 0013).\nexport interface RegistryScaffold {\n /** New workspace directory, project-relative POSIX (e.g. `apps/api`). */\n workspace: string;\n /** Aliases this scaffold registers into saasaloy.json (e.g. `{ \"@api\": \"apps/api/src\" }`). */\n aliases?: Record<string, string>;\n /** Files copied into the workspace; each `target` is relative to `workspace`. */\n files: RegistryFile[];\n}\n\nexport interface RegistryAgent {\n skills?: string[];\n}\n\n// A structural config patch as authored in registry-item.json: an engine `Patch`\n// (kind + payload) plus the project-relative `file` it targets. Serialized as a flat\n// array so one module can patch several files, each op self-describing (ADR 0019).\nexport type RegistryPatch = { file: string } & Patch;\n\nexport interface RegistryItem {\n name: string;\n type: \"saasaloy:capability\" | \"saasaloy:feature\";\n /**\n * Tool versions this descriptor needs. `saasaloy` is a semver range the running CLI has\n * to satisfy; a CLI outside it refuses the whole run before planning a single write,\n * naming this module even when it arrived as a transitive `dependsOn` (#50).\n */\n requires?: { saasaloy?: string };\n dependsOn?: string[];\n /** Modules this one refuses to sit beside; `add` refuses rather than installing both. Recorded in the lockfile so the check works in either install order. */\n conflictsWith?: string[];\n /** Modules exactly one of which has to be present — a capability naming its mutually exclusive drivers. `add` refuses when none is installed or in the resolved graph, and offers the list as a prompt on a terminal. */\n requiresOneOf?: string[];\n /** npm deps merged into the consumer's `dependencies`. Exact-pinned `name@version` (bare/range rejected by the schema). */\n dependencies?: string[];\n /** npm deps merged into the consumer's `devDependencies` (`@types/*`, build tooling). Same exact-pin form; a name in both buckets lands in `dependencies` only. */\n devDependencies?: string[];\n files?: RegistryFile[];\n envVars?: Record<string, string>;\n /** Local-dev values for a subset of `envVars`, pre-filled into `.dev.vars.example`. Never a secret: a loopback URL or a fixed port, the same on every machine. */\n devVars?: Record<string, string>;\n /** Warnings persisted at add time and printed before this module is removed. */\n removeWarnings?: string[];\n patches?: RegistryPatch[];\n scaffolds?: RegistryScaffold[];\n agent?: RegistryAgent;\n}\n\nexport function validateSaasaloyConfig(\n data: unknown\n): Promise<ValidationResult> {\n return validate(\"saasaloy\", data);\n}\n\nexport function validateManifest(data: unknown): Promise<ValidationResult> {\n return validate(\"manifest\", data);\n}\n\nexport function validateRegistryItem(data: unknown): Promise<ValidationResult> {\n return validate(\"registry-item\", data);\n}\n\nexport function validateLock(data: unknown): Promise<ValidationResult> {\n return validate(\"saasaloy-lock\", data);\n}\n","import { builders, generateCode, parseModule } from \"magicast\";\nimport {\n describeBinding,\n foreignBinding,\n isReferenced,\n keepTerminator,\n} from \"./ts-ast.js\";\nimport type { ModuleImports } from \"./ts-ast.js\";\n\n// `magicast` codemod for a chained-call router entry file — Hono's RPC shape, where\n// `const app = new Hono().route(\"/x\", x)` is the exported chain a client derives\n// `typeof app` from. The sibling of ts-module.ts's plugin-array insert, split out\n// because a call chain is a different structure from an array literal.\n//\n// Unlike the other three kinds this one ships its inverse (`removeChainedRoute`), so\n// `saasaloy remove` takes the link and its import back out instead of only warning.\n// Both directions are pure string→string, so the applier and the remover can preview\n// them. Nothing writes an anchor or sentinel comment: magicast is backed by recast, so\n// untouched lines keep their exact source and the chain locates itself (ADR 0006).\n\nexport interface ChainedRoute {\n /** Exported binding whose call chain to extend, e.g. \"default\" or \"app\". */\n exportName: string;\n /** Route path — `.route()`'s first argument, e.g. \"/waitlist\". Also the match key. */\n path: string;\n /** Identifier passed as `.route()`'s second argument, e.g. \"waitlist\". */\n call: string;\n /** Named import to ensure is present for `call`. */\n import: { name: string; from: string };\n}\n\n/**\n * Append `.route(<path>, <call>)` to the chain the module exports as `exportName`,\n * adding the named import if missing. Idempotent and formatting-safe:\n *\n * - the chain already routes `path` → return `source` **unchanged** (never clobber);\n * - the export is a bare identifier (`export default app`) → follow it to its\n * declaration and extend that, so `export type AppType = typeof app` keeps working;\n * - the chain has no `.route()` link yet → this becomes the first one;\n * - import already present → not duplicated;\n * - the local name is already bound to a *different* import → return `source`\n * unchanged, because wiring the route would bind the wrong handler\n * (`chainedRouteInsertRefusal` reports why);\n * - export missing or an unrecognised shape → return `source` unchanged.\n */\nexport function insertChainedRoute(\n source: string,\n patch: ChainedRoute\n): string {\n const mod = parseModule(source);\n const slot = findChainSlot(mod.$ast as unknown as Program, patch.exportName);\n if (!slot) {\n return source;\n } // not the shape we expected — leave it be\n\n for (const link of chainLinks(slot.node)) {\n if (isRouteLink(link, patch.path)) {\n return source;\n } // already routed\n }\n\n const imports = mod.imports;\n if (insertBindingConflict(imports, patch)) {\n return source;\n } // wrong binding — never wire it\n\n // Ensure the named import exists (magicast keys imports by local name).\n if (!(patch.import.name in mod.imports)) {\n mod.imports.$add({\n from: patch.import.from,\n imported: patch.import.name,\n local: patch.import.name,\n });\n }\n\n slot.replace(buildRouteLink(slot.node, patch.path, patch.call));\n return keepTerminator(source, generateCode(mod).code);\n}\n\n/**\n * Why `insertChainedRoute` declined to change `source`, or `undefined` when it had no\n * objection (it applied the link, or the link was already there).\n *\n * The codemods are pure `string → string`, so a refusal is indistinguishable from an\n * idempotent no-op at the call site. `applyPatch` asks this only when nothing changed,\n * which is what lets `add` warn by name instead of silently skipping a route.\n */\nexport function chainedRouteInsertRefusal(\n source: string,\n patch: ChainedRoute\n): string | undefined {\n const mod = parseModule(source);\n const slot = findChainSlot(mod.$ast as unknown as Program, patch.exportName);\n if (!slot) {\n return undefined;\n } // unrecognised shape, reported by the caller as \"missing\"\n for (const link of chainLinks(slot.node)) {\n if (isRouteLink(link, patch.path)) {\n return undefined;\n } // already routed, not a refusal\n }\n return insertBindingConflict(mod.imports, patch);\n}\n\n/**\n * The inverse: drop the `.route()` link matching `path` from the exported chain, and\n * drop the named import it needed.\n *\n * Located by `path`, since that is the key the forward direction is idempotent on, then\n * **verified against the recorded handler** before anything is deleted. A user who\n * repointed the route at their own handler owns that line, and drift is surfaced rather\n * than resolved here.\n *\n * - link already absent → return `source` **unchanged**, so a hand-reverted file is\n * never force-edited (the remover warns and skips instead);\n * - the link routes `path` to anything but `call` → return `source` **unchanged**\n * (`chainedRouteRemoveRefusal` reports why);\n * - the local name now binds a different import → return `source` **unchanged**, since\n * repointing the import changes what the link means without touching the link;\n * - the link is the only one → the bare receiver is left behind (`const app = new\n * Hono();`), which still parses and typechecks;\n * - the import statement holds other specifiers → only this one is removed;\n * - the identifier is still referenced elsewhere in the file → the import stays, so a\n * hand-written second use (`app.use(waitlist.middleware)`) is not left unbound.\n */\nexport function removeChainedRoute(\n source: string,\n patch: ChainedRoute\n): string {\n const mod = parseModule(source);\n const slot = findChainSlot(mod.$ast as unknown as Program, patch.exportName);\n if (!slot) {\n return source;\n }\n\n // Walk outermost-inward, remembering the link that holds the one we're looking for —\n // splicing a link out means re-pointing its holder at the link's own receiver.\n let holder: CallExpression | undefined;\n let found: CallExpression | undefined;\n for (const link of chainLinks(slot.node)) {\n if (isRouteLink(link, patch.path)) {\n found = link;\n break;\n }\n holder = link;\n }\n if (!found) {\n return source;\n } // already gone — never force-edit\n if (handlerDrift(found, patch)) {\n return source;\n } // user's handler — not ours to delete\n // The link can read exactly as written while meaning something else: repointing the\n // import changes what `waitlist` resolves to without touching the `.route()` line. That\n // makes both the link and the import the user's, so neither is ours to delete.\n if (removeBindingConflict(mod.imports, patch)) {\n return source;\n }\n\n const receiver = (found.callee as MemberExpression).object;\n if (holder) {\n (holder.callee as MemberExpression).object = receiver;\n } else {\n slot.replace(receiver);\n }\n\n // Guarded twice: magicast's delete trap throws when the local name isn't imported, and\n // a binding the file still references elsewhere must keep its import or the file stops\n // compiling.\n const program = mod.$ast as unknown as Program;\n if (\n patch.import.name in mod.imports &&\n !isReferenced(program, patch.import.name)\n ) {\n delete mod.imports[patch.import.name];\n }\n return keepTerminator(source, generateCode(mod).code);\n}\n\n/**\n * Why `removeChainedRoute` declined to change `source`, or `undefined` when it had no\n * objection (it removed the link, or the link was already gone). The mirror of\n * `chainedRouteInsertRefusal`, and the same reason for existing: it tells the remover's\n * \"nothing left to revert\" apart from \"that route is yours now\".\n */\nexport function chainedRouteRemoveRefusal(\n source: string,\n patch: ChainedRoute\n): string | undefined {\n const mod = parseModule(source);\n const slot = findChainSlot(mod.$ast as unknown as Program, patch.exportName);\n if (!slot) {\n return undefined;\n }\n for (const link of chainLinks(slot.node)) {\n if (isRouteLink(link, patch.path)) {\n return (\n handlerDrift(link, patch) ?? removeBindingConflict(mod.imports, patch)\n );\n }\n }\n return undefined; // already gone, not a refusal\n}\n\n// --- Ownership -------------------------------------------------------------------\n// Both directions key on the route path, and a path is not proof of ownership. These two\n// answer \"is this link, or this binding, still the one the manifest recorded?\" — the check\n// that keeps a patch from clobbering a hand edit that happens to sit at the same path.\n// `foreignBinding` and `describeBinding` are in ts-ast.ts: ts-module.ts's inverse asks the\n// same question of the same import proxy, so only the message text differs.\n\nfunction insertBindingConflict(\n imports: ModuleImports,\n patch: ChainedRoute\n): string | undefined {\n const held = foreignBinding(imports, patch.import);\n if (!held) {\n return undefined;\n }\n return `${describeBinding(patch.import.name, held)}, so routing ${JSON.stringify(patch.path)} would bind the wrong handler`;\n}\n\nfunction removeBindingConflict(\n imports: ModuleImports,\n patch: ChainedRoute\n): string | undefined {\n const held = foreignBinding(imports, patch.import);\n if (!held) {\n return undefined;\n }\n return `${describeBinding(patch.import.name, held)} now, not from ${JSON.stringify(patch.import.from)}, so the link and its import are not the ones that were applied`;\n}\n\n/**\n * Whether the link found at the recorded path routes it somewhere other than the recorded\n * handler. Anything that isn't the plain `call` identifier (or dotted path) counts, an\n * inline arrow included: the second argument is no longer what the manifest recorded.\n */\nfunction handlerDrift(\n link: CallExpression,\n patch: ChainedRoute\n): string | undefined {\n const argument = link.arguments[1];\n const handler = argument === undefined ? undefined : dottedName(argument);\n if (handler === patch.call) {\n return undefined;\n }\n const now =\n handler === undefined ? \"an inline expression\" : JSON.stringify(handler);\n return `${JSON.stringify(patch.path)} now routes to ${now}, not to ${JSON.stringify(patch.call)}`;\n}\n\n/** `waitlist` → \"waitlist\", `api.waitlist` → \"api.waitlist\", anything else → undefined. */\nfunction dottedName(node: AstNode): string | undefined {\n if (node.type === \"Identifier\") {\n return (node as Identifier).name;\n }\n if (node.type !== \"MemberExpression\") {\n return undefined;\n }\n const member = node as MemberExpression;\n if (member.property.type !== \"Identifier\") {\n return undefined;\n }\n const object = dottedName(member.object);\n return object === undefined\n ? undefined\n : `${object}.${(member.property as Identifier).name}`;\n}\n\n// --- AST shapes -------------------------------------------------------------------\n// magicast types its AST as a broad union; these are the narrow shapes this codemod\n// reads, so the casts below stay local and named.\n\ninterface AstNode {\n type: string;\n}\ninterface Identifier extends AstNode {\n name: string;\n}\ninterface StringLike extends AstNode {\n value: unknown;\n}\ninterface MemberExpression extends AstNode {\n object: AstNode;\n property: AstNode;\n}\ninterface CallExpression extends AstNode {\n callee: AstNode;\n arguments: AstNode[];\n}\ninterface VariableDeclarator extends AstNode {\n id: AstNode;\n init: AstNode | null;\n}\ninterface VariableDeclaration extends AstNode {\n declarations: VariableDeclarator[];\n}\ninterface ExportDefaultDeclaration extends AstNode {\n declaration: AstNode;\n}\ninterface ExportNamedDeclaration extends AstNode {\n declaration: AstNode | null;\n}\ninterface Program extends AstNode {\n body: AstNode[];\n}\n\n/** A writable position in the AST holding the chain expression. */\ninterface ChainSlot {\n /** The expression currently in the slot. */\n node: AstNode;\n /** Put a different expression in its place. */\n replace(next: AstNode): void;\n}\n\n// --- Locating the chain -----------------------------------------------------------\n\nfunction findChainSlot(\n program: Program,\n exportName: string\n): ChainSlot | undefined {\n const exported = findExportSlot(program, exportName);\n if (!exported) {\n return undefined;\n }\n // `export default app` exports a reference, not the chain. Follow it to the\n // declaration so the edit lands on `const app = …` and the export line is untouched.\n if (exported.node.type === \"Identifier\") {\n const declared = findDeclaratorSlot(\n program,\n (exported.node as Identifier).name\n );\n if (declared) {\n return declared;\n }\n }\n return exported;\n}\n\nfunction findExportSlot(\n program: Program,\n exportName: string\n): ChainSlot | undefined {\n for (const statement of program.body) {\n if (\n exportName === \"default\" &&\n statement.type === \"ExportDefaultDeclaration\"\n ) {\n const node = statement as ExportDefaultDeclaration;\n return {\n node: node.declaration,\n replace: (next) => {\n node.declaration = next;\n },\n };\n }\n if (\n exportName !== \"default\" &&\n statement.type === \"ExportNamedDeclaration\"\n ) {\n const declaration = (statement as ExportNamedDeclaration).declaration;\n if (declaration?.type === \"VariableDeclaration\") {\n const slot = declaratorSlot(\n declaration as VariableDeclaration,\n exportName\n );\n if (slot) {\n return slot;\n }\n }\n }\n }\n return undefined;\n}\n\nfunction findDeclaratorSlot(\n program: Program,\n name: string\n): ChainSlot | undefined {\n for (const statement of program.body) {\n const declaration =\n statement.type === \"VariableDeclaration\"\n ? statement\n : statement.type === \"ExportNamedDeclaration\"\n ? (statement as ExportNamedDeclaration).declaration\n : undefined;\n if (declaration?.type !== \"VariableDeclaration\") {\n continue;\n }\n const slot = declaratorSlot(declaration as VariableDeclaration, name);\n if (slot) {\n return slot;\n }\n }\n return undefined;\n}\n\nfunction declaratorSlot(\n declaration: VariableDeclaration,\n name: string\n): ChainSlot | undefined {\n for (const declarator of declaration.declarations) {\n if (declarator.id.type !== \"Identifier\") {\n continue;\n }\n if ((declarator.id as Identifier).name !== name) {\n continue;\n }\n if (!declarator.init) {\n continue;\n }\n return {\n node: declarator.init,\n replace: (next) => {\n declarator.init = next;\n },\n };\n }\n return undefined;\n}\n\n// --- Reading and writing the chain ------------------------------------------------\n\n/**\n * Yield each `x.y(…)` link of a call chain, outermost first.\n *\n * @yields {CallExpression} the next link, walking inward from the outermost call.\n */\nfunction* chainLinks(node: AstNode): Generator<CallExpression> {\n let current = node;\n while (\n current.type === \"CallExpression\" &&\n (current as CallExpression).callee.type === \"MemberExpression\"\n ) {\n const call = current as CallExpression;\n yield call;\n current = (call.callee as MemberExpression).object;\n }\n}\n\nfunction isRouteLink(link: CallExpression, path: string): boolean {\n const property = (link.callee as MemberExpression).property;\n if (\n property.type !== \"Identifier\" ||\n (property as Identifier).name !== \"route\"\n ) {\n return false;\n }\n const first = link.arguments[0];\n return first !== undefined && stringValue(first) === path;\n}\n\n// The parser emits `StringLiteral`; magicast's own builders emit ast-types `Literal`.\n// A chain can hold both once a previous patch has run, so read either.\nfunction stringValue(node: AstNode): string | undefined {\n if (node.type !== \"StringLiteral\" && node.type !== \"Literal\") {\n return undefined;\n }\n const value = (node as StringLike).value;\n return typeof value === \"string\" ? value : undefined;\n}\n\n// magicast has no builder for a chained method call, so parse the link as an\n// expression against a placeholder receiver and graft the real chain in as the\n// receiver. The grafted node keeps its original source, so recast reprints only the\n// appended link.\nfunction buildRouteLink(\n receiver: AstNode,\n path: string,\n call: string\n): AstNode {\n const parsed = builders.raw(\n `__chain__.route(${JSON.stringify(path)}, ${call})`\n ) as unknown as {\n $ast: AstNode;\n };\n const link = parsed.$ast as CallExpression;\n (link.callee as MemberExpression).object = receiver;\n return link;\n}\n","// The bits of AST and import handling both magicast codemods need. chained-route.ts and\n// ts-module.ts ask the same three questions in their inverse direction — \"does the local\n// name still bind what we imported?\", \"is the name still used anywhere else?\", \"did recast\n// eat the file's terminator?\" — so the answers live here once rather than twice.\n\n/** Structural view of magicast's import proxy: local name → what it binds. */\nexport type ModuleImports = Record<\n string,\n { imported?: unknown; from?: unknown } | undefined\n>;\n\n/** A named import as a descriptor declares it. */\nexport interface NamedImport {\n name: string;\n from: string;\n}\n\n/**\n * The binding currently held by the patch's local name, when it is not the recorded one.\n *\n * magicast keys imports by *local* name, so `want.name in mod.imports` proves only that\n * something holds the name — not that it is the binding this patch needs. Both directions\n * care: an insert would wire the wrong value, and a remove would delete an import line the\n * user rewrote. Compare the local name, the imported name, and the source. An unbound name\n * is not a conflict; it is the ordinary case.\n */\nexport function foreignBinding(\n imports: ModuleImports,\n want: NamedImport\n): { imported: string; from: string } | undefined {\n const held = imports[want.name];\n if (!held) {\n return undefined;\n }\n\n const imported = typeof held.imported === \"string\" ? held.imported : \"\";\n const from = typeof held.from === \"string\" ? held.from : \"\";\n if (imported === want.name && from === want.from) {\n return undefined;\n }\n return { imported, from };\n}\n\n/** \"…is imported as a default import from \"./legacy.js\"\" — the shared half of both reasons. */\nexport function describeBinding(\n name: string,\n held: { imported: string; from: string }\n): string {\n const bound =\n held.imported === \"*\"\n ? \"as a namespace import\"\n : held.imported === \"default\"\n ? \"as a default import\"\n : `as ${JSON.stringify(held.imported)}`;\n return `${JSON.stringify(name)} is imported ${bound} from ${JSON.stringify(held.from)}`;\n}\n\n// recast reprints the whole program, and its printer drops a trailing newline the source\n// had. Untouched bytes must stay untouched, so put the terminator back — an add→remove\n// round trip is then byte-identical to the file the user started with.\nexport function keepTerminator(source: string, code: string): string {\n if (source.endsWith(\"\\n\") && !code.endsWith(\"\\n\")) {\n return `${code}\\n`;\n }\n if (!source.endsWith(\"\\n\") && code.endsWith(\"\\n\")) {\n return code.slice(0, -1);\n }\n return code;\n}\n\n/** The one thing `isReferenced` needs of a program: its top-level statements. */\nexport interface ProgramLike {\n body: unknown[];\n}\n\n// Does any non-import part of the program still read `name`? Walks the plain-object AST\n// rather than pulling in a visitor, since the only question is whether an `Identifier`\n// with this name survives outside the import declarations and outside member/property\n// positions (`x.waitlist` is not a use of `waitlist`).\nexport function isReferenced(program: ProgramLike, name: string): boolean {\n let found = false;\n\n function walk(node: unknown, key?: string): void {\n if (found || node === null || typeof node !== \"object\") {\n return;\n }\n if (Array.isArray(node)) {\n for (const child of node) {\n walk(child);\n }\n return;\n }\n const record = node as Record<string, unknown> & { type?: string };\n if (typeof record.type !== \"string\") {\n return;\n }\n if (record.type === \"ImportDeclaration\") {\n return;\n } // the import itself is not a use\n if (record.type === \"Identifier\") {\n // Skip a name in a non-reference slot: `a.name`, `{ name: … }`, `function name()`.\n if (key !== \"property\" && key !== \"key\" && record.name === name) {\n found = true;\n }\n return;\n }\n for (const [childKey, value] of Object.entries(record)) {\n // recast hangs its own bookkeeping off these; walking them re-walks the whole file.\n if (\n childKey === \"loc\" ||\n childKey === \"comments\" ||\n childKey === \"original\"\n ) {\n continue;\n }\n // A computed member or key (`obj[name]`, `{ [name]: … }`) is a real reference, so\n // clear the slot name and let the identifier count.\n walk(value, record.computed === true ? undefined : childKey);\n }\n }\n\n walk(program.body);\n return found;\n}\n","import { parseModule } from \"magicast\";\n\nexport interface ConstArrayInsert {\n /** Module-scope const array to extend. */\n constName: string;\n /** Property whose value identifies an entry. */\n key: string;\n /** JSON object literal to append. */\n entry: Record<string, unknown>;\n}\n\n/** Append an object to a named module-scope const array, matched by one property. */\nexport function insertIntoConstArray(\n source: string,\n patch: ConstArrayInsert\n): string {\n const mod = parseModule(source);\n const array = findConstArray(mod.$ast as unknown as Program, patch.constName);\n const wanted = patch.entry[patch.key];\n if (!array || wanted === undefined || hasEntry(array, patch.key, wanted)) {\n return source;\n }\n\n return appendEntry(source, array, renderObject(patch.entry));\n}\n\n/** Explain why an unchanged insert could not find its declared target shape. */\nexport function constArrayInsertRefusal(\n source: string,\n patch: ConstArrayInsert\n): string | undefined {\n const mod = parseModule(source);\n const array = findConstArray(mod.$ast as unknown as Program, patch.constName);\n if (!array) {\n return `const-array could not find a module-scope const array named ${patch.constName}`;\n }\n if (patch.entry[patch.key] === undefined) {\n return `const-array entry has no identity property named ${patch.key}`;\n }\n return undefined;\n}\n\n/** Remove the object whose identity property matches the recorded entry. */\nexport function removeFromConstArray(\n source: string,\n patch: ConstArrayInsert\n): string {\n const mod = parseModule(source);\n const array = findConstArray(mod.$ast as unknown as Program, patch.constName);\n const wanted = patch.entry[patch.key];\n if (!array || wanted === undefined) {\n return source;\n }\n\n const index = array.elements.findIndex(\n (element) => entryValue(element, patch.key) === wanted\n );\n if (index === -1) {\n return source;\n }\n return removeEntry(source, array, index);\n}\n\nfunction appendEntry(\n source: string,\n array: ArrayExpression,\n entry: string\n): string {\n const close = array.end - 1;\n const closeLineStart = source.lastIndexOf(\"\\n\", close - 1) + 1;\n const closeIndent = source.slice(closeLineStart, close);\n const isMultiline = closeIndent.trim() === \"\" && closeLineStart > array.start;\n if (!isMultiline) {\n const separator = array.elements.length === 0 ? \"\" : \", \";\n return `${source.slice(0, close)}${separator}${entry}${source.slice(close)}`;\n }\n\n let before = source.slice(0, closeLineStart);\n const last = array.elements.at(-1);\n if (last && !source.slice(last.end, close).includes(\",\")) {\n before = `${source.slice(0, last.end)},${source.slice(last.end, closeLineStart)}`;\n }\n const itemIndent = `${closeIndent} `;\n return `${before}${itemIndent}${entry},\\n${source.slice(closeLineStart)}`;\n}\n\nfunction removeEntry(\n source: string,\n array: ArrayExpression,\n index: number\n): string {\n const element = array.elements[index];\n if (!element) {\n return source;\n }\n const lineStart = source.lastIndexOf(\"\\n\", element.start - 1) + 1;\n const lineEnd = source.indexOf(\"\\n\", element.end);\n const ownLine = source.slice(lineStart, element.start).trim() === \"\";\n if (ownLine && lineEnd !== -1) {\n return `${source.slice(0, lineStart)}${source.slice(lineEnd + 1)}`;\n }\n\n const next = array.elements[index + 1];\n if (next) {\n return `${source.slice(0, element.start)}${source.slice(next.start)}`;\n }\n const previous = array.elements[index - 1];\n const start = previous\n ? source.lastIndexOf(\",\", element.start - 1)\n : element.start;\n return `${source.slice(0, start)}${source.slice(element.end)}`;\n}\n\nfunction hasEntry(\n array: ArrayExpression,\n key: string,\n wanted: unknown\n): boolean {\n return array.elements.some((element) => entryValue(element, key) === wanted);\n}\n\nfunction entryValue(element: AstNode | null, key: string): unknown {\n if (element?.type !== \"ObjectExpression\") {\n return undefined;\n }\n for (const property of (element as ObjectExpression).properties) {\n if (property.type !== \"ObjectProperty\" || property.computed) {\n continue;\n }\n if (propertyName(property.key) !== key) {\n continue;\n }\n return literalValue(property.value);\n }\n return undefined;\n}\n\nfunction propertyName(node: AstNode): string | undefined {\n if (node.type === \"Identifier\") {\n return (node as Identifier).name;\n }\n return literalValue(node) as string | undefined;\n}\n\nfunction literalValue(node: AstNode): unknown {\n if (\n node.type !== \"StringLiteral\" &&\n node.type !== \"NumericLiteral\" &&\n node.type !== \"BooleanLiteral\" &&\n node.type !== \"NullLiteral\" &&\n node.type !== \"Literal\"\n ) {\n return undefined;\n }\n return node.type === \"NullLiteral\" ? null : (node as Literal).value;\n}\n\nfunction findConstArray(\n program: Program,\n constName: string\n): ArrayExpression | undefined {\n for (const statement of program.body) {\n const declaration =\n statement.type === \"ExportNamedDeclaration\"\n ? (statement as ExportNamedDeclaration).declaration\n : statement;\n if (\n declaration?.type !== \"VariableDeclaration\" ||\n (declaration as VariableDeclaration).kind !== \"const\"\n ) {\n continue;\n }\n for (const declarator of (declaration as VariableDeclaration)\n .declarations) {\n if (\n declarator.id.type !== \"Identifier\" ||\n (declarator.id as Identifier).name !== constName ||\n !declarator.init\n ) {\n continue;\n }\n const value = unwrapTypeExpression(declarator.init);\n return value.type === \"ArrayExpression\"\n ? (value as ArrayExpression)\n : undefined;\n }\n }\n return undefined;\n}\n\nfunction unwrapTypeExpression(node: AstNode): AstNode {\n let value = node;\n while (\n value.type === \"TSAsExpression\" ||\n value.type === \"TSSatisfiesExpression\" ||\n value.type === \"TypeCastExpression\"\n ) {\n value = (value as TypeExpression).expression;\n }\n return value;\n}\n\nfunction renderObject(entry: Record<string, unknown>): string {\n return `{ ${Object.entries(entry)\n .map(([key, value]) => `${renderKey(key)}: ${renderValue(value)}`)\n .join(\", \")} }`;\n}\n\nfunction renderKey(key: string): string {\n return /^[A-Za-z_$][\\w$]*$/.test(key) ? key : JSON.stringify(key);\n}\n\nfunction renderValue(value: unknown): string {\n if (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"boolean\"\n ) {\n return JSON.stringify(value);\n }\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return String(value);\n }\n if (Array.isArray(value)) {\n return `[${value.map(renderValue).join(\", \")}]`;\n }\n if (typeof value === \"object\") {\n return renderObject(value as Record<string, unknown>);\n }\n throw new TypeError(\"const-array entries must contain JSON values\");\n}\n\ninterface AstNode {\n type: string;\n start: number;\n end: number;\n}\ninterface Identifier extends AstNode {\n name: string;\n}\ninterface Literal extends AstNode {\n value: unknown;\n}\ninterface ObjectProperty extends AstNode {\n computed: boolean;\n key: AstNode;\n value: AstNode;\n}\ninterface ObjectExpression extends AstNode {\n properties: ObjectProperty[];\n}\ninterface ArrayExpression extends AstNode {\n elements: (AstNode | null)[];\n}\ninterface VariableDeclarator extends AstNode {\n id: AstNode;\n init: AstNode | null;\n}\ninterface VariableDeclaration extends AstNode {\n declarations: VariableDeclarator[];\n kind: string;\n}\ninterface ExportNamedDeclaration extends AstNode {\n declaration: AstNode | null;\n}\ninterface TypeExpression extends AstNode {\n expression: AstNode;\n}\ninterface Program extends AstNode {\n body: AstNode[];\n}\n","import { createTwoFilesPatch } from \"diff\";\n\n// Every patch is `--dry-run`/`--diff`-able (build spec §3.4): the engine is pure and\n// always hands back a unified diff of what it *would* write, so the applier can show\n// it and decide whether to commit the change to disk.\n\n/**\n * Render a unified diff between `before` and `after` for `filename`.\n * Returns `\"\"` when the two are identical — the signal an applier uses to\n * report \"nothing to do\" and never touch the file (idempotent re-run).\n */\nexport function toDiff(\n before: string,\n after: string,\n filename: string\n): string {\n if (before === after) {\n return \"\";\n }\n // Same path on both sides: this is an in-place edit, not a rename.\n const patch = createTwoFilesPatch(filename, filename, before, after, \"\", \"\");\n // Drop the leading `Index:`-style banner createPatch would add; createTwoFilesPatch\n // starts straight at the `---`/`+++` header, which is what we want to surface.\n return patch;\n}\n","import {\n applyEdits,\n findNodeAtLocation,\n getNodeValue,\n modify,\n parseTree,\n} from \"jsonc-parser\";\nimport type { FormattingOptions, Node } from \"jsonc-parser\";\n\n// `jsonc-parser` edits for `wrangler.jsonc` binding/route changes (build spec §3.4).\n// It rewrites only the touched region and leaves comments + surrounding formatting\n// intact — the reason we don't just `JSON.parse` → stringify, which would strip the\n// comments Cloudflare configs rely on.\n\nexport interface WranglerBinding {\n /**\n * Array to upsert into, e.g. \"d1_databases\", \"kv_namespaces\", \"routes\".\n *\n * A value with a dot addresses a **nested** array — \"queues.producers\",\n * \"queues.consumers\", \"triggers.crons\" (ADR 0033). Missing parent objects are\n * created on insert and unwound on removal, so the round trip is byte-identical.\n * A value with no dot keeps the top-level behaviour exactly.\n */\n bindingType: string;\n /**\n * The value to insert: an object (a binding or a route, matched by `matchOn`) or a\n * bare string (a flag, e.g. `compatibility_flags: [\"nodejs_compat\"]`, matched by\n * plain equality).\n */\n entry: Record<string, unknown> | string;\n /**\n * Property that identifies an object `entry` for idempotency. Defaults to \"binding\"\n * (bindings); pass \"pattern\" for routes, etc. Ignored for a string `entry`.\n */\n matchOn?: string;\n}\n\n/**\n * Insert `entry` into the top-level `bindingType` array of a `wrangler.jsonc`\n * document, idempotently and formatting-safe:\n *\n * - array missing → create it holding `entry`;\n * - array present, no entry matches (`matchOn` for an object, equality for a string)\n * → append `entry`;\n * - a matching entry already exists → return `source` **unchanged** (never clobber a\n * value the user may have edited).\n */\nexport function upsertWranglerBinding(\n source: string,\n patch: WranglerBinding\n): string {\n const matchOn = patch.matchOn ?? \"binding\";\n const root = parseTree(source);\n if (!root) {\n return source;\n } // unparseable — leave it to the caller/validator to surface\n\n const bindingPath = splitBindingType(patch.bindingType);\n const arrayNode = findNodeAtLocation(root, bindingPath);\n const formattingOptions = inferFormatting(source);\n\n if (arrayNode?.type === \"array\") {\n const existing = (arrayNode.children ?? []).map(\n (child) => getNodeValue(child) as unknown\n );\n const { entry } = patch;\n // A local `const` (not the `patch.entry` property access) so the `typeof` guard's\n // narrowing survives into the closure below — TS doesn't narrow property accesses\n // across a nested function boundary the way it does a plain variable.\n const alreadyPresent =\n typeof entry === \"string\"\n ? existing.includes(entry)\n : existing.some(\n (value) => isRecord(value) && value[matchOn] === entry[matchOn]\n );\n if (alreadyPresent) {\n return source;\n }\n\n const edits = modify(\n source,\n [...bindingPath, existing.length],\n patch.entry,\n {\n formattingOptions,\n isArrayInsertion: true,\n }\n );\n return applyEdits(source, edits);\n }\n\n // No array (or a non-array value) at that key — create the array fresh. `modify`\n // creates any missing parent object along a nested path on its way there.\n const edits = modify(source, bindingPath, [patch.entry], {\n formattingOptions,\n });\n return applyEdits(source, edits);\n}\n\n/**\n * The inverse: take `entry` back out of the top-level `bindingType` array, and take the\n * array with it when that leaves it empty.\n *\n * Located by the same key the forward direction is idempotent on (`matchOn` for an\n * object, equality for a string), then **verified against the recorded entry** before\n * anything is deleted. A user who edited the value we wrote owns it, so drift is\n * surfaced rather than resolved here.\n *\n * - the entry is already absent (or the array is) → return `source` **unchanged**, so a\n * hand-reverted file is never force-edited (the remover warns and skips instead);\n * - the entry under the match key holds anything but the recorded value → return\n * `source` **unchanged** (`wranglerBindingRemoveRefusal` reports why);\n * - the array still holds siblings → only this entry goes, comments and formatting kept;\n * - the array is left empty → the whole `bindingType` property goes, so a module that\n * *created* the array restores the file to its pre-patch bytes. An array that was\n * already there but empty before the patch is the one case that does not round-trip:\n * the manifest records no pre-patch state to tell the two apart, and dropping an empty\n * array is the harmless side of that guess.\n */\nexport function removeWranglerBinding(\n source: string,\n patch: WranglerBinding\n): string {\n const root = parseTree(source);\n if (!root) {\n return source;\n } // unparseable — leave it to the caller/validator to surface\n\n const bindingPath = splitBindingType(patch.bindingType);\n const arrayNode = findNodeAtLocation(root, bindingPath);\n if (arrayNode?.type !== \"array\") {\n return source;\n } // no array — nothing of ours to take out\n\n const existing = (arrayNode.children ?? []).map(\n (child) => getNodeValue(child) as unknown\n );\n const index = findEntryIndex(existing, patch);\n if (index === -1) {\n return source;\n } // already gone — never force-edit\n if (matchWranglerBinding(source, patch)) {\n return source;\n } // the user's entry now — not ours to delete\n\n // Last one out takes the array with it; the forward direction created it. On a\n // nested path it takes every parent the forward direction created too, so\n // `queues` and `triggers` never survive as empty objects.\n const path: (number | string)[] =\n existing.length === 1\n ? emptiedAncestorPath(root, bindingPath)\n : [...bindingPath, index];\n // Same formatting the forward direction infers, for the same reason: jsonc-parser's\n // raw deletion leaves the removed element's separator and indent behind (`} ]`), which\n // the generated project's own `prettier --check` would then fail.\n const edits = modify(source, path, undefined, {\n formattingOptions: inferFormatting(source),\n });\n return applyEdits(source, edits);\n}\n\n/**\n * Why `removeWranglerBinding` declined to change `source`, or `undefined` when it had no\n * objection (it removed the entry, or the entry was already gone).\n *\n * The codemods are pure `string → string`, so a refusal is indistinguishable from an\n * idempotent no-op at the call site. `reversePatch` asks this only when nothing changed,\n * which is what lets `remove` warn by name instead of silently leaving a binding behind.\n *\n * `matchWranglerBinding` already answers exactly this question in the forward direction —\n * \"something holds the match key at a value we did not write\" — so the two directions\n * agree on drift by construction. A string `entry` matches by equality and so can never\n * drift.\n */\nexport function wranglerBindingRemoveRefusal(\n source: string,\n patch: WranglerBinding\n): string | undefined {\n const match = matchWranglerBinding(source, patch);\n if (!match) {\n return undefined;\n }\n return `${match.key} holds ${JSON.stringify(match.current)} now, not the entry that was applied, so it is not ours to delete`;\n}\n\n/**\n * A `bindingType` as a `jsonc-parser` location path. \"d1_databases\" stays one\n * segment; \"queues.producers\" becomes two, which is the whole of the dotted-path\n * extension ADR 0033 asks for.\n */\nfunction splitBindingType(bindingType: string): string[] {\n return bindingType.split(\".\");\n}\n\n/**\n * The shallowest prefix of `bindingPath` that deleting the array would leave empty.\n *\n * Removing the last entry of `queues.producers` should take `producers` **and** the\n * `queues` object the forward direction created, so the file returns to its\n * pre-patch bytes. A parent holding a sibling (`queues.consumers` is still there)\n * stops the walk, and so does a top-level path, which has no parent to unwind.\n */\nfunction emptiedAncestorPath(root: Node, bindingPath: string[]): string[] {\n let cut = bindingPath.length;\n for (let depth = bindingPath.length - 1; depth >= 1; depth -= 1) {\n const parent = findNodeAtLocation(root, bindingPath.slice(0, depth));\n if (parent?.type !== \"object\" || (parent.children?.length ?? 0) !== 1) {\n break;\n }\n cut = depth;\n }\n return bindingPath.slice(0, cut);\n}\n\n/** Where the entry the forward direction would call \"already present\" sits, or -1. */\nfunction findEntryIndex(existing: unknown[], patch: WranglerBinding): number {\n const { entry } = patch;\n if (typeof entry === \"string\") {\n return existing.indexOf(entry);\n }\n const matchOn = patch.matchOn ?? \"binding\";\n return existing.findIndex(\n (value) => isRecord(value) && value[matchOn] === entry[matchOn]\n );\n}\n\n/**\n * What an already-present entry looks like when it *isn't* the one the descriptor\n * wants — the signal `saasaloy update` needs to tell \"already applied\" apart from\n * \"the user edited the value we would have written\" (issue #48, decision 1).\n */\nexport interface BindingMatch {\n /** Which entry matched, as `<bindingType>[<matchOn>=<value>]`. */\n key: string;\n /** The entry as the document holds it today. */\n current: unknown;\n /** The entry the descriptor declares. */\n wanted: unknown;\n}\n\n/**\n * Find the entry `upsertWranglerBinding` would consider \"already present\" and report\n * it when it differs from `patch.entry`. Returns `undefined` when nothing matches\n * (the patch will apply) or when the match is byte-equal (a true idempotent re-run).\n *\n * A string `entry` is matched by plain equality, so a match is always identical —\n * there is nothing to report.\n */\nexport function matchWranglerBinding(\n source: string,\n patch: WranglerBinding\n): BindingMatch | undefined {\n const entry = patch.entry;\n if (typeof entry === \"string\") {\n return undefined;\n }\n\n const root = parseTree(source);\n if (!root) {\n return undefined;\n }\n const arrayNode = findNodeAtLocation(\n root,\n splitBindingType(patch.bindingType)\n );\n if (arrayNode?.type !== \"array\") {\n return undefined;\n }\n\n const matchOn = patch.matchOn ?? \"binding\";\n const wantedId = entry[matchOn];\n for (const child of arrayNode.children ?? []) {\n const value = getNodeValue(child) as unknown;\n if (!isRecord(value) || value[matchOn] !== wantedId) {\n continue;\n }\n if (sameValue(value, entry)) {\n return undefined;\n }\n return {\n key: `${patch.bindingType}[${matchOn}=${String(wantedId)}]`,\n current: value,\n wanted: entry,\n };\n }\n return undefined;\n}\n\n// Order-insensitive structural equality. Both sides come from JSON, so a canonical\n// re-serialization with sorted keys is enough — and cheaper than a deep walk.\nfunction sameValue(a: unknown, b: unknown): boolean {\n return canonical(a) === canonical(b);\n}\n\nfunction canonical(value: unknown): string {\n if (Array.isArray(value)) {\n return `[${value.map(canonical).join(\",\")}]`;\n }\n if (isRecord(value)) {\n return `{${Object.keys(value)\n .toSorted()\n .map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)\n .join(\",\")}}`;\n }\n return JSON.stringify(value) ?? \"null\";\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n// Match the document's own indentation so inserted lines don't stand out. wrangler\n// configs are conventionally 2-space, but respect tabs if that's what the file uses.\n// Exported for pkg-json.ts, the other jsonc-parser codemod — same document-formatting\n// concern, different top-level shape (package.json dependency maps vs. binding arrays).\nexport function inferFormatting(source: string): FormattingOptions {\n const usesTabs = /^\\t/m.test(source);\n const spaceIndent = source.match(/^( +)\\S/m)?.[1];\n return {\n eol: source.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\",\n insertSpaces: !usesTabs,\n tabSize: spaceIndent ? spaceIndent.length : 2,\n };\n}\n","import {\n applyEdits,\n findNodeAtLocation,\n getNodeValue,\n modify,\n parseTree,\n} from \"jsonc-parser\";\nimport { inferFormatting } from \"./jsonc.js\";\n\n// `jsonc-parser` edits for package.json dependency merges — the counterpart to\n// upsertWranglerBinding, for the one case a module's config lives outside its own\n// scaffold: adding a dependency to a package.json ANOTHER module already wrote (e.g.\n// `database` making itself importable from `api`). Rewrites only the touched region,\n// preserving the rest of the document's formatting.\n\nexport interface PackageJsonDependency {\n /** Which dependency map to upsert into. */\n section:\n | \"dependencies\"\n | \"devDependencies\"\n | \"peerDependencies\"\n | \"optionalDependencies\";\n /** npm or workspace package name. */\n name: string;\n /** Version range to write (e.g. \"workspace:*\", \"^1.2.3\"). */\n range: string;\n}\n\n/**\n * Insert `name: range` into a package.json's `section` map, idempotently and\n * formatting-safe:\n *\n * - section missing → create it holding `{ [name]: range }`;\n * - section present, name absent → add the entry;\n * - name already present (any range) → return `source` **unchanged** (never clobber\n * a value the user may have edited, matching `upsertWranglerBinding`).\n */\nexport function upsertPackageJsonDependency(\n source: string,\n patch: PackageJsonDependency\n): string {\n const root = parseTree(source);\n if (!root) {\n return source;\n } // unparseable — leave it to the caller/validator to surface\n\n const formattingOptions = inferFormatting(source);\n const sectionNode = findNodeAtLocation(root, [patch.section]);\n\n if (sectionNode?.type === \"object\") {\n const existing = getNodeValue(sectionNode) as Record<string, unknown>;\n if (Object.hasOwn(existing, patch.name)) {\n return source;\n }\n\n const edits = modify(source, [patch.section, patch.name], patch.range, {\n formattingOptions,\n });\n return applyEdits(source, edits);\n }\n\n // No section (or a non-object value) at that key — create it fresh.\n const edits = modify(\n source,\n [patch.section],\n { [patch.name]: patch.range },\n {\n formattingOptions,\n }\n );\n return applyEdits(source, edits);\n}\n\n/**\n * Report the dependency this patch would have added when it is already declared at a\n * *different* range — the same \"already present, but not what we wanted\" signal\n * `matchWranglerBinding` gives for bindings (issue #48, decision 1). `undefined` when\n * the name is absent (the patch applies) or already pinned to `patch.range`.\n */\nexport function matchPackageJsonDependency(\n source: string,\n patch: PackageJsonDependency\n): { key: string; current: unknown; wanted: unknown } | undefined {\n const root = parseTree(source);\n if (!root) {\n return undefined;\n }\n const sectionNode = findNodeAtLocation(root, [patch.section]);\n if (sectionNode?.type !== \"object\") {\n return undefined;\n }\n\n const existing = getNodeValue(sectionNode) as Record<string, unknown>;\n if (!Object.hasOwn(existing, patch.name)) {\n return undefined;\n }\n const current = existing[patch.name];\n if (current === patch.range) {\n return undefined;\n }\n return {\n key: `${patch.section}[${patch.name}]`,\n current,\n wanted: patch.range,\n };\n}\n","import {\n applyEdits,\n findNodeAtLocation,\n getNodeValue,\n modify,\n parseTree,\n} from \"jsonc-parser\";\nimport { inferFormatting } from \"./jsonc.js\";\n\n// `jsonc-parser` edits for package.json `scripts` entries — the sibling of\n// upsertPackageJsonDependency, for the case where a module needs a command runnable in a\n// workspace ANOTHER module scaffolded (e.g. `database` adding `db:generate` to the app it\n// wires itself into). Rewrites only the touched region, preserving the rest of the\n// document's formatting.\n\nexport interface PackageJsonScript {\n /** Script key to upsert (e.g. \"db:generate\"). */\n name: string;\n /** Command the script runs (e.g. \"drizzle-kit generate\"). */\n value: string;\n}\n\n// Script keys npm and pnpm run on their own, without the user typing them: the four\n// install hooks and the publish hooks. A descriptor comes from a registry the user may\n// not control (third-party `owner/repo/name` coordinates are a documented form), so a\n// patch that lands one of these earns arbitrary code execution on the victim's next\n// `pnpm install` (#98). The denylist is exact plus the `prepublish*` family, and the\n// codemod refuses rather than upserting. `install:deps` and `preparse` are ordinary\n// names and stay allowed, which is why the check is equality, not a prefix scan.\n// npm generates a `pre`/`post` pair around every script it runs, so blocking `prepare`\n// without `preprepare` and `postprepare` leaves two keys that run on the same\n// `npm install`.\nconst LIFECYCLE_SCRIPTS: ReadonlySet<string> = new Set([\n \"install\",\n \"postinstall\",\n \"postprepare\",\n \"preinstall\",\n \"prepare\",\n \"preprepare\",\n]);\n\nfunction isLifecycleScript(name: string): boolean {\n return LIFECYCLE_SCRIPTS.has(name) || name.startsWith(\"prepublish\");\n}\n\n/**\n * Why this codemod declined to write, or `undefined` when it had no objection. Feeds\n * the patch engine's refusal table, so `add` names the rule and the key instead of\n * reporting a silent no-op.\n */\nexport function packageJsonScriptRefusal(\n _source: string,\n patch: PackageJsonScript\n): string | undefined {\n if (!isLifecycleScript(patch.name)) {\n return undefined;\n }\n return `refusing to write the ${JSON.stringify(patch.name)} script: package managers run install and publish lifecycle keys automatically, so a module descriptor may not define one.`;\n}\n\n/**\n * Insert `name: value` into a package.json's `scripts` map, idempotently and\n * formatting-safe:\n *\n * - `name` is an install/publish lifecycle key → return `source` **unchanged**\n * (see `packageJsonScriptRefusal` for the reason the caller reports);\n * - `scripts` missing (or holding a non-object) → create it holding `{ [name]: value }`;\n * - `scripts` present, name absent → add the entry;\n * - name already present (any command, including `\"\"`) → return `source` **unchanged**\n * (never clobber a command the user may have edited, matching\n * `upsertPackageJsonDependency`).\n *\n * Every write goes to the `[\"scripts\", name]` JSON path, so the patch kind can only ever\n * reach the scripts map — no sibling key is created, moved, or read.\n */\nexport function upsertPackageJsonScript(\n source: string,\n patch: PackageJsonScript\n): string {\n if (isLifecycleScript(patch.name)) {\n return source;\n }\n\n const root = parseTree(source);\n if (!root) {\n return source;\n } // unparseable — leave it to the caller/validator to surface\n\n const formattingOptions = inferFormatting(source);\n const scriptsNode = findNodeAtLocation(root, [\"scripts\"]);\n\n if (scriptsNode?.type === \"object\") {\n const existing = getNodeValue(scriptsNode) as Record<string, unknown>;\n if (Object.hasOwn(existing, patch.name)) {\n return source;\n }\n\n const edits = modify(source, [\"scripts\", patch.name], patch.value, {\n formattingOptions,\n });\n return applyEdits(source, edits);\n }\n\n // No `scripts` (or a non-object value) at that key — create it fresh.\n const edits = modify(\n source,\n [\"scripts\"],\n { [patch.name]: patch.value },\n { formattingOptions }\n );\n return applyEdits(source, edits);\n}\n","import { builders, generateCode, parseModule } from \"magicast\";\nimport {\n describeBinding,\n foreignBinding,\n isReferenced,\n keepTerminator,\n} from \"./ts-ast.js\";\nimport type { ModuleImports, ProgramLike } from \"./ts-ast.js\";\n\n// `magicast` codemods for TS/JS module edits (build spec §3.4): the canonical case is\n// pushing `stripe()` into Better Auth's `plugins` array. magicast is built on recast,\n// so untouched lines keep their exact original formatting — the edit is surgical.\n//\n// Both directions ship: `saasaloy remove` takes the call and its import back out instead\n// of only warning. They are pure string→string, so the applier and the remover can\n// preview them.\n\nexport interface PluginArrayInsert {\n /** Exported binding whose first call-argument object holds the array, e.g. \"auth\". */\n exportName: string;\n /** Property on that object which is the array, e.g. \"plugins\". */\n arrayProp: string;\n /** Factory to call and append, e.g. \"stripe\" → produces `stripe()`. */\n call: string;\n /** Named import to ensure is present for `call`. */\n import: { name: string; from: string };\n}\n\n/**\n * Append `call()` to `export const <exportName> = <fn>({ <arrayProp>: [...] })`,\n * adding the named import if missing. Idempotent and formatting-safe:\n *\n * - `call` already in the array → return `source` **unchanged** (never clobber);\n * - array property absent → create it as `[call()]`;\n * - import already present → not duplicated.\n */\nexport function insertIntoPluginArray(\n source: string,\n patch: PluginArrayInsert\n): string {\n const mod = parseModule(source);\n\n const exported = mod.exports[patch.exportName];\n const callArg = exported?.$args?.[0];\n if (!callArg) {\n return source;\n } // not the shape we expected — leave it be\n\n const array: unknown = callArg[patch.arrayProp];\n\n // Already present? Detect by the callee name of each function-call element.\n if (Array.isArray(array) && findCallIndex(array, patch.call) !== -1) {\n return source;\n }\n\n // Ensure the named import exists (magicast keys imports by local name).\n if (!(patch.import.name in mod.imports)) {\n mod.imports.$add({\n from: patch.import.from,\n imported: patch.import.name,\n local: patch.import.name,\n });\n }\n\n const newCall = builders.functionCall(patch.call);\n if (Array.isArray(array)) {\n array.push(newCall);\n } else {\n // No array yet — create the property holding a single call.\n callArg[patch.arrayProp] = [newCall];\n }\n\n return print(source, mod);\n}\n\n/**\n * The inverse: take `call()` back out of the array, take the array with it when that\n * leaves it empty, and drop the named import it needed.\n *\n * Located by the callee name, since that is the key the forward direction is idempotent\n * on, then **verified against what the descriptor recorded** before anything is deleted.\n * The forward direction writes a bare `call()` from a named import, so anything else under\n * that name is the user's edit, and drift is surfaced rather than resolved here.\n *\n * - the call is already absent (or the array is) → return `source` **unchanged**, so a\n * hand-reverted file is never force-edited (the remover warns and skips instead);\n * - the call now takes arguments → return `source` **unchanged**\n * (`pluginArrayRemoveRefusal` reports why);\n * - the local name now binds a different import → return `source` **unchanged**, since\n * repointing the import changes what the call means without touching the call;\n * - the array is left empty → it stays, as an empty array literal. The capability that\n * owns the file ships that property and depends on it being there for the next install;\n * - the import statement holds other specifiers → only this one is removed;\n * - the identifier is still referenced elsewhere in the file → the import stays, so a\n * hand-written second use (`stripe.plans`) is not left unbound.\n */\nexport function removeFromPluginArray(\n source: string,\n patch: PluginArrayInsert\n): string {\n const mod = parseModule(source);\n\n const exported = mod.exports[patch.exportName];\n const callArg = exported?.$args?.[0];\n if (!callArg) {\n return source;\n } // not the shape we expected — leave it be\n\n const array: unknown = callArg[patch.arrayProp];\n if (!Array.isArray(array)) {\n return source;\n } // no array — nothing of ours to take out\n\n const index = findCallIndex(array, patch.call);\n if (index === -1) {\n return source;\n } // already gone — never force-edit\n if (argumentDrift(array[index], patch)) {\n return source;\n } // the user's call now — not ours to delete\n // The call can read exactly as written while meaning something else: repointing the\n // import changes what `stripe` resolves to without touching the `stripe()` line. That\n // makes both the call and the import the user's, so neither is ours to delete.\n if (removeBindingConflict(mod.imports, patch)) {\n return source;\n }\n\n array.splice(index, 1);\n // The emptied array stays. Unlike a `wrangler.jsonc` binding array, which no module\n // ships and every patch therefore creates, the capability that owns this file writes\n // the array itself — `export const email = defineEmail({ providers: [] })`, with a\n // comment above it saying never to omit the property even while it is empty, because\n // the forward codemod has nothing to push into otherwise. Deleting it would return the\n // file to something the capability never shipped.\n\n // Guarded twice: magicast's delete trap throws when the local name isn't imported, and\n // a binding the file still references elsewhere must keep its import or the file stops\n // compiling.\n const program = mod.$ast as unknown as ProgramLike;\n if (\n patch.import.name in mod.imports &&\n !isReferenced(program, patch.import.name)\n ) {\n delete mod.imports[patch.import.name];\n }\n return print(source, mod);\n}\n\n/**\n * Why `removeFromPluginArray` declined to change `source`, or `undefined` when it had no\n * objection (it removed the call, or the call was already gone). The mirror of what\n * `chainedRouteRemoveRefusal` does for a route: it tells the remover's \"nothing left to\n * revert\" apart from \"that plugin is yours now\".\n */\nexport function pluginArrayRemoveRefusal(\n source: string,\n patch: PluginArrayInsert\n): string | undefined {\n const mod = parseModule(source);\n const callArg = mod.exports[patch.exportName]?.$args?.[0];\n if (!callArg) {\n return undefined;\n }\n const array: unknown = callArg[patch.arrayProp];\n if (!Array.isArray(array)) {\n return undefined;\n }\n const index = findCallIndex(array, patch.call);\n if (index === -1) {\n return undefined;\n } // already gone, not a refusal\n return (\n argumentDrift(array[index], patch) ??\n removeBindingConflict(mod.imports, patch)\n );\n}\n\n// The generated project ships Prettier and runs it as part of its own `pnpm lint`, so what\n// the codemod writes has to survive `prettier --check`. Two recast defaults do not: it\n// prints new named imports as `{foo}`, and it drops the file's final newline. Both are\n// ours to correct, not the user's to re-fix after every `saasaloy add` or `remove`.\nfunction print(source: string, mod: ReturnType<typeof parseModule>): string {\n return keepTerminator(\n source,\n generateCode(mod, { format: { objectCurlySpacing: true } }).code\n );\n}\n\n/** Where the call the forward direction would call \"already present\" sits, or -1. */\nfunction findCallIndex(array: unknown[], call: string): number {\n // NB: magicast's array proxy hands raw AST nodes to `.some`/`.findIndex` callbacks\n // (no `$type`/`$callee`), so this indexes each element to get the wrapped proxy.\n // for-of is not equivalent: it would hand back the same raw nodes the callback form\n // does, which is the whole reason this indexes.\n // oxlint-disable-next-line typescript/prefer-for-of\n for (let i = 0; i < array.length; i++) {\n const el: unknown = array[i];\n if (isFunctionCall(el) && el.$callee === call) {\n return i;\n }\n }\n return -1;\n}\n\n/**\n * Whether the call found under the recorded callee takes arguments. The forward direction\n * writes a bare `call()`, so anything with an argument is a line the user wrote over —\n * deleting it would throw away configuration the manifest never recorded.\n */\nfunction argumentDrift(\n element: unknown,\n patch: PluginArrayInsert\n): string | undefined {\n if (!isFunctionCall(element) || (element.$args?.length ?? 0) === 0) {\n return undefined;\n }\n return `${patch.call}() in ${JSON.stringify(patch.arrayProp)} takes arguments now, not the bare call that was applied`;\n}\n\nfunction removeBindingConflict(\n imports: ModuleImports,\n patch: PluginArrayInsert\n): string | undefined {\n const held = foreignBinding(imports, patch.import);\n if (!held) {\n return undefined;\n }\n return `${describeBinding(patch.import.name, held)} now, not from ${JSON.stringify(patch.import.from)}, so the plugin and its import are not the ones that were applied`;\n}\n\nfunction isFunctionCall(\n value: unknown\n): value is { $type: string; $callee: string; $args?: unknown[] } {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (value as { $type?: unknown }).$type === \"function-call\"\n );\n}\n","import {\n chainedRouteInsertRefusal,\n chainedRouteRemoveRefusal,\n insertChainedRoute,\n removeChainedRoute,\n} from \"./chained-route.js\";\nimport type { ChainedRoute } from \"./chained-route.js\";\nimport {\n constArrayInsertRefusal,\n insertIntoConstArray,\n removeFromConstArray,\n} from \"./const-array.js\";\nimport type { ConstArrayInsert } from \"./const-array.js\";\nimport { toDiff } from \"./diff.js\";\nimport {\n matchWranglerBinding,\n removeWranglerBinding,\n upsertWranglerBinding,\n wranglerBindingRemoveRefusal,\n} from \"./jsonc.js\";\nimport type { WranglerBinding } from \"./jsonc.js\";\nimport {\n matchPackageJsonDependency,\n upsertPackageJsonDependency,\n} from \"./pkg-json.js\";\nimport type { PackageJsonDependency } from \"./pkg-json.js\";\nimport {\n packageJsonScriptRefusal,\n upsertPackageJsonScript,\n} from \"./pkg-json-script.js\";\nimport type { PackageJsonScript } from \"./pkg-json-script.js\";\nimport {\n insertIntoPluginArray,\n pluginArrayRemoveRefusal,\n removeFromPluginArray,\n} from \"./ts-module.js\";\nimport type { PluginArrayInsert } from \"./ts-module.js\";\n\n// The config-patch engine (build spec §3.4): the ~10% of module application that isn't\n// a pure file-drop. Small, well-tested AST codemods the applier (#6) invokes for\n// structural edits — `jsonc-parser` for `wrangler.jsonc` bindings/routes and package.json\n// dependency merges, `magicast` for TS/JS module edits (Better Auth plugin arrays). Every\n// patch is pure and `--dry-run`/`--diff`-able: `applyPatch` never writes, it returns the\n// would-be content plus a unified diff, and re-running an already-applied patch is a no-op.\n\nexport {\n chainedRouteInsertRefusal,\n chainedRouteRemoveRefusal,\n insertChainedRoute,\n removeChainedRoute,\n type ChainedRoute,\n} from \"./chained-route.js\";\nexport {\n constArrayInsertRefusal,\n insertIntoConstArray,\n removeFromConstArray,\n type ConstArrayInsert,\n} from \"./const-array.js\";\nexport { toDiff } from \"./diff.js\";\nexport {\n type BindingMatch,\n matchWranglerBinding,\n removeWranglerBinding,\n upsertWranglerBinding,\n type WranglerBinding,\n wranglerBindingRemoveRefusal,\n} from \"./jsonc.js\";\nexport {\n matchPackageJsonDependency,\n upsertPackageJsonDependency,\n type PackageJsonDependency,\n} from \"./pkg-json.js\";\nexport {\n packageJsonScriptRefusal,\n upsertPackageJsonScript,\n type PackageJsonScript,\n} from \"./pkg-json-script.js\";\nexport {\n insertIntoPluginArray,\n type PluginArrayInsert,\n pluginArrayRemoveRefusal,\n removeFromPluginArray,\n} from \"./ts-module.js\";\n\n/** A single structural patch, tagged by the codemod that applies it. */\nexport type Patch =\n | ({ kind: \"const-array\" } & ConstArrayInsert)\n | ({ kind: \"wrangler-binding\" } & WranglerBinding)\n | ({ kind: \"package-json-dependency\" } & PackageJsonDependency)\n | ({ kind: \"package-json-script\" } & PackageJsonScript)\n | ({ kind: \"plugin-array\" } & PluginArrayInsert)\n | ({ kind: \"chained-route\" } & ChainedRoute);\n\nexport type PatchKind = Patch[\"kind\"];\n\n// The `Patch` union at runtime. `Record<PatchKind, true>` is exhaustive in both\n// directions: a kind added to the union without a key here fails typecheck, and a key\n// that names no kind is an excess property. `PATCH_KINDS` is what `schema.test.ts`\n// holds both JSON Schemas to, so the enums can't drift apart again (#98).\nconst PATCH_KIND_KEYS: Record<PatchKind, true> = {\n \"chained-route\": true,\n \"const-array\": true,\n \"package-json-dependency\": true,\n \"package-json-script\": true,\n \"plugin-array\": true,\n \"wrangler-binding\": true,\n};\n\n/** Every patch kind the engine applies, sorted. */\nexport const PATCH_KINDS: readonly PatchKind[] = (\n Object.keys(PATCH_KIND_KEYS) as PatchKind[]\n).toSorted();\n\n/**\n * An entry the codemod found under the identity it matches on, holding a value other\n * than the one the descriptor declares — i.e. the user edited what we would have\n * written. Distinct from a plain `changed: false`, which also covers a clean\n * already-applied re-run (issue #48, decision 1).\n */\nexport interface PatchMatch {\n /** Where the collision is, as `<container>[<identity>]` (e.g. `d1_databases[binding=DB]`). */\n key: string;\n /** The value on disk today. */\n current: unknown;\n /** The value the descriptor declares. */\n wanted: unknown;\n}\n\nexport interface PatchResult {\n /** The would-be file content after the patch (equal to the input on a no-op). */\n content: string;\n /** `false` when the patch was already applied — the applier skips the write. */\n changed: boolean;\n /** Unified diff of the change; `\"\"` when nothing changed. */\n diff: string;\n /**\n * Why an unchanged result was *refused* rather than already-applied — a binding the\n * patch would have wired wrongly, a route the user repointed. `undefined` for a plain\n * idempotent no-op, which is the common case and says nothing worth reporting.\n */\n reason?: string;\n /**\n * Set when the patch was a no-op *because* something already occupies its identity\n * at a different value. `saasaloy update` reports these into the merge plan; `add`\n * ignores it, so its behaviour is unchanged.\n */\n matched?: PatchMatch;\n}\n\n/**\n * Apply one structural `patch` to `source` and report the result. Pure: it computes\n * the new content and a diff but writes nothing, so the caller can preview\n * (`--dry-run`/`--diff`) or commit as it sees fit. Idempotent — a patch already\n * present yields `changed: false` and an empty diff.\n */\nexport function applyPatch(\n source: string,\n patch: Patch,\n filename: string\n): PatchResult {\n const content = applyCodemod(source, patch);\n const changed = content !== source;\n const matched = matchExisting(source, patch);\n return {\n content,\n changed,\n diff: toDiff(source, content, filename),\n reason: changed ? undefined : refusalReason(REFUSALS, source, patch),\n ...(matched ? { matched } : {}),\n };\n}\n\n// Only the two jsonc codemods have an identity to collide on. A `plugin-array` insert\n// is matched by the call expression itself, so a match is always an exact re-run.\nfunction matchExisting(source: string, patch: Patch): PatchMatch | undefined {\n switch (patch.kind) {\n case \"wrangler-binding\": {\n return matchWranglerBinding(source, patch);\n }\n case \"package-json-dependency\": {\n return matchPackageJsonDependency(source, patch);\n }\n default: {\n return undefined;\n }\n }\n}\n\nfunction applyCodemod(source: string, patch: Patch): string {\n switch (patch.kind) {\n case \"const-array\": {\n return insertIntoConstArray(source, patch);\n }\n case \"wrangler-binding\": {\n return upsertWranglerBinding(source, patch);\n }\n case \"package-json-dependency\": {\n return upsertPackageJsonDependency(source, patch);\n }\n case \"package-json-script\": {\n return upsertPackageJsonScript(source, patch);\n }\n case \"plugin-array\": {\n return insertIntoPluginArray(source, patch);\n }\n case \"chained-route\": {\n return insertChainedRoute(source, patch);\n }\n default: {\n const exhaustive: never = patch;\n throw new Error(`unknown patch kind: ${JSON.stringify(exhaustive)}`);\n }\n }\n}\n\n// The one table of kind → inverse codemod. The four kinds that edit a *config* file\n// reverse: `chained-route` (#83), then `wrangler-binding`, `plugin-array` and\n// `const-array` (#36). The two `package.json` kinds deliberately do not — uninstalling an\n// npm dependency isn't derivable offline and other code may already use it, and nothing\n// asks for a script back (plan-remove-command-2026-07-25.md, non-goals). `remove`\n// drops-and-warns for every kind absent from this table, and both `isReversibleKind` and\n// `reversePatch` read it, so adding an inverse stays one edit.\ntype Inverse<K extends PatchKind> = (\n source: string,\n patch: Extract<Patch, { kind: K }>\n) => string;\n\nconst INVERSES: { [K in PatchKind]?: Inverse<K> } = {\n \"chained-route\": removeChainedRoute,\n \"const-array\": removeFromConstArray,\n \"plugin-array\": removeFromPluginArray,\n \"wrangler-binding\": removeWranglerBinding,\n};\n\n/** Whether `reversePatch` can undo a patch of this kind. */\nexport function isReversibleKind(kind: string): boolean {\n return Object.hasOwn(INVERSES, kind);\n}\n\n/**\n * Undo one structural `patch`, the mirror of `applyPatch`. Returns `undefined` for a\n * kind with no inverse yet, so the caller can tell \"nothing to reverse here\" from \"the\n * reversal ran and changed nothing\". Pure and idempotent, like the forward direction:\n * a patch already reversed yields `changed: false` and an empty diff, which is what\n * keeps a hand-reverted file from being force-edited.\n */\nexport function reversePatch(\n source: string,\n patch: Patch,\n filename: string\n): PatchResult | undefined {\n const inverse = INVERSES[patch.kind] as\n ((source: string, patch: Patch) => string) | undefined;\n if (!inverse) {\n return undefined;\n }\n const content = inverse(source, patch);\n const changed = content !== source;\n return {\n content,\n changed,\n diff: toDiff(source, content, filename),\n reason: changed\n ? undefined\n : refusalReason(REVERSAL_REFUSALS, source, patch),\n };\n}\n\n// The tables of kind → \"why did nothing change?\", one per direction. A codemod is pure\n// `string → string`, so a refusal to touch the file looks exactly like an idempotent\n// no-op at the call site; these tell the two apart, so `add` and `remove` can warn by\n// name instead of skipping in silence. Asking costs a second parse, so both callers ask\n// only when nothing changed. The forward table is short because most kinds only ever\n// no-op on an edit that is already present; `package-json-script` joined it in #98,\n// because it refuses an install-lifecycle key outright and a refusal a user never sees is\n// a security hole that reads as a typo. The reversal table follows `INVERSES`, minus\n// `const-array`, whose removal only ever no-ops on an entry that is already gone.\ntype Refusal<K extends PatchKind> = (\n source: string,\n patch: Extract<Patch, { kind: K }>\n) => string | undefined;\n\nconst REFUSALS: { [K in PatchKind]?: Refusal<K> } = {\n \"chained-route\": chainedRouteInsertRefusal,\n \"const-array\": constArrayInsertRefusal,\n \"package-json-script\": packageJsonScriptRefusal,\n};\n\nconst REVERSAL_REFUSALS: { [K in PatchKind]?: Refusal<K> } = {\n \"chained-route\": chainedRouteRemoveRefusal,\n \"plugin-array\": pluginArrayRemoveRefusal,\n \"wrangler-binding\": wranglerBindingRemoveRefusal,\n};\n\nfunction refusalReason(\n table: { [K in PatchKind]?: Refusal<K> },\n source: string,\n patch: Patch\n): string | undefined {\n const ask = table[patch.kind] as\n ((source: string, patch: Patch) => string | undefined) | undefined;\n return ask?.(source, patch);\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { RefusalError } from \"./exit.js\";\nimport { pathExists } from \"./fs-utils.js\";\nimport type { SaasaloyConfig } from \"./schema.js\";\nimport { validateSaasaloyConfig } from \"./schema.js\";\n\n// `saasaloy.json` is the consumer manifest at a generated project's root: the alias\n// map that resolves module file targets, plus the `installed` list that drives\n// dependsOn resolution (build spec §3.2). This loads/saves it and turns an\n// alias-prefixed target (`@api/routes/x.ts`) into a project-relative path.\n\nexport const CONFIG_FILE = \"saasaloy.json\";\n\n// The raw parse keeps the optional `$schema` pointer so a round-trip save doesn't\n// strip an author's editor-validation hint.\nexport type LoadedConfig = SaasaloyConfig & { $schema?: string };\n\nexport async function loadConfig(root: string): Promise<LoadedConfig> {\n const file = join(root, CONFIG_FILE);\n if (!(await pathExists(file))) {\n throw new RefusalError(\n `No ${CONFIG_FILE} found in ${root}. Run \\`saasaloy init\\` first, or cd into a Saasaloy project.`\n );\n }\n const parsed = JSON.parse(await readFile(file, \"utf-8\")) as unknown;\n const result = await validateSaasaloyConfig(parsed);\n if (!result.valid) {\n throw new RefusalError(\n `${CONFIG_FILE} is invalid:\\n ${result.errors.join(\"\\n \")}`\n );\n }\n return migrateBase(parsed as LoadedConfig);\n}\n\n/**\n * The base app moved out of `installed[]` and into its own `base` field (#98). A project\n * scaffolded before that still lists `web` as if the tool had applied it, which is what\n * forced every engine to carry a \"except the template's own `web`\" branch. Lift it here,\n * once, on load: from this point on `installed[]` holds only modules `saasaloy add` put\n * there, and the next `saveConfig` persists the corrected shape.\n *\n * Only `web` is lifted. It is the one name the template ever wrote, and guessing at any\n * other would silently unmanage a real module.\n */\nconst LEGACY_BASE = \"web\";\n\nexport function migrateBase(config: LoadedConfig): LoadedConfig {\n if (config.base !== undefined || !config.installed.includes(LEGACY_BASE)) {\n return config;\n }\n return {\n ...config,\n base: LEGACY_BASE,\n installed: config.installed.filter((name) => name !== LEGACY_BASE),\n };\n}\n\nexport async function saveConfig(\n root: string,\n config: LoadedConfig\n): Promise<void> {\n const file = join(root, CONFIG_FILE);\n await writeFile(file, `${JSON.stringify(config, null, 2)}\\n`, \"utf-8\");\n}\n\n// Resolve `@api/routes/x.ts` against the alias map to a project-relative POSIX path\n// (e.g. `apps/api/src/routes/x.ts`). POSIX separators keep manifest keys stable\n// across OSes; callers split on \"/\" when touching the filesystem.\nexport function resolveTarget(\n aliases: Record<string, string>,\n target: string\n): string {\n const slash = target.indexOf(\"/\");\n if (slash === -1) {\n throw new RefusalError(\n `Malformed target \"${target}\" — expected \"@alias/rest\".`\n );\n }\n const alias = target.slice(0, slash);\n const rest = target.slice(slash + 1);\n const base = aliases[alias];\n if (base === undefined) {\n const known = Object.keys(aliases).join(\", \") || \"(none)\";\n throw new RefusalError(\n `Unknown alias \"${alias}\" in target \"${target}\". Known aliases: ${known}.`\n );\n }\n // `base` is already POSIX + no leading slash (enforced by the schema); join by hand\n // to avoid the platform separator that node:path would introduce on Windows.\n const trimmed = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n return `${trimmed}/${rest}`;\n}\n","// A semver range matcher, small enough to own (#50). The descriptor's `requires` field\n// names a range the running CLI has to satisfy, so the tool needs to answer \"does\n// version V fall inside range R?\" and nothing else — no version bumping, no sorting a\n// release feed, no coercion of loose input.\n//\n// That is why there is no `semver` dependency here. The npm package is ~10 files of\n// range arithmetic the CLI would ship to every consumer for one boolean, and the CLI's\n// runtime dependency list is deliberately short (`@clack/prompts`, `ajv`, `diff`,\n// `giget`, `jsonc-parser`, `magicast`, `picocolors`). The grammar below is the subset\n// the field can legally use, and every shape it accepts is pinned by a test.\n//\n// Supported: `*` / `x` / empty (any), partial versions (`1`, `1.2`, `1.x`), `^`, `~`,\n// `>`/`>=`/`<`/`<=`/`=`, whitespace as AND, `||` as OR, and the hyphen range `A - B`.\n//\n// Prereleases follow node-semver's default rule: `1.0.0-rc.1` does not satisfy `*` or\n// `>=0.9.0`. A prerelease is only admitted when some comparator in the same AND-set\n// names the same major.minor.patch *and* carries a prerelease of its own. Without that\n// rule every release candidate would silently pass a range written for stable versions.\n\nexport interface SemVer {\n major: number;\n minor: number;\n patch: number;\n /** Dot-separated prerelease identifiers, numeric ones parsed as numbers. Empty on a release. */\n prerelease: (string | number)[];\n}\n\n/** One `>=1.2.0`-shaped constraint. `=` is the exact-match form partial ranges expand into. */\nexport interface Comparator {\n op: \">\" | \">=\" | \"<\" | \"<=\" | \"=\";\n version: SemVer;\n}\n\n/**\n * A parsed range: an OR of AND-sets, mirroring `a || b` over `>=1 <2`. An empty AND-set\n * is `*` — it matches every release. An empty OR list is impossible; `parseRange`\n * returns `undefined` instead.\n */\nexport type Range = Comparator[][];\n\n// No leading zeroes in a numeric part: `01.2.3` is not a semver version, and accepting\n// it would make `01.2.3` and `1.2.3` two spellings of one release.\nconst VERSION_PATTERN =\n /^v?(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([0-9A-Za-z][0-9A-Za-z.-]*))?(?:\\+[0-9A-Za-z][0-9A-Za-z.-]*)?$/;\n\nconst NUMERIC = /^(?:0|[1-9]\\d*)$/;\n\nfunction parsePrerelease(raw: string | undefined): (string | number)[] {\n if (raw === undefined) {\n return [];\n }\n return raw\n .split(\".\")\n .map((part) => (NUMERIC.test(part) ? Number(part) : part));\n}\n\n/** A `SemVer` for a full `major.minor.patch` version, or `undefined` when it is not one. */\nexport function parseVersion(input: string): SemVer | undefined {\n const match = VERSION_PATTERN.exec(input.trim());\n if (!match) {\n return undefined;\n }\n return {\n major: Number(match[1]),\n minor: Number(match[2]),\n patch: Number(match[3]),\n prerelease: parsePrerelease(match[4]),\n };\n}\n\nfunction compareIdentifiers(a: string | number, b: string | number): number {\n const aNumeric = typeof a === \"number\";\n const bNumeric = typeof b === \"number\";\n // \"Numeric identifiers always have lower precedence than non-numeric identifiers.\"\n if (aNumeric && bNumeric) {\n return a < b ? -1 : a > b ? 1 : 0;\n }\n if (aNumeric) {\n return -1;\n }\n if (bNumeric) {\n return 1;\n }\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\n/** Negative when `a` precedes `b`, positive when it follows, zero when they are equal. */\nexport function compareVersions(a: SemVer, b: SemVer): number {\n for (const key of [\"major\", \"minor\", \"patch\"] as const) {\n if (a[key] !== b[key]) {\n return a[key] < b[key] ? -1 : 1;\n }\n }\n // A version with a prerelease precedes the release it leads to.\n if (a.prerelease.length === 0 && b.prerelease.length === 0) {\n return 0;\n }\n if (a.prerelease.length === 0) {\n return 1;\n }\n if (b.prerelease.length === 0) {\n return -1;\n }\n const shared = Math.min(a.prerelease.length, b.prerelease.length);\n for (let i = 0; i < shared; i++) {\n const result = compareIdentifiers(a.prerelease[i]!, b.prerelease[i]!);\n if (result !== 0) {\n return result;\n }\n }\n // A longer identifier list wins when every shared identifier is equal.\n return a.prerelease.length - b.prerelease.length;\n}\n\n/** One `1`, `1.2`, `1.2.3`, `1.x` token, with the parts the author left out marked absent. */\ninterface Partial {\n major?: number;\n minor?: number;\n patch?: number;\n prerelease: (string | number)[];\n}\n\nconst WILDCARD = new Set([\"\", \"*\", \"x\", \"X\"]);\nconst PARTIAL_PATTERN =\n /^v?(0|[1-9]\\d*|[xX*])(?:\\.(0|[1-9]\\d*|[xX*])(?:\\.(0|[1-9]\\d*|[xX*])(?:-([0-9A-Za-z][0-9A-Za-z.-]*))?(?:\\+[0-9A-Za-z][0-9A-Za-z.-]*)?)?)?$/;\n\nfunction parsePartial(raw: string): Partial | undefined {\n const input = raw.trim();\n if (WILDCARD.has(input)) {\n return { prerelease: [] };\n }\n const match = PARTIAL_PATTERN.exec(input);\n if (!match) {\n return undefined;\n }\n const part = (value: string | undefined): number | undefined =>\n value === undefined || WILDCARD.has(value) ? undefined : Number(value);\n const major = part(match[1]);\n const minor = part(match[2]);\n const patch = part(match[3]);\n // `1.x.3` names a patch under an unknown minor, which constrains nothing coherent.\n if (\n (major === undefined && (minor !== undefined || patch !== undefined)) ||\n (minor === undefined && patch !== undefined)\n ) {\n return undefined;\n }\n return {\n ...(major === undefined ? {} : { major }),\n ...(minor === undefined ? {} : { minor }),\n ...(patch === undefined ? {} : { patch }),\n prerelease: parsePrerelease(match[4]),\n };\n}\n\nfunction version(\n major: number,\n minor = 0,\n patch = 0,\n prerelease: (string | number)[] = []\n): SemVer {\n return { major, minor, patch, prerelease };\n}\n\n/** The lowest version a partial admits: `1.2` → `1.2.0`, `1` → `1.0.0`. */\nfunction floor(partial: Partial): SemVer {\n return version(\n partial.major ?? 0,\n partial.minor ?? 0,\n partial.patch ?? 0,\n partial.prerelease\n );\n}\n\n/** The first version *above* a partial: `1.2` → `1.3.0`, `1` → `2.0.0`. */\nfunction ceiling(partial: Partial): SemVer | undefined {\n if (partial.major === undefined) {\n return undefined;\n }\n if (partial.minor === undefined) {\n return version(partial.major + 1);\n }\n if (partial.patch === undefined) {\n return version(partial.major, partial.minor + 1);\n }\n return version(partial.major, partial.minor, partial.patch + 1);\n}\n\n/** `^1.2.3` → `<2.0.0`, `^0.2.3` → `<0.3.0`, `^0.0.3` → `<0.0.4`: the leftmost non-zero part moves. */\nfunction caretCeiling(partial: Partial): SemVer | undefined {\n const { major, minor, patch } = partial;\n if (major === undefined) {\n return undefined;\n }\n if (major !== 0 || minor === undefined) {\n return version(major + 1);\n }\n if (minor !== 0 || patch === undefined) {\n return version(0, minor + 1);\n }\n return version(0, 0, patch + 1);\n}\n\nconst OPERATOR_PATTERN = /^(>=|<=|>|<|=)?\\s*(.*)$/;\n\n/** The comparators one whitespace-separated token expands into, or `undefined` when it is not a token. */\nfunction parseToken(token: string): Comparator[] | undefined {\n const match = OPERATOR_PATTERN.exec(token);\n if (!match) {\n return undefined;\n }\n const raw = match[2] ?? \"\";\n const op = match[1];\n if (op !== undefined && WILDCARD.has(raw.trim())) {\n // `>=*` and friends: an operator with nothing to compare against.\n return raw.trim() === \"\" ? undefined : [];\n }\n if (raw.startsWith(\"^\") || raw.startsWith(\"~\")) {\n if (op !== undefined) {\n return undefined; // `>=^1.0.0` is not a range.\n }\n // A bare `^` or `~` parses as \"any\" in node-semver. Here it is a truncated range an\n // author meant to finish, so it is reported rather than silently widened.\n if (raw.slice(1).trim() === \"\") {\n return undefined;\n }\n const partial = parsePartial(raw.slice(1));\n if (!partial) {\n return undefined;\n }\n if (partial.major === undefined) {\n return [];\n }\n const upper = raw.startsWith(\"^\")\n ? caretCeiling(partial)\n : ceiling(partial);\n const lower: Comparator = { op: \">=\", version: floor(partial) };\n // `~1.2` and `~1.2.3` both stop at the next minor; `~1` stops at the next major.\n const tildeUpper =\n partial.minor === undefined\n ? version(partial.major + 1)\n : version(partial.major, partial.minor + 1);\n const bound = raw.startsWith(\"^\") ? upper : tildeUpper;\n return bound ? [lower, { op: \"<\", version: bound }] : [lower];\n }\n\n const partial = parsePartial(raw);\n if (!partial) {\n return undefined;\n }\n if (partial.major === undefined) {\n // `*`, `x`, and a bare `=` with a wildcard constrain nothing.\n return [];\n }\n const low = floor(partial);\n const high = ceiling(partial);\n switch (op) {\n case \">\": {\n // `>1.2` means \"after everything 1.2.x\", so it floors at the next minor.\n return [\n partial.patch === undefined && high\n ? { op: \">=\", version: high }\n : { op: \">\", version: low },\n ];\n }\n case \">=\": {\n return [{ op: \">=\", version: low }];\n }\n case \"<\": {\n return [{ op: \"<\", version: low }];\n }\n case \"<=\": {\n return [\n partial.patch === undefined && high\n ? { op: \"<\", version: high }\n : { op: \"<=\", version: low },\n ];\n }\n default: {\n // Bare or `=`: an exact version pins, a partial becomes the range it covers.\n if (partial.patch !== undefined) {\n return [{ op: \"=\", version: low }];\n }\n return high\n ? [\n { op: \">=\", version: low },\n { op: \"<\", version: high },\n ]\n : [{ op: \">=\", version: low }];\n }\n }\n}\n\nconst HYPHEN_PATTERN = /\\s+-\\s+/;\n\n/** The comparators one AND-set (`>=1 <2`, or `1.2.3 - 2.3.4`) expands into. */\nfunction parseComparatorSet(input: string): Comparator[] | undefined {\n const trimmed = input.trim();\n if (trimmed === \"\") {\n return [];\n }\n if (HYPHEN_PATTERN.test(trimmed)) {\n const sides = trimmed.split(HYPHEN_PATTERN);\n if (sides.length !== 2) {\n return undefined;\n }\n const low = parsePartial(sides[0]!);\n const high = parsePartial(sides[1]!);\n if (!low || !high) {\n return undefined;\n }\n const out: Comparator[] = [];\n if (low.major !== undefined) {\n out.push({ op: \">=\", version: floor(low) });\n }\n if (high.major === undefined) {\n return out;\n }\n // `1 - 2` includes all of 2.x; `1 - 2.3.4` stops at that exact version.\n const bound = ceiling(high);\n out.push(\n high.patch === undefined && bound\n ? { op: \"<\", version: bound }\n : { op: \"<=\", version: floor(high) }\n );\n return out;\n }\n\n const comparators: Comparator[] = [];\n for (const token of trimmed.split(/\\s+/)) {\n const parsed = parseToken(token);\n if (!parsed) {\n return undefined;\n }\n comparators.push(...parsed);\n }\n return comparators;\n}\n\n/** The parsed range, or `undefined` when the string is not a range this grammar accepts. */\nexport function parseRange(input: string): Range | undefined {\n const sets: Range = [];\n for (const part of input.split(\"||\")) {\n // An empty side of `||` (as in `\"||\"`) is a typo, not \"match anything\".\n if (input.includes(\"||\") && part.trim() === \"\") {\n return undefined;\n }\n const set = parseComparatorSet(part);\n if (!set) {\n return undefined;\n }\n sets.push(set);\n }\n return sets;\n}\n\n/** True when the string is a range `satisfies` can evaluate. `doctor` reports the rest. */\nexport function isValidRange(input: string): boolean {\n return parseRange(input) !== undefined;\n}\n\nfunction matches(value: SemVer, comparator: Comparator): boolean {\n const order = compareVersions(value, comparator.version);\n switch (comparator.op) {\n case \">\": {\n return order > 0;\n }\n case \">=\": {\n return order >= 0;\n }\n case \"<\": {\n return order < 0;\n }\n case \"<=\": {\n return order <= 0;\n }\n default: {\n return order === 0;\n }\n }\n}\n\nfunction sameTuple(a: SemVer, b: SemVer): boolean {\n return a.major === b.major && a.minor === b.minor && a.patch === b.patch;\n}\n\nfunction satisfiesSet(value: SemVer, set: Comparator[]): boolean {\n if (!set.every((comparator) => matches(value, comparator))) {\n return false;\n }\n if (value.prerelease.length === 0) {\n return true;\n }\n // node-semver's default: a prerelease is only in range when the author wrote a\n // prerelease of the same version into the range, so `>=0.9` never picks up `1.0.0-rc.1`.\n return set.some(\n (comparator) =>\n comparator.version.prerelease.length > 0 &&\n sameTuple(value, comparator.version)\n );\n}\n\n/** True when `version` falls inside `range`. False when either side does not parse. */\nexport function satisfies(candidate: string, range: string): boolean {\n const parsed = parseVersion(candidate);\n const sets = parseRange(range);\n if (!parsed || !sets) {\n return false;\n }\n return sets.some((set) => satisfiesSet(parsed, set));\n}\n","import { isValidRange, satisfies } from \"./semver.js\";\nimport type { Graph } from \"./resolve.js\";\n\n// `requires.saasaloy` enforcement — \"this descriptor needs a CLI at least this new\".\n//\n// A descriptor sets `additionalProperties: false`, so an old CLI meeting a field it has\n// never heard of already fails. It fails as an Ajv dump about an unexpected property,\n// which tells the user nothing about what to do, and a field whose *meaning* changed\n// fails not at all: the old CLI reads it the old way and half-applies the module. One\n// declared range turns both cases into the same sentence, before anything is written.\n//\n// Kept apart from `requires.ts`, which enforces `requiresOneOf` — a module-choice rule\n// with nothing to do with the CLI's version. Two unrelated meanings of \"requires\" in one\n// file would be a permanent reading tax.\n//\n// The check is fatal, never a warning. A module that says it needs a newer CLI is saying\n// the applier in front of it will get the module wrong, and a warning would leave the\n// project half-written with no way back.\n\n/** How a consumer gets a newer CLI. Named in every refusal so the fix is in the message. */\nexport const UPGRADE_COMMAND = \"pnpm add --global saasaloy@latest\";\n\n/** What `readVersion()` returns when the CLI's own package.json will not parse. */\nexport const UNKNOWN_VERSION = \"unknown\";\n\n/** Why one module's `requires` could not be satisfied. */\nexport type MismatchReason =\n /** The range parsed and the running CLI falls outside it. */\n | \"unsatisfied\"\n /** The range is not a range — an author error, reported rather than assumed to pass. */\n | \"unparseable\"\n /** The CLI cannot read its own version, so no range can be honestly evaluated. */\n | \"unknown-version\";\n\nexport interface CliMismatch {\n /** The module whose descriptor declared the range. May be a transitive prerequisite. */\n declaredBy: string;\n /** The range exactly as the descriptor spells it. */\n range: string;\n reason: MismatchReason;\n}\n\nexport interface DetectCliMismatchesArgs {\n /** The resolved `dependsOn` graph for this run — every descriptor it touched. */\n graph: Graph;\n /** The running CLI's version, from `readVersion()`. */\n cliVersion: string;\n}\n\n/**\n * Every module in the graph whose `requires.saasaloy` the running CLI fails, in\n * topological order so a prerequisite is reported before the module that dragged it in.\n * Empty means the run may proceed.\n *\n * Transitive by construction: the graph holds every descriptor resolution touched, not\n * only the one the user named, so a prerequisite's range is enforced exactly like the\n * requested module's.\n */\nexport function detectCliMismatches(\n args: DetectCliMismatchesArgs\n): CliMismatch[] {\n const { graph, cliVersion } = args;\n const mismatches: CliMismatch[] = [];\n\n for (const name of graph.order) {\n const range = graph.modules.get(name)?.item.requires?.saasaloy;\n // The field is optional, and its absence means \"any CLI\" — every descriptor written\n // before the field existed keeps working, on every version.\n if (range === undefined) {\n continue;\n }\n if (!isValidRange(range)) {\n mismatches.push({ declaredBy: name, range, reason: \"unparseable\" });\n continue;\n }\n // An unreadable version refuses rather than passing silently: \"I could not check\"\n // is not \"you are fine\", and the failure mode of guessing is a half-applied module.\n if (cliVersion === UNKNOWN_VERSION) {\n mismatches.push({ declaredBy: name, range, reason: \"unknown-version\" });\n continue;\n }\n if (!satisfies(cliVersion, range)) {\n mismatches.push({ declaredBy: name, range, reason: \"unsatisfied\" });\n }\n }\n\n return mismatches;\n}\n\n// One mismatch as a sentence. `requested` is what the user typed, so a range raised by a\n// transitive prerequisite says so rather than naming a module the user never mentioned —\n// the same shape `requires.ts` and `conflicts.ts` use.\nfunction describe(\n mismatch: CliMismatch,\n requested: string,\n cliVersion: string\n): string {\n const { declaredBy, range, reason } = mismatch;\n const subject =\n declaredBy === requested\n ? declaredBy\n : `${declaredBy} (required by ${requested})`;\n if (reason === \"unparseable\") {\n return `${subject} declares requires.saasaloy \"${range}\", which isn't a semver range — the module's author has to fix it.`;\n }\n if (reason === \"unknown-version\") {\n return `${subject} needs saasaloy ${range}, and this CLI's own version could not be read. Upgrade with \\`${UPGRADE_COMMAND}\\`.`;\n }\n return `${subject} needs saasaloy ${range}, and ${cliVersion} is installed. Upgrade with \\`${UPGRADE_COMMAND}\\`.`;\n}\n\n/** The refusal a command prints. One line per mismatch, under a heading. */\nexport function formatCliMismatches(\n mismatches: CliMismatch[],\n requested: string,\n cliVersion: string\n): string {\n const heading = `Cannot apply ${requested} — this CLI is too old for ${mismatches.length > 1 ? \"these modules\" : \"it\"}:`;\n return [\n heading,\n ...mismatches.map((m) => ` ${describe(m, requested, cliVersion)}`),\n `Nothing was written.`,\n ].join(\"\\n\");\n}\n","import type { Lockfile } from \"./lock.js\";\nimport type { Graph } from \"./resolve.js\";\nimport type { SaasaloyConfig } from \"./schema.js\";\n\n// `conflictsWith` enforcement for `saasaloy add` — the deterministic core, kept out of\n// commands/add.ts so it can be unit-tested (the commands have no test files).\n//\n// Two modules can be mutually exclusive: two drivers behind one capability, two mailers\n// behind one interface. Either side may be the one that declares it, and the refusal has\n// to fire whichever module went in first, so the check runs in both directions:\n//\n// forward — a descriptor in the resolved graph names an already-installed module;\n// reverse — an already-installed module's lock entry names a module in the graph.\n//\n// The reverse direction is why `conflictsWith` is recorded into saasaloy-lock.json beside\n// `dependsOn`: descriptors aren't kept after install, so the lock is the only offline\n// record of what an installed module refuses to sit beside. Same mechanism buildRemovePlan\n// already uses for reverse-dependency detection.\n//\n// A conflict is reported, never resolved. `add` refuses; it uninstalls nothing.\n\nexport interface ModuleConflict {\n /** The module that declared the conflict — in its descriptor, or in its lock entry. */\n declaredBy: string;\n /** The module named in that `conflictsWith` list. */\n conflictsWith: string;\n /**\n * Whichever of the two is already installed, and therefore the one `saasaloy remove`\n * can clear. Undefined when both modules arrive in this same run.\n */\n installed?: string;\n}\n\nexport interface ConflictReport {\n /** Every conflicting pair found, one per pair. Non-empty means `add` must refuse. */\n conflicts: ModuleConflict[];\n /**\n * Installed modules with no lock entry at all: nothing records what they conflict\n * with, so the reverse direction is unverifiable for them (mirrors\n * `RemovePlan.missingLockEntries`). A lock entry written before this field existed is\n * indistinguishable from one whose module declares no conflicts, so it can't be\n * flagged here — this covers the gap that is actually visible.\n *\n * Every name in `installed[]` is a module `saasaloy add` applied, so every one of them\n * belongs here when its lock entry is missing. The base app is no longer in that list:\n * it has its own `base` field (#98), which is what retired the `managed` allowlist this\n * report used to need.\n */\n missingLockEntries: string[];\n}\n\nexport interface DetectConflictsArgs {\n /** The resolved `dependsOn` graph for this run — descriptors included. */\n graph: Graph;\n config: SaasaloyConfig;\n lock: Lockfile;\n}\n\n// Order-independent key, so a pair both sides declare is reported once. The separator is\n// written as the two-character escape, never a raw NUL byte, or git treats this file as\n// binary and it stops being reviewable.\nfunction pairKey(a: string, b: string): string {\n return a < b ? `${a}\\0${b}` : `${b}\\0${a}`;\n}\n\nexport function detectConflicts(args: DetectConflictsArgs): ConflictReport {\n const { graph, config, lock } = args;\n const installed = new Set(config.installed);\n const conflicts: ModuleConflict[] = [];\n const seen = new Set<string>();\n\n function record(declaredBy: string, conflictsWith: string): void {\n // A module naming itself is a typo, not a self-conflict — ignore it.\n if (declaredBy === conflictsWith) {\n return;\n }\n const key = pairKey(declaredBy, conflictsWith);\n if (seen.has(key)) {\n return;\n }\n seen.add(key);\n const installedSide = installed.has(conflictsWith)\n ? conflictsWith\n : installed.has(declaredBy)\n ? declaredBy\n : undefined;\n conflicts.push({\n declaredBy,\n conflictsWith,\n ...(installedSide ? { installed: installedSide } : {}),\n });\n }\n\n // Forward. A fresh descriptor names something already installed, or something else in\n // this same graph — two mutually exclusive modules pulled in by one `add`.\n for (const [name, mod] of graph.modules) {\n for (const other of mod.item.conflictsWith ?? []) {\n if (installed.has(other) || graph.modules.has(other)) {\n record(name, other);\n }\n }\n }\n\n // Reverse. An installed module's lock entry names something arriving in this run.\n // Installed modules whose descriptor is in the graph are skipped: the forward pass just\n // read that descriptor, and a fresh descriptor beats a recorded copy of one.\n const missingLockEntries: string[] = [];\n for (const name of config.installed) {\n if (graph.modules.has(name)) {\n continue;\n }\n const entry = lock.modules[name];\n if (!entry) {\n missingLockEntries.push(name);\n continue;\n }\n for (const other of entry.conflictsWith ?? []) {\n if (graph.modules.has(other)) {\n record(name, other);\n }\n }\n }\n\n return { conflicts, missingLockEntries };\n}\n\n/**\n * Which command is reporting. `update` runs the same check because a new version can\n * introduce a `dependsOn` on a second driver, and installing it as a prerequisite would\n * land the exact pair `add` refuses (#98).\n */\nexport type ConflictAction = \"add\" | \"update\";\n\nconst GERUND: Record<ConflictAction, string> = {\n add: \"adding\",\n update: \"updating\",\n};\n\n// One conflict as a sentence: both module names, which side declared it, and the way out.\n// `requested` is what the user typed, so a conflict raised by a transitive prerequisite\n// says so rather than naming a module the user never mentioned.\nfunction describe(\n conflict: ModuleConflict,\n requested: string,\n action: ConflictAction\n): string {\n const { declaredBy, conflictsWith, installed } = conflict;\n\n if (!installed) {\n const first =\n declaredBy === requested\n ? declaredBy\n : `${declaredBy} (required by ${requested})`;\n return `${first} declares a conflict with ${conflictsWith}, and ${GERUND[action]} ${requested} installs both. Add only one of them.`;\n }\n\n const incoming = installed === declaredBy ? conflictsWith : declaredBy;\n const phrase =\n incoming === requested\n ? incoming\n : `${incoming} (required by ${requested})`;\n const sentence =\n installed === declaredBy\n ? `${installed} is already installed and declares a conflict with ${phrase}`\n : `${phrase} declares a conflict with ${installed}, which is already installed`;\n return `${sentence}. Run \\`saasaloy remove ${installed}\\` first.`;\n}\n\n/** The refusal `add` and `update` print. One line per conflicting pair, under a heading. */\nexport function formatConflicts(\n conflicts: ModuleConflict[],\n requested: string,\n action: ConflictAction = \"add\"\n): string {\n const heading = `Cannot ${action} ${requested} — module conflict${conflicts.length > 1 ? \"s\" : \"\"}:`;\n return [\n heading,\n ...conflicts.map((c) => ` ${describe(c, requested, action)}`),\n ].join(\"\\n\");\n}\n","import type { Plan } from \"./applier.js\";\n\n// The project's design contract (`DESIGN.md`) is derived from what `packages/ui/`\n// actually ships, so anything a module writes there can invalidate it. `add` uses\n// this to prompt for a re-derivation.\n\nconst UI_PREFIX = \"packages/ui/\";\n\n// Both halves of a plan can land in `packages/ui/`: a file the module copies in, and\n// a structural patch whose target happens to live there. Only the actions that write\n// count — a held or unchanged entry leaves the file as it was.\nexport function planWritesUi(plan: Pick<Plan, \"files\" | \"patches\">): boolean {\n const writesFile = plan.files.some(\n (file) =>\n (file.action === \"create\" || file.action === \"overwrite\") &&\n file.target.startsWith(UI_PREFIX)\n );\n if (writesFile) {\n return true;\n }\n return plan.patches.some(\n (patch) => patch.action === \"apply\" && patch.file.startsWith(UI_PREFIX)\n );\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, posix } from \"node:path\";\nimport {\n assertNoSymlinkPath,\n pathExists,\n resolveWithinRoot,\n} from \"./fs-utils.js\";\n\n// `apps/api/.dev.vars.example` — the checked-in template a developer copies to\n// `.dev.vars` before `wrangler dev`. The base's `_gitignore` has carved out an\n// exception for this exact filename since the template shipped, and until #98 nothing\n// ever wrote it: every module printed its env vars into the `add` transcript, where\n// they scrolled away. `add` now generates the file from the descriptors' own `envVars`\n// maps, so the record of what the project needs lives in the project.\n//\n// Two rules keep the regeneration safe:\n//\n// 1. A value already in the file wins. The file is checked in, so a team edits it,\n// and `saasaloy add` must never revert that edit.\n// 2. Nothing is ever removed. `saasaloy remove` owns deletions; an `add` that dropped\n// a key would silently break a second module that reads the same variable.\n\nexport const DEV_VARS_EXAMPLE = \".dev.vars.example\";\n\n/** The alias whose workspace holds the Worker that reads `.dev.vars`. */\nconst API_ALIAS = \"@api\";\n\n/**\n * The workspace root of the Worker, or undefined when this project has no api workspace\n * yet. The `@api` alias points at the Worker's *source* directory (`apps/api/src`), so\n * its parent is the workspace `wrangler dev` runs in — where `.dev.vars` and\n * `wrangler.jsonc` both live.\n *\n * Exported because `saasaloy env` routes every non-`PUBLIC_` variable to this same\n * workspace (#50): `.dev.vars` is a wrangler file, so the Worker is the only thing that\n * reads one, whichever package declared the variable.\n */\nexport function apiWorkspace(\n aliases: Record<string, string>\n): string | undefined {\n const src = aliases[API_ALIAS];\n if (!src) {\n return undefined;\n }\n const workspace = posix.dirname(src.replace(/\\/+$/, \"\"));\n return workspace === \".\" || workspace === \"/\" ? undefined : workspace;\n}\n\nconst HEADER = [\n \"# Environment variables this project's modules declare.\",\n \"# Generated by `saasaloy add`, one entry per declared variable, comments and all.\",\n \"#\",\n \"# Copy this file to `.dev.vars` and fill in the blanks: `.dev.vars` is what\",\n \"# `wrangler dev` reads, and it is gitignored. This example is not — keep it\",\n \"# checked in, and keep real secrets out of it.\",\n];\n\n/**\n * Where the file belongs, or undefined when this project has no api workspace yet.\n * The `@api` alias points at the Worker's source directory (`apps/api/src`), so its\n * parent is the workspace root that `wrangler dev` runs in.\n */\nexport function devVarsExamplePath(\n aliases: Record<string, string>\n): string | undefined {\n const workspace = apiWorkspace(aliases);\n return workspace ? posix.join(workspace, DEV_VARS_EXAMPLE) : undefined;\n}\n\n/**\n * The `KEY=value` pairs already in a `.dev.vars.example`. Comments and blank lines are\n * dropped: they are regenerated from the descriptors, which are the current truth about\n * what a variable is for. Only the values a person typed are worth carrying over.\n */\nexport function parseDevVars(text: string): Record<string, string> {\n const values: Record<string, string> = {};\n for (const line of text.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) {\n continue;\n }\n const eq = trimmed.indexOf(\"=\");\n if (eq <= 0) {\n continue;\n }\n values[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1);\n }\n return values;\n}\n\nexport interface RenderDevVarsArgs {\n /** Every variable the installed descriptors declare: name → description. */\n envVars: Record<string, string>;\n /** Local-dev values the descriptors supply for some of them. */\n devVars: Record<string, string>;\n /** Values already in the file, which always win over `devVars`. */\n existing: Record<string, string>;\n}\n\nexport function renderDevVarsExample(args: RenderDevVarsArgs): string {\n const { envVars, devVars, existing } = args;\n // Everything the file already carried stays, even when no installed module declares\n // it any more — a hand-added variable is not ours to delete.\n const names = [\n ...new Set([...Object.keys(envVars), ...Object.keys(existing)]),\n ].toSorted();\n\n const blocks = names.map((name) => {\n const value = existing[name] ?? devVars[name] ?? \"\";\n const description = envVars[name];\n const lines = description\n ? description.split(\"\\n\").map((line) => `# ${line}`)\n : [\"# Declared outside saasaloy — no module describes this one.\"];\n lines.push(`${name}=${value}`);\n return lines.join(\"\\n\");\n });\n\n return `${[HEADER.join(\"\\n\"), ...blocks].join(\"\\n\\n\")}\\n`;\n}\n\nexport interface WriteDevVarsArgs extends Omit<RenderDevVarsArgs, \"existing\"> {\n root: string;\n aliases: Record<string, string>;\n}\n\n/**\n * Write (or refresh) the example file. Returns the project-relative path written, or\n * undefined when there is no api workspace or nothing to declare — a project with no\n * environment variables gets no file rather than an empty one.\n */\nexport async function writeDevVarsExample(\n args: WriteDevVarsArgs\n): Promise<string | undefined> {\n const { root, aliases, envVars, devVars } = args;\n const target = devVarsExamplePath(aliases);\n if (!target) {\n return undefined;\n }\n const abs = resolveWithinRoot(root, target);\n // `resolveWithinRoot` proves the path is lexically inside the project; the read and the\n // write below both follow links, so a planted symlink on any component would carry them\n // outside it. Refuse before either (#98), matching `applier` and `remover`.\n await assertNoSymlinkPath(root, abs);\n // One read, reused for the parse and the compare. Reading twice would compare the\n // rendered content against a file that may have changed since the values were parsed\n // out of it, and then overwrite that change with content rendered from the stale copy.\n const before = (await pathExists(abs))\n ? await readFile(abs, \"utf-8\")\n : undefined;\n const existing = before === undefined ? {} : parseDevVars(before);\n if (Object.keys(envVars).length === 0 && Object.keys(existing).length === 0) {\n return undefined;\n }\n const content = renderDevVarsExample({ devVars, envVars, existing });\n if (before === content) {\n return undefined;\n }\n await mkdir(dirname(abs), { recursive: true });\n await writeFile(abs, content, \"utf-8\");\n return target;\n}\n","// A dependency-free line diff for `--diff`. Classic LCS backtrack: emit each line\n// prefixed with \" \" (context), \"-\" (removed) or \"+\" (added). Good enough to show a\n// human (or hand to an agent) what an update would change; we deliberately don't\n// pull in a diff library — the CLI stays lean and its deps stay auditable.\n\nexport interface DiffLine {\n kind: \"context\" | \"del\" | \"add\";\n text: string;\n}\n\nexport function lineDiff(oldText: string, newText: string): DiffLine[] {\n // An empty side is a pure creation/deletion. Special-cased because \"\".split(\"\\n\")\n // is [\"\"], which would inject a spurious blank del/add line whenever the other\n // side has no empty line (e.g. a created file without a trailing newline).\n if (oldText === \"\") {\n return newText.split(\"\\n\").map((text): DiffLine => ({ kind: \"add\", text }));\n }\n if (newText === \"\") {\n return oldText.split(\"\\n\").map((text): DiffLine => ({ kind: \"del\", text }));\n }\n const a = oldText.split(\"\\n\");\n const b = newText.split(\"\\n\");\n const m = a.length;\n const n = b.length;\n\n // dp[i][j] = length of the LCS of a[i..] and b[j..].\n const dp: number[][] = Array.from({ length: m + 1 }, () =>\n Array.from<number>({ length: n + 1 }).fill(0)\n );\n for (let i = m - 1; i >= 0; i--) {\n for (let j = n - 1; j >= 0; j--) {\n dp[i]![j] =\n a[i] === b[j]\n ? dp[i + 1]![j + 1]! + 1\n : Math.max(dp[i + 1]![j]!, dp[i]![j + 1]!);\n }\n }\n\n const out: DiffLine[] = [];\n let i = 0;\n let j = 0;\n while (i < m && j < n) {\n if (a[i] === b[j]) {\n out.push({ kind: \"context\", text: a[i]! });\n i++;\n j++;\n } else if (dp[i + 1]![j]! >= dp[i]![j + 1]!) {\n out.push({ kind: \"del\", text: a[i]! });\n i++;\n } else {\n out.push({ kind: \"add\", text: b[j]! });\n j++;\n }\n }\n while (i < m) {\n out.push({ kind: \"del\", text: a[i++]! });\n }\n while (j < n) {\n out.push({ kind: \"add\", text: b[j++]! });\n }\n return out;\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { RefusalError } from \"./exit.js\";\nimport { pathExists } from \"./fs-utils.js\";\nimport type { ModuleProvenance } from \"./registry.js\";\nimport type { Graph } from \"./resolve.js\";\nimport { validateLock } from \"./schema.js\";\n\n// `saasaloy-lock.json` records, for every installed module, the registry source it came\n// from and the exact commit SHA it resolved to — the npm-style lock to `saasaloy.json`'s\n// intent (ADR 0012). The resolved SHA is the integrity anchor: a re-install against the\n// committed lock reproduces identical bytes. Shape: schemas/saasaloy-lock.schema.json.\n\nexport const LOCK_FILE = \"saasaloy-lock.json\";\nexport const LOCKFILE_VERSION = 1;\nconst LOCK_SCHEMA_URL =\n \"https://saasaloy.dev/schemas/saasaloy-lock.schema.json\";\n\nexport interface LockModule extends ModuleProvenance {\n /** The module's declared dependencies, so the resolved graph is self-describing. */\n dependsOn?: string[];\n /** The module's declared `conflictsWith`, so `add` can refuse a conflicting module\n * that goes in second — the descriptor is gone by then, the lock is not. */\n conflictsWith?: string[];\n}\n\n/**\n * Where the base came from (#120). The base ships inside the CLI package rather than in\n * a registry, so its provenance is a CLI version rather than a commit: `templateHash` is\n * a sha256 over the shipped template's sorted `(path, hash)` pairs and is what `outdated`\n * compares, and `cliVersion` is the label printed beside it. Every build is `0.0.0` until\n * a release process exists, which is why the hash, not the version, carries the verdict.\n */\nexport interface LockBase {\n /** The base app's name — `saasaloy.json`'s `base` field, `web`. */\n name: string;\n /** The CLI that rendered or adopted the base, from `readVersion()`. */\n cliVersion: string;\n /** sha256 over the shipped template as `templateHash()` computes it. */\n templateHash: string;\n}\n\nexport interface Lockfile {\n $schema?: string;\n lockfileVersion: number;\n modules: Record<string, LockModule>;\n /** Absent on a project scaffolded before the base carried provenance; `update` adopts it. */\n base?: LockBase;\n}\n\nexport function emptyLock(): Lockfile {\n return {\n $schema: LOCK_SCHEMA_URL,\n lockfileVersion: LOCKFILE_VERSION,\n modules: {},\n };\n}\n\nexport async function loadLock(root: string): Promise<Lockfile> {\n const file = join(root, LOCK_FILE);\n if (!(await pathExists(file))) {\n return emptyLock();\n }\n // Unparseable JSON is the same invalid state as a schema miss, so it takes the same\n // exit code. Left to throw, `SyntaxError` reaches `exitCodeFor` as a plain failure (1)\n // and never names the file it came from.\n let raw: unknown;\n try {\n raw = JSON.parse(await readFile(file, \"utf-8\"));\n } catch (error) {\n throw new RefusalError(\n `${LOCK_FILE} is invalid:\\n ${error instanceof Error ? error.message : String(error)}`,\n { cause: error }\n );\n }\n // The lock is what `update` diffs against and what `add` reads a module's\n // `conflictsWith` back out of, so an entry with no `resolved` SHA or an unknown\n // `lockfileVersion` has to stop the command here (#98). Same posture as `loadConfig`.\n const result = await validateLock(raw);\n if (!result.valid) {\n throw new RefusalError(\n `${LOCK_FILE} is invalid:\\n ${result.errors.join(\"\\n \")}`\n );\n }\n const parsed = raw as Partial<Lockfile>;\n return {\n $schema: parsed.$schema ?? LOCK_SCHEMA_URL,\n lockfileVersion: parsed.lockfileVersion ?? LOCKFILE_VERSION,\n modules: parsed.modules ?? {},\n ...(parsed.base ? { base: parsed.base } : {}),\n };\n}\n\nexport async function saveLock(root: string, lock: Lockfile): Promise<void> {\n const file = join(root, LOCK_FILE);\n await writeFile(file, `${JSON.stringify(lock, null, 2)}\\n`, \"utf-8\");\n}\n\n// Record the modules that were actually applied under one source's provenance —\n// intra-repo, so they share the same source/ref/SHA. Only `installed` is written: an\n// already-installed dependency keeps the SHA it was fetched at, so the lock never\n// misstates the provenance of bytes on disk. `graph` supplies each module's declared\n// `dependsOn` and `conflictsWith` — the two lists `remove` and `add` need to read back\n// offline, once the descriptor they came from is gone.\nexport function upsertLock(\n lock: Lockfile,\n provenance: ModuleProvenance,\n installed: string[],\n graph: Graph\n): void {\n for (const name of installed) {\n const item = graph.modules.get(name)?.item;\n const dependsOn = item?.dependsOn;\n const conflictsWith = item?.conflictsWith;\n lock.modules[name] = {\n ...provenance,\n ...(dependsOn && dependsOn.length > 0 ? { dependsOn } : {}),\n ...(conflictsWith && conflictsWith.length > 0 ? { conflictsWith } : {}),\n };\n }\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { pathExists } from \"./fs-utils.js\";\n\n// Merge a module's npm deps into the consumer project's root package.json (build spec\n// §2.4 \"apply files + npm deps\"). Descriptors carry two buckets — `dependencies[]` and\n// `devDependencies[]` — each an exact-pinned `name@version` (schema-enforced). A bare\n// name still parses (falling back to \"latest\"), but descriptors no longer author them.\n//\n// Scope note: v1 adds deps at the project root. Routing each dep to the specific\n// workspace it belongs to (apps/api vs packages/db) needs the capability modules'\n// workspace conventions (issues #8/#9), which don't exist yet — root placement is\n// deterministic, reversible, and hoists across the pnpm workspace until then.\n\n/** Split `name@version` into parts, keeping the leading `@` of a scoped name intact. */\nexport function parseDep(spec: string): { name: string; version: string } {\n const at = spec.lastIndexOf(\"@\");\n if (at > 0) {\n return { name: spec.slice(0, at), version: spec.slice(at + 1) };\n }\n return { name: spec, version: \"latest\" };\n}\n\nexport interface DepChange {\n name: string;\n version: string;\n}\n\nexport interface AddDepsResult {\n /** Deps newly written into `dependencies`. */\n added: DepChange[];\n /** Deps newly written into `devDependencies`. */\n devAdded: DepChange[];\n /** Deps already present in either bucket (left untouched). */\n skipped: string[];\n /** Human-readable version disagreements — the kept version won, the other was ignored. */\n conflicts: string[];\n}\n\n/**\n * Compute which deps are new vs already declared, without touching disk. `dependencies`\n * is planned first and claims a name across both buckets: a package can't land in both,\n * and `dependencies` wins (a `devDependencies` entry for an already-claimed name is\n * dropped). Within a bucket, the first module to declare a dep wins (topological order).\n */\nexport function planDeps(\n pkg: PackageJson,\n deps: string[],\n devDeps: string[] = []\n): AddDepsResult {\n const conflicts: string[] = [];\n const skipped: string[] = [];\n // Name → chosen version across both buckets, so a later module — or the dev bucket —\n // pinning a different version is a real disagreement worth surfacing, not silently dropping.\n const seen = new Map<string, string>();\n\n function planBucket(\n specs: string[],\n existing: Record<string, string>\n ): DepChange[] {\n const out: DepChange[] = [];\n for (const spec of specs) {\n const { name, version } = parseDep(spec);\n const prior = seen.get(name);\n if (prior !== undefined) {\n if (prior !== version) {\n conflicts.push(`${name}: keeping ${prior}, ignoring ${version}`);\n }\n continue;\n }\n seen.set(name, version);\n const current = existing[name];\n if (current === undefined) {\n out.push({ name, version });\n } else {\n if (version !== \"latest\" && current !== version) {\n conflicts.push(\n `${name}: package.json already has ${current}, ignoring ${version}`\n );\n }\n skipped.push(name);\n }\n }\n return out;\n }\n\n const added = planBucket(deps, pkg.dependencies ?? {});\n const devAdded = planBucket(devDeps, pkg.devDependencies ?? {});\n return { added, conflicts, devAdded, skipped };\n}\n\nexport interface PackageJson {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n [key: string]: unknown;\n}\n\nexport async function readRootPackageJson(\n root: string\n): Promise<PackageJson | null> {\n const file = join(root, \"package.json\");\n if (!(await pathExists(file))) {\n return null;\n }\n return JSON.parse(await readFile(file, \"utf-8\")) as PackageJson;\n}\n\n/** Merge additions into an existing bucket, keeping keys sorted. */\nfunction mergeSorted(\n existing: Record<string, string> | undefined,\n added: DepChange[]\n): Record<string, string> {\n const merged = { ...existing };\n for (const { name, version } of added) {\n merged[name] = version;\n }\n const sorted: Record<string, string> = {};\n for (const key of Object.keys(merged).toSorted()) {\n sorted[key] = merged[key]!;\n }\n return sorted;\n}\n\n/** Apply the planned additions to package.json on disk, keeping each bucket sorted. */\nexport async function writeDeps(\n root: string,\n pkg: PackageJson,\n added: DepChange[],\n devAdded: DepChange[] = []\n): Promise<void> {\n if (added.length === 0 && devAdded.length === 0) {\n return;\n }\n if (added.length > 0) {\n pkg.dependencies = mergeSorted(pkg.dependencies, added);\n }\n if (devAdded.length > 0) {\n pkg.devDependencies = mergeSorted(pkg.devDependencies, devAdded);\n }\n const file = join(root, \"package.json\");\n await writeFile(file, `${JSON.stringify(pkg, null, 2)}\\n`, \"utf-8\");\n}\n","import { dirname, join, parse } from \"node:path\";\nimport { pathExists } from \"./fs-utils.js\";\n\n// Walk up from `start` looking for a Saasaloy project marker so commands work from\n// any subdirectory, like git. `package.json` is deliberately NOT a marker: in a\n// monorepo every package has one, which would stop the search at the nearest\n// package instead of the project root that owns saasaloy.json. Falls back to the\n// starting dir when nothing is found.\nconst MARKERS = [\"saasaloy.json\"];\n\nexport async function findProjectRoot(\n start: string = process.cwd()\n): Promise<string> {\n let dir = start;\n const { root } = parse(dir);\n for (;;) {\n for (const marker of MARKERS) {\n if (await pathExists(join(dir, marker))) {\n return dir;\n }\n }\n if (dir === root) {\n return start;\n }\n dir = dirname(dir);\n }\n}\n","import { mkdtemp, readFile, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { isAbsolute, join, resolve } from \"node:path\";\nimport { downloadTemplate } from \"giget\";\nimport { RefusalError } from \"./exit.js\";\nimport { pathExists, readDirNames } from \"./fs-utils.js\";\nimport type { RegistryItem } from \"./schema.js\";\nimport { validateRegistryItem } from \"./schema.js\";\n\n// A module registry is a GitHub repo whose `modules/<name>/` folders each hold one\n// module's `registry-item.json` descriptor plus the files it drops in (build spec §2.4,\n// ADR 0012 — remote-first, the repo *is* the registry). `saasaloy add` resolves a ref to\n// a commit SHA and fetches the module subtree at that SHA; the resolved SHA is written to\n// `saasaloy-lock.json` for reproducibility. A local checkout (SAASALOY_REGISTRY_DIR) is a\n// dev/offline override. This module is the single seam that knows *where* descriptors live.\n\n// Point the applier at a local `modules/` checkout instead of fetching — for developing\n// modules and for offline/CI runs against fixtures.\nexport const REGISTRY_ENV = \"SAASALOY_REGISTRY_DIR\";\n\n// Where a bare `saasaloy add <name>` resolves: the CLI's own repo is the first-party\n// registry. An explicit `owner/repo/name` coordinate overrides it.\nexport const DEFAULT_OWNER = \"mimukit\";\nexport const DEFAULT_REPO = \"saasaloy\";\n\nexport interface LoadedModule {\n /** Absolute path to a local copy of the module's folder (source of `files[].path`). */\n dir: string;\n item: RegistryItem;\n}\n\n/** Where a module was resolved from — the lockfile entry, minus `dependsOn`. */\nexport interface ModuleProvenance {\n /** `owner/repo` for a remote source, or `local` for a SAASALOY_REGISTRY_DIR checkout. */\n source: string;\n /** The ref requested (branch/tag/SHA), or `local`. */\n ref: string;\n /** The exact commit SHA resolved, or `local` — the integrity anchor. */\n resolved: string;\n}\n\n/** The seam that knows *where* descriptors live. Remote (default) or local (override). */\nexport interface RegistrySource {\n /** Human label for messages (e.g. `mimukit/saasaloy@main` or a local path). */\n readonly label: string;\n /** Read + validate one module descriptor by name; `dir` is a local copy of its folder. */\n readModule(name: string, requiredBy?: string): Promise<LoadedModule>;\n /** Names of the modules this source offers — powers the interactive picker. */\n listModules(): Promise<string[]>;\n /** Provenance for the lockfile (source + ref + resolved SHA). */\n provenance(): ModuleProvenance;\n /**\n * The commit SHA this source's ref currently points at (`local` for a local\n * checkout). `saasaloy update` compares it against the lock's `resolved` to decide\n * whether anything moved — the same resolution path `readModule` pins to, exposed.\n */\n resolveSha(): Promise<string>;\n /**\n * Subjects of the commits touching `modulePath` between two SHAs, oldest first —\n * the natural-language \"intent\" half of the merge plan (issue #48, decision 4).\n * Best-effort: returns `[]` rather than throwing when the history isn't reachable\n * (force-push, deleted tag, rate limit, local source), so a merge plan degrades to\n * diff-only instead of failing.\n */\n commitSubjects(\n modulePath: string,\n baseSha: string,\n headSha: string\n ): Promise<string[]>;\n /** Drop any temp working dirs created while fetching. No-op for a local source. */\n cleanup?(): Promise<void>;\n}\n\n// --- Module coordinate parsing ---------------------------------------------------\n\nexport interface Coordinate {\n owner?: string;\n repo?: string;\n /** Branch/tag/SHA; undefined means the repo's default branch. */\n ref?: string;\n /** The module to add; undefined means \"show a picker\". */\n module?: string;\n}\n\n// Grammar (build spec §3, ADR 0012):\n// waitlist → default repo, module `waitlist`\n// owner/repo/waitlist → third-party repo, module `waitlist`\n// owner/repo@ref/waitlist → pinned ref\n// owner/repo → no module ⇒ picker over that repo\n// (nothing) → picker over the default repo\n// A ref carrying a `/` (e.g. a `feature/x` branch) isn't addressable in v1 — pin such a\n// branch's tip SHA instead, or rely on the default branch. Pinning a ref on the *default*\n// repo (`waitlist@v2`) is likewise unsupported: a ref requires an explicit `owner/repo`.\nexport function parseCoordinate(input?: string): Coordinate {\n if (!input) {\n return {};\n }\n\n let ref: string | undefined;\n let rest = input;\n const at = input.indexOf(\"@\");\n if (at !== -1) {\n const afterAt = input.slice(at + 1);\n const slash = afterAt.indexOf(\"/\");\n ref = slash === -1 ? afterAt : afterAt.slice(0, slash);\n const beforeAt = input.slice(0, at);\n const afterRef = slash === -1 ? \"\" : afterAt.slice(slash + 1);\n rest = afterRef ? `${beforeAt}/${afterRef}` : beforeAt;\n if (!ref) {\n throw new RefusalError(\n `Malformed coordinate \"${input}\" — empty ref after \"@\".`\n );\n }\n }\n\n const segs = rest.split(\"/\").filter(Boolean);\n\n if (ref === undefined && segs.length === 1) {\n return { module: segs[0] };\n }\n if (segs.length === 2) {\n return { owner: segs[0], repo: segs[1], ref };\n }\n if (segs.length === 3) {\n return { owner: segs[0], repo: segs[1], ref, module: segs[2] };\n }\n\n throw new RefusalError(\n `Malformed coordinate \"${input}\" — expected \"name\", \"owner/repo\", or \"owner/repo[@ref]/name\".`\n );\n}\n\n/** Build the source a coordinate resolves against: local override if set, else remote. */\nexport function createRegistrySource(coord: Coordinate): RegistrySource {\n const override = process.env[REGISTRY_ENV];\n if (override) {\n const dir = isAbsolute(override)\n ? override\n : resolve(process.cwd(), override);\n return new LocalRegistrySource(dir);\n }\n return new RemoteRegistrySource(\n coord.owner ?? DEFAULT_OWNER,\n coord.repo ?? DEFAULT_REPO,\n coord.ref\n );\n}\n\n// --- Shared descriptor loading ---------------------------------------------------\n\n/** Read + validate `<dir>/registry-item.json`, asserting it declares `name`. */\nexport async function loadModuleFolder(\n dir: string,\n name: string,\n requiredBy?: string\n): Promise<LoadedModule> {\n const file = join(dir, \"registry-item.json\");\n if (!(await pathExists(file))) {\n const because = requiredBy ? ` (required by ${requiredBy})` : \"\";\n // Refused, not failed: the coordinate names something the registry does not have,\n // so a retry cannot help (#98's 0/1/2 scheme).\n throw new RefusalError(\n `Unknown module \"${name}\"${because} — no ${name}/registry-item.json in the registry.`\n );\n }\n // A truncated or hand-edited descriptor threw a bare SyntaxError that named no module\n // and exited 1. It is bad input, not a transient failure, so it refuses with the name.\n let parsed: unknown;\n try {\n parsed = JSON.parse(await readFile(file, \"utf-8\")) as unknown;\n } catch (error) {\n throw new RefusalError(\n `Module \"${name}\" has an unreadable descriptor: ${file} is not valid JSON.`,\n { cause: error }\n );\n }\n const result = await validateRegistryItem(parsed);\n if (!result.valid) {\n throw new RefusalError(\n `Module \"${name}\" has an invalid descriptor:\\n ${result.errors.join(\"\\n \")}`\n );\n }\n const item = parsed as RegistryItem;\n if (item.name !== name) {\n throw new RefusalError(\n `Module folder \"${name}\" declares name \"${item.name}\" — the folder and descriptor name must match.`\n );\n }\n return { dir, item };\n}\n\n// --- Local source (SAASALOY_REGISTRY_DIR) ---------------------------------------\n\n/** A local `modules/` directory: `<dir>/<name>/registry-item.json`. Dev/offline. */\nexport class LocalRegistrySource implements RegistrySource {\n constructor(private readonly dir: string) {}\n\n get label(): string {\n return this.dir;\n }\n\n async readModule(name: string, requiredBy?: string): Promise<LoadedModule> {\n await this.assertExists();\n return loadModuleFolder(join(this.dir, name), name, requiredBy);\n }\n\n async listModules(): Promise<string[]> {\n await this.assertExists();\n const names: string[] = [];\n for (const name of await readDirNames(this.dir)) {\n if (await pathExists(join(this.dir, name, \"registry-item.json\"))) {\n names.push(name);\n }\n }\n return names.toSorted();\n }\n\n provenance(): ModuleProvenance {\n return { ref: \"local\", resolved: \"local\", source: \"local\" };\n }\n\n // A working copy has no commit identity and no history to read — `update` treats\n // both as \"no merge base, no intent\" and degrades the merge plan accordingly.\n async resolveSha(): Promise<string> {\n return \"local\";\n }\n\n async commitSubjects(\n _modulePath: string,\n _baseSha: string,\n _headSha: string\n ): Promise<string[]> {\n return [];\n }\n\n private async assertExists(): Promise<void> {\n if (!(await pathExists(this.dir))) {\n throw new RefusalError(`${REGISTRY_ENV}=${this.dir} does not exist.`);\n }\n }\n}\n\n// --- Remote source (GitHub, default) --------------------------------------------\n\nconst GITHUB_API = \"https://api.github.com\";\n\n/**\n * Point the GitHub API calls at another base URL. The remote path is what every real user\n * hits, so it has to be testable, and the only seam that reaches it is an env var: the\n * end-to-end harness spawns the CLI as a subprocess, which no in-process injection can\n * reach. giget reads `GIGET_GITHUB_URL` for the tarball half and builds\n * `${base}/repos/<owner>/<repo>/tarball/<ref>` from it, so one fixture server can serve\n * both halves when both variables name it.\n *\n * Not a general-purpose GitHub Enterprise switch: nothing else in the CLI reads it, and\n * a real GHES base would need `GIGET_GITHUB_URL` set to match.\n */\nexport const GITHUB_API_ENV = \"SAASALOY_GITHUB_API\";\n\n// Read at call time, not at import: a test sets it after this module is loaded, and the\n// e2e subprocess inherits it from its environment.\nfunction githubApiBase(): string {\n const override = process.env[GITHUB_API_ENV];\n return override ? override.replace(/\\/$/, \"\") : GITHUB_API;\n}\n\n// Every GitHub call gets one. Without it a connection that opens and then stalls — a\n// captive portal, a half-dead proxy — hangs `saasaloy add` with no output and no way\n// out but Ctrl-C (#98). Fifteen seconds is far above a healthy round trip and far below\n// a person's patience. No retry: a failure here is reported with the offline escape\n// hatch below, which is faster than waiting through a second attempt.\nconst FETCH_TIMEOUT_MS = 15_000;\n\n// The module download gets a longer one. It is a tarball over a link the API calls never\n// touch, so 15s would fail a healthy fetch on a slow connection, and giget 3.3.0 takes\n// neither a timeout nor an AbortSignal — the race below can only stop *waiting* on the\n// download, not stop the download itself. That is enough: the files land in a temp dir\n// this process deletes, and the point is that `saasaloy add` never hangs with no output.\nconst DOWNLOAD_TIMEOUT_MS = 60_000;\n\n/**\n * Reject when `work` outruns `ms`. The work keeps running; only the wait ends. Used where\n * the underlying call exposes no cancellation of its own.\n */\nasync function withDeadline<T>(\n work: Promise<T>,\n ms: number,\n what: string\n): Promise<T> {\n // The loser of the race still settles. Without a handler of its own, a rejection\n // arriving after the deadline is an unhandled rejection, which takes the process down\n // instead of the message below.\n work.catch(() => {\n // Reported by the race, or irrelevant because the deadline already won.\n });\n let timer: ReturnType<typeof setTimeout> | undefined;\n const deadline = new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => {\n reject(new Error(`${what} did not finish in ${ms / 1000}s`));\n }, ms);\n });\n try {\n return await Promise.race([work, deadline]);\n } finally {\n clearTimeout(timer);\n }\n}\n\n/** The line every network failure ends with, so the offline path is never a secret. */\nexport const OFFLINE_HINT = `Set ${REGISTRY_ENV} to a local \\`modules/\\` checkout to work offline.`;\n\n// GITHUB_TOKEN (or GIGET_AUTH) lifts the 60→5000 req/hr limit and unlocks private repos.\n// Read at call time so tests and long-lived processes can set it after import.\nfunction authToken(): string | undefined {\n return process.env.GITHUB_TOKEN ?? process.env.GIGET_AUTH;\n}\n\ninterface TreeEntry {\n path: string;\n}\n\ninterface CommitEntry {\n sha: string;\n commit: { message: string };\n}\n\n/** First line of a commit message — this repo's Conventional Commits read as a changelog. */\nfunction subjectOf(message: string): string {\n return message.split(\"\\n\", 1)[0]?.trim() ?? \"\";\n}\n\n/** A GitHub repo fetched via giget; descriptors + files pulled at a pinned commit SHA. */\nexport class RemoteRegistrySource implements RegistrySource {\n private resolved?: { ref: string; sha: string };\n private readonly tempDirs: string[] = [];\n\n constructor(\n private readonly owner: string,\n private readonly repo: string,\n private readonly ref?: string\n ) {}\n\n get label(): string {\n return `${this.owner}/${this.repo}${this.ref ? `@${this.ref}` : \"\"}`;\n }\n\n async readModule(name: string, requiredBy?: string): Promise<LoadedModule> {\n const { sha } = await this.resolve();\n const parent = await mkdtemp(join(tmpdir(), \"saasaloy-reg-\"));\n this.tempDirs.push(parent);\n const dir = join(parent, name);\n try {\n await withDeadline(\n downloadTemplate(\n `github:${this.owner}/${this.repo}/modules/${name}#${sha}`,\n {\n auth: authToken(),\n dir,\n force: true,\n }\n ),\n DOWNLOAD_TIMEOUT_MS,\n `Download of module \"${name}\"`\n );\n } catch (error) {\n const because = requiredBy ? ` (required by ${requiredBy})` : \"\";\n const detail = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Could not fetch module \"${name}\"${because} from ${this.owner}/${this.repo} — ${detail}. ${OFFLINE_HINT}`,\n { cause: error }\n );\n }\n return loadModuleFolder(dir, name, requiredBy);\n }\n\n async listModules(): Promise<string[]> {\n const { sha } = await this.resolve();\n const tree = await this.api<{ tree?: TreeEntry[] }>(\n `/repos/${this.owner}/${this.repo}/git/trees/${sha}?recursive=1`\n );\n const names: string[] = [];\n for (const entry of tree.tree ?? []) {\n const name = /^modules\\/([a-z0-9][a-z0-9-]*)\\/registry-item\\.json$/.exec(\n entry.path\n )?.[1];\n if (name) {\n names.push(name);\n }\n }\n return names.toSorted();\n }\n\n provenance(): ModuleProvenance {\n if (!this.resolved) {\n throw new Error(\n \"provenance() called before the source resolved a commit SHA.\"\n );\n }\n return {\n ref: this.resolved.ref,\n resolved: this.resolved.sha,\n source: `${this.owner}/${this.repo}`,\n };\n }\n\n async resolveSha(): Promise<string> {\n return (await this.resolve()).sha;\n }\n\n // GitHub has no \"commits touching this path between A and B\" endpoint, so intersect\n // two queries: `compare` gives the commits between the SHAs, `commits?path=` gives\n // the ones touching the module. Both are reported best-effort — the merge plan is\n // still useful without intent, and an update must never fail over a changelog.\n async commitSubjects(\n modulePath: string,\n baseSha: string,\n headSha: string\n ): Promise<string[]> {\n if (baseSha === headSha) {\n return [];\n }\n try {\n const between = await this.api<{ commits?: CommitEntry[] }>(\n `/repos/${this.owner}/${this.repo}/compare/${baseSha}...${headSha}`\n );\n const touching = await this.api<CommitEntry[]>(\n `/repos/${this.owner}/${this.repo}/commits?sha=${headSha}&path=${encodeURIComponent(modulePath)}&per_page=100`\n );\n const touchingShas = new Set(touching.map((c) => c.sha));\n return (between.commits ?? [])\n .filter((c) => touchingShas.has(c.sha))\n .map((c) => subjectOf(c.commit.message))\n .filter((subject) => subject.length > 0);\n } catch {\n return [];\n }\n }\n\n async cleanup(): Promise<void> {\n await Promise.all(\n this.tempDirs.map((dir) => rm(dir, { force: true, recursive: true }))\n );\n this.tempDirs.length = 0;\n }\n\n // Resolve the requested ref (or the repo's default branch) to a concrete commit SHA,\n // once per source — every module in one install pins to the same SHA.\n private async resolve(): Promise<{ ref: string; sha: string }> {\n if (this.resolved) {\n return this.resolved;\n }\n const ref =\n this.ref ??\n (\n await this.api<{ default_branch: string }>(\n `/repos/${this.owner}/${this.repo}`\n )\n ).default_branch;\n const sha = (\n await this.apiText(\n `/repos/${this.owner}/${this.repo}/commits/${ref}`,\n \"application/vnd.github.sha\"\n )\n ).trim();\n this.resolved = { ref, sha };\n return this.resolved;\n }\n\n private headers(accept: string): Record<string, string> {\n const headers: Record<string, string> = {\n Accept: accept,\n \"User-Agent\": \"saasaloy-cli\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n };\n // Never attach the token to a cleartext base. The only non-HTTPS base is a\n // SAASALOY_GITHUB_API override (the test fixture server), which needs no auth; a\n // typo'd `http://` override must not leak GITHUB_TOKEN on the wire.\n const auth = authToken();\n if (auth && githubApiBase().startsWith(\"https://\")) {\n headers.Authorization = `Bearer ${auth}`;\n }\n return headers;\n }\n\n private async api<T>(path: string): Promise<T> {\n return JSON.parse(\n await this.apiText(path, \"application/vnd.github+json\")\n ) as T;\n }\n\n private async apiText(path: string, accept: string): Promise<string> {\n let res: Response;\n try {\n res = await fetch(`${githubApiBase()}${path}`, {\n headers: this.headers(accept),\n signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),\n });\n } catch (error) {\n // A timeout arrives as a TimeoutError and everything else (DNS, TLS, refused\n // connection) as a TypeError; both read the same to a user, and both are worth\n // pointing at the offline path. The original is kept as `cause` so\n // SAASALOY_DEBUG can print it.\n const timedOut = error instanceof Error && error.name === \"TimeoutError\";\n throw new Error(\n `Could not reach GitHub for ${this.owner}/${this.repo} (${path})` +\n `${timedOut ? ` — no response in ${FETCH_TIMEOUT_MS / 1000}s` : \"\"}. ${OFFLINE_HINT}`,\n { cause: error }\n );\n }\n if (!res.ok) {\n if (\n res.status === 403 &&\n res.headers.get(\"x-ratelimit-remaining\") === \"0\"\n ) {\n throw new Error(\n `GitHub API rate limit hit for ${this.owner}/${this.repo}. Set GITHUB_TOKEN to raise it. ${OFFLINE_HINT}`\n );\n }\n if (res.status === 404) {\n throw new RefusalError(\n `Not found on GitHub: ${this.owner}/${this.repo} (${path}). ${OFFLINE_HINT}`\n );\n }\n throw new Error(\n `GitHub API error ${res.status} for ${this.owner}/${this.repo} (${path}). ${OFFLINE_HINT}`\n );\n }\n return res.text();\n }\n}\n","import type { Graph } from \"./resolve.js\";\nimport type { SaasaloyConfig } from \"./schema.js\";\n\n// `requiresOneOf` enforcement for `saasaloy add` — the deterministic core, kept out of\n// commands/add.ts so it can be unit-tested, mirroring conflicts.ts.\n//\n// `dependsOn` says \"install this too\". `conflictsWith` says \"refuse this beside me\".\n// Neither can say \"pick one of these\", which is what a capability split across mutually\n// exclusive drivers needs: `database` scaffolds `packages/db` and declares the `./client`\n// export, but the file behind that export ships in `database-d1` or `database-postgres`.\n// Installing the core alone leaves a project whose `@repo/db/client` import resolves to\n// nothing, and nothing said so at `add` time (#98, ADR 0026's amendment).\n//\n// So the descriptor gains `requiresOneOf`: a list of modules, exactly one of which has to\n// be present. \"Present\" means installed already, or arriving in this same resolved graph —\n// a driver pulled in by `auth`'s `dependsOn` satisfies the core's requirement without the\n// user naming it.\n//\n// The check reports; it never resolves. `add` refuses, or offers the list as a prompt on\n// an interactive terminal.\n\nexport interface MissingRequirement {\n /** The module whose descriptor declared `requiresOneOf`. */\n declaredBy: string;\n /** The candidates, exactly one of which has to be installed. Descriptor order. */\n options: string[];\n}\n\nexport interface DetectMissingRequirementsArgs {\n /** The resolved `dependsOn` graph for this run — descriptors included. */\n graph: Graph;\n config: SaasaloyConfig;\n}\n\n/**\n * Every module in the graph whose `requiresOneOf` list nothing satisfies, in topological\n * order. Empty means the run may proceed.\n */\nexport function detectMissingRequirements(\n args: DetectMissingRequirementsArgs\n): MissingRequirement[] {\n const { graph, config } = args;\n const installed = new Set(config.installed);\n const missing: MissingRequirement[] = [];\n\n // `graph.order` rather than `graph.modules`, so a prerequisite is reported before the\n // module that dragged it in — the order the user would install them in.\n for (const name of graph.order) {\n const options = graph.modules.get(name)?.item.requiresOneOf ?? [];\n // An empty list is \"no requirement\", not \"nothing can satisfy me\". An author who\n // writes `\"requiresOneOf\": []` gets a no-op rather than a module nobody can add.\n if (options.length === 0) {\n continue;\n }\n const satisfied = options.some(\n (option) => graph.modules.has(option) || installed.has(option)\n );\n if (!satisfied) {\n missing.push({ declaredBy: name, options: [...options] });\n }\n }\n\n return missing;\n}\n\n// One unmet requirement as a sentence. `requested` is what the user typed, so a\n// requirement raised by a transitive prerequisite says so rather than naming a module the\n// user never mentioned — same shape as conflicts.ts's `describe`.\nfunction describe(missing: MissingRequirement, requested: string): string {\n const { declaredBy, options } = missing;\n const subject =\n declaredBy === requested\n ? declaredBy\n : `${declaredBy} (required by ${requested})`;\n const first = options[0];\n const suggestion = first\n ? ` Run \\`saasaloy add ${first}\\` first, or pick another from that list.`\n : \"\";\n return `${subject} needs one of: ${options.join(\", \")}, and none is installed.${suggestion}`;\n}\n\n/** The refusal `add` prints. One line per unmet requirement, under a heading. */\nexport function formatMissingRequirements(\n missing: MissingRequirement[],\n requested: string\n): string {\n const heading = `Cannot add ${requested} — unmet requirement${missing.length > 1 ? \"s\" : \"\"}:`;\n return [heading, ...missing.map((m) => ` ${describe(m, requested)}`)].join(\n \"\\n\"\n );\n}\n","import type { LoadedModule, RegistrySource } from \"./registry.js\";\n\n// Recursive `dependsOn` resolution, topologically sorted so prerequisites are applied\n// before the modules that need them (build spec §2.7). A depth-first post-order walk\n// yields that order and catches dependency cycles along the way. Dependencies resolve\n// intra-repo: a bare `dependsOn` name is a sibling in the same registry source (ADR 0012;\n// cross-repo `owner/repo#module` is a follow-up, #26).\n\nexport interface Graph {\n /** Topological order: every module appears after the modules it dependsOn; requested is last. */\n order: string[];\n /** Every descriptor touched during resolution, keyed by name. */\n modules: Map<string, LoadedModule>;\n}\n\nexport async function resolveGraph(\n source: RegistrySource,\n requested: string\n): Promise<Graph> {\n const modules = new Map<string, LoadedModule>();\n const order: string[] = [];\n const done = new Set<string>();\n const onPath = new Set<string>(); // names currently on the DFS stack — a revisit is a cycle\n const stack: string[] = [];\n\n async function visit(name: string, requiredBy?: string): Promise<void> {\n if (done.has(name)) {\n return;\n }\n if (onPath.has(name)) {\n const cycle = [...stack.slice(stack.indexOf(name)), name].join(\" → \");\n throw new Error(`Dependency cycle detected: ${cycle}.`);\n }\n onPath.add(name);\n stack.push(name);\n const mod = await source.readModule(name, requiredBy);\n modules.set(name, mod);\n for (const dep of mod.item.dependsOn ?? []) {\n await visit(dep, name);\n }\n onPath.delete(name);\n stack.pop();\n done.add(name);\n order.push(name); // post-order: prerequisites land before this module\n }\n\n await visit(requested);\n return { modules, order };\n}\n\n/**\n * Fold a second resolution into the first, returning a new graph and mutating neither.\n *\n * `add` needs this when a `requiresOneOf` prompt picks a driver after the requested\n * module's graph is already resolved: the picked module is resolved on its own and merged\n * here, rather than re-resolving everything, because a remote source re-downloads every\n * descriptor it reads (`registry.ts`'s `readModule` extracts to a temp dir per call).\n *\n * The base wins on a name both graphs carry — it is the descriptor the run already\n * planned against. `extra.order` is a post-order walk, so appending the names the base\n * lacks, in that order, keeps every prerequisite ahead of the module that needs it.\n */\nexport function mergeGraph(base: Graph, extra: Graph): Graph {\n const modules = new Map(base.modules);\n const order = [...base.order];\n const seen = new Set(order);\n for (const name of extra.order) {\n if (seen.has(name)) {\n continue;\n }\n const mod = extra.modules.get(name);\n if (!mod) {\n continue;\n }\n seen.add(name);\n modules.set(name, mod);\n order.push(name);\n }\n return { modules, order };\n}\n","// Shared helpers for clack terminal output, used by the commands' `note` boxes.\n\n/**\n * Send a command's whole clack TUI to stderr, leaving stdout free for the one thing a\n * caller might pipe. `saasaloy update` writes its merge plan to stdout, so\n * `saasaloy update email | claude` has to work with no flag (issue #48, decision 9) —\n * every clack function takes this as its options object.\n */\nexport const TUI_ON_STDERR = { output: process.stderr } as const;\n\n// pnpm (and our own picocolors output) embed SGR codes; strip them when measuring\n// or when a block needs recoloring uniformly (embedded resets cancel a wrapper color).\n// The control character is deliberate — this pattern exists to match ANSI escapes,\n// which is exactly what `no-control-regex` flags. Suppressed at this line and at the\n// copy in scripts/update-deps.ts, and nowhere else.\n// oxlint-disable-next-line no-control-regex\nconst ANSI_PATTERN = /\\u001B\\[[0-9;]*m/g;\nexport function stripAnsi(text: string): string {\n return text.replace(ANSI_PATTERN, \"\");\n}\n\n// Can this session hold a prompt? Both streams have to be a terminal: clack needs\n// raw-mode stdin to read keys and a real stdout to draw its rail, so a pipe on either\n// side means nobody can answer. Read at call time — like `wrapForNote` reads `columns`\n// below — so the answer reflects the live process rather than module-load order.\n// `process.env.CI` is deliberately absent: every genuinely non-interactive context (CI\n// runners, `docker run` without -t, a pipe, `ssh host cmd`) already has no TTY, while a\n// developer with CI exported would silently lose every prompt in the CLI.\nexport function isInteractive(): boolean {\n return process.stdin.isTTY && process.stdout.isTTY;\n}\n\n// Hard-wrap text to the terminal width so a `note` box can't overflow the rail.\n// clack's box adds a border + padding (~6 cols), so we wrap a bit narrower. Widths\n// are measured on the ANSI-stripped text so colored words wrap by their visible\n// length. Words longer than the width (URLs, hashes) are split so nothing runs off\n// the edge — except words carrying ANSI codes, which a raw slice could cut mid-escape;\n// those are left whole on their own line.\nexport function wrapForNote(text: string): string {\n const width = Math.max(24, (process.stdout.columns ?? 80) - 6);\n const out: string[] = [];\n for (const line of text.split(\"\\n\")) {\n let current = \"\";\n for (const word of line.split(\" \")) {\n let chunk = word;\n // Break a single over-long word across lines.\n while (stripAnsi(chunk).length > width && !chunk.includes(\"\\u001B\")) {\n if (current) {\n out.push(current);\n current = \"\";\n }\n out.push(chunk.slice(0, width));\n chunk = chunk.slice(width);\n }\n const candidate = current ? `${current} ${chunk}` : chunk;\n if (stripAnsi(candidate).length > width) {\n out.push(current);\n current = chunk;\n } else {\n current = candidate;\n }\n }\n out.push(current);\n }\n return out.join(\"\\n\");\n}\n","// A module that ships UI writes a **block** into the ui package's blocks folder, exactly\n// like every block the base ships, and nothing puts it on a page. There is no descriptor\n// field for that: the signal is the target itself, `@ui/blocks/<name>.tsx`, and the\n// convention is the alias prefix (ADR 0030). `add` reads it to print the wire-up pointer,\n// `remove` reads it to say what deleting the file does not undo.\n//\n// The check runs against the *resolved* target, because that is what a plan carries by\n// the time either command reports on it — so resolve the folder back through the same\n// alias map the applier used rather than hardcoding one project layout.\n\nconst UI_ALIAS = \"@ui\";\nconst BLOCKS_DIR = \"blocks\";\n\n// What `saasaloy init` writes into a fresh project's saasaloy.json. A project that has\n// dropped the alias still gets a truthful answer for the layout the base ships.\nconst DEFAULT_UI_ROOT = \"packages/ui/src\";\n\n/** The project-relative POSIX prefix every ui block target starts with. */\nexport function uiBlocksPrefix(aliases: Record<string, string>): string {\n return `${aliases[UI_ALIAS] ?? DEFAULT_UI_ROOT}/${BLOCKS_DIR}/`;\n}\n\n/**\n * The subset of `files` that land in the ui package's blocks folder. Generic over the\n * caller's own file type so `add` keeps its `module` and `remove` keeps its action.\n */\nexport function uiBlockFiles<T extends { target: string }>(\n files: readonly T[],\n aliases: Record<string, string>\n): T[] {\n const prefix = uiBlocksPrefix(aliases);\n return files.filter((file) => file.target.startsWith(prefix));\n}\n","import pc from \"picocolors\";\n\n// One shape for `saasaloy <command> --help`. Before #98 the flag reached a command's\n// argument parser, which did not know it, so `saasaloy add --help` died with \"Unknown\n// argument(s): --help\" — the one message a person asking for help cannot use. Every\n// command now answers it the same way, and every command rejects a flag it does not\n// know rather than ignoring it.\n\nexport interface CommandHelp {\n /** The command's own name, e.g. `add`. */\n name: string;\n /** The one-line description, taken from the command registry. */\n describe: string;\n /** The usage line, without the leading `saasaloy`. */\n usage: string;\n /** Flag → what it does. Rendered in declaration order. */\n flags: Record<string, string>;\n}\n\n/** The flags every command accepts, listed after its own. */\nexport const COMMON_FLAGS: Record<string, string> = {\n \"-h, --help\": \"show this help\",\n};\n\nexport function printCommandHelp(help: CommandHelp): void {\n console.log(\n `${pc.bold(`saasaloy ${help.name}`)} ${pc.dim(`— ${help.describe}`)}\\n`\n );\n console.log(`${pc.bold(\"Usage:\")} ${help.usage}\\n`);\n console.log(pc.bold(\"Flags:\"));\n const flags = { ...help.flags, ...COMMON_FLAGS };\n const width = Math.max(...Object.keys(flags).map((flag) => flag.length));\n for (const [flag, describe] of Object.entries(flags)) {\n console.log(` ${pc.cyan(flag.padEnd(width))} ${pc.dim(describe)}`);\n }\n}\n\n/** True when the user asked for a command's help rather than for the command. */\nexport function wantsHelp(argv: string[]): boolean {\n return argv.includes(\"--help\") || argv.includes(\"-h\");\n}\n","import { readFile } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\n\n// The version comes off the package's own package.json rather than a build-time\n// constant, so a bug report names the build that is actually installed. At runtime\n// import.meta.url is <pkg>/dist/index.js and under vitest it is <pkg>/src/version.ts, so\n// `../package.json` resolves to <pkg>/package.json either way. That depth is load-bearing:\n// this file cannot move under `src/lib/` without breaking the source-mode path, because\n// tsup bundles the whole CLI into the single flat `dist/index.js`.\n//\n// It lives here rather than in `cli.ts` because `commands/add.ts` needs it for the\n// `requires.saasaloy` check (#50), and `cli.ts` already imports `commands/index.ts` —\n// reading it from there would close an import cycle.\n\n/** What this returns when the CLI's own package.json is missing, unreadable, or version-less. */\nexport const UNKNOWN_VERSION = \"unknown\";\n\n/** The running CLI's version, or `\"unknown\"` when its package.json will not parse. */\nexport async function readVersion(): Promise<string> {\n try {\n const file = fileURLToPath(new URL(\"../package.json\", import.meta.url));\n const parsed = JSON.parse(await readFile(file, \"utf-8\")) as {\n version?: unknown;\n };\n return typeof parsed.version === \"string\"\n ? parsed.version\n : UNKNOWN_VERSION;\n } catch {\n // A missing or unreadable package.json is not worth failing `--version` over.\n return UNKNOWN_VERSION;\n }\n}\n","// The one-line description of each command, in the lifecycle order a user walks\n// (scaffold → compose → configure → inspect → refresh → undo → browse), with the\n// author-facing `new` and `doctor`\n// last. It lives here rather than in\n// `index.ts` because each command's own `--help` prints its description too, and\n// importing the registry from a command module would close an import cycle\n// (`index.ts` → `add.ts` → `index.ts`). `index.ts` reads this map to build the\n// registry, so the order below is still the order help and the picker render.\n\nexport const DESCRIPTIONS = {\n init: \"scaffold a new Saasaloy project (base: Astro landing + ui + config)\",\n add: \"apply a module into the current project (resolves dependsOn)\",\n env: \"fill in the environment variables the installed modules declare (--check gates a deploy)\",\n outdated:\n \"report the base template and each installed module, current vs latest (--check gates CI)\",\n update:\n \"re-apply the base template and modules at a newer version, with a merge plan for anything you edited\",\n remove: \"undo a module's applied files via the manifest (offline)\",\n list: \"list the modules a registry offers, marking the ones installed here\",\n new: \"scaffold a new module in a registry repo (descriptor + files/ + skill stub)\",\n doctor:\n \"validate local module descriptors, or a project's saasaloy.json against its manifest\",\n} as const satisfies Record<string, string>;\n","import { cancel, intro, log, note, outro } from \"@clack/prompts\";\nimport { join, relative } from \"node:path\";\nimport pc from \"picocolors\";\nimport { readBaseDeclaration } from \"../lib/base.js\";\nimport {\n checkBase,\n checkPartialInstalls,\n checkProject,\n checkTarget,\n resolveDoctorTarget,\n} from \"../lib/doctor.js\";\nimport type { BaseReport, ModuleReport } from \"../lib/doctor.js\";\nimport {\n EXIT_OK,\n EXIT_REFUSED,\n exitCodeFor,\n formatFailure,\n} from \"../lib/exit.js\";\nimport { pathExists } from \"../lib/fs-utils.js\";\nimport { loadLock } from \"../lib/lock.js\";\nimport { loadManifest } from \"../lib/manifest.js\";\nimport { CONFIG_FILE, loadConfig } from \"../lib/saasaloy-config.js\";\nimport { baseTemplateDir } from \"../lib/scaffold.js\";\nimport { wrapForNote } from \"../lib/tui.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\n\n// `saasaloy doctor [path]` — validate module descriptors before they reach a stranger's\n// machine. It checks a local folder: one module (`doctor modules/waitlist`) or a whole\n// registry (`doctor modules`). Nothing named defaults to the current directory. Validating\n// a remote coordinate — the consumer asking \"is this module safe to add?\" — is a separate\n// command and a follow-up issue.\n//\n// A path carrying a `saasaloy.json` is checked as a project instead: the rules there read\n// `installed` against `.saasaloy/manifest.json` and report a module that owns no files\n// (#107), and a Base section says whether the template is recorded and which of its\n// files drifted (#120). Drift there is information, not a problem: it never fails the\n// run, and nothing here writes.\n//\n// The rules live in `lib/doctor.ts`; this file is presentation and exit codes only. An\n// invalid descriptor is bad input, so a run with findings exits 2 (the refusal code),\n// which is what makes `doctor` usable as a pre-publish gate in CI.\n\ninterface Options {\n path?: string;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\"--help\", \"-h\"]);\nconst USAGE = \"saasaloy doctor [<path>]\";\nconst HELP: CommandHelp = {\n name: \"doctor\",\n describe: DESCRIPTIONS.doctor,\n usage: USAGE,\n flags: {},\n};\n\n/** The directory checked when nothing is named — the current directory. */\nconst DEFAULT_PATH = \".\";\n\nexport function parseArgs(argv: string[]): Options {\n const positional: string[] = [];\n const unknown: string[] = [];\n for (const arg of argv) {\n if (!arg.startsWith(\"-\")) {\n positional.push(arg);\n } else if (!KNOWN_FLAGS.has(arg)) {\n unknown.push(arg);\n }\n }\n unknown.push(...positional.slice(1));\n return { path: positional[0], unknown };\n}\n\nfunction renderReport(report: ModuleReport, root: string): string {\n const lines = report.findings.map(\n (found) => ` ${pc.yellow(found.where)} ${found.message}`\n );\n return [pc.cyan(relative(root, report.dir) || report.module), ...lines].join(\n \"\\n\"\n );\n}\n\n/** The Base box: record or `untracked`, the recorded CLI, per-file drift, and the seed list. */\nfunction renderBase(report: BaseReport): string {\n if (report.status === \"untracked\" || !report.record) {\n return [\n `${pc.yellow(\"untracked\")} — no record of which template scaffolded this project.`,\n pc.dim(\n \"Run `saasaloy update` to record the base at this CLI; nothing is applied on that run.\"\n ),\n ].join(\"\\n\");\n }\n const lines = [\n `${pc.cyan(report.record.name)} ${pc.dim(`recorded at CLI ${report.record.cliVersion} (template ${report.record.templateHash.slice(0, 7)})`)}`,\n `${pc.green(\"✔\")} ${report.matching} file${report.matching === 1 ? \"\" : \"s\"} match the record`,\n ...report.drifted.map(\n (target) =>\n `${pc.yellow(\"✎\")} ${target} ${pc.dim(\"edited since it was recorded\")}`\n ),\n ...report.missing.map(\n (target) =>\n `${pc.red(\"✗\")} ${target} ${pc.dim(\"missing — `saasaloy update` restores it\")}`\n ),\n ];\n if (report.seed.length > 0) {\n lines.push(pc.dim(`seed, not checked: ${report.seed.join(\", \")}`));\n }\n return lines.join(\"\\n\");\n}\n\n/** The project side of `doctor`: what `saasaloy.json` and the manifest say about each other. */\nasync function reportProject(path: string): Promise<number> {\n const config = await loadConfig(path);\n const manifest = await loadManifest(path);\n const lock = await loadLock(path);\n const base = await checkBase({\n root: path,\n lock,\n manifest,\n seedFiles: (await readBaseDeclaration(await baseTemplateDir())).seedFiles,\n });\n note(wrapForNote(renderBase(base)), \"Base\");\n // Two halves of the same question: a module installed that owns nothing (#107), and a\n // module that owns work but was never marked installed (#49).\n const findings = [\n ...checkProject({ config, manifest }),\n ...checkPartialInstalls({ installed: config.installed, manifest }),\n ];\n if (findings.length === 0) {\n const count = config.installed.length;\n note(\n wrapForNote(\n config.installed.map((name) => `${pc.green(\"✔\")} ${name}`).join(\"\\n\") ||\n pc.dim(\"No modules installed.\")\n ),\n `Checked ${count} installed module${count === 1 ? \"\" : \"s\"}`\n );\n outro(pc.green(\"No problems found.\"));\n return EXIT_OK;\n }\n\n note(\n wrapForNote(\n findings\n .map((found) => `${pc.yellow(found.module)} ${found.message}`)\n .join(\"\\n\")\n ),\n `${pc.yellow(\"Project state\")} ${pc.dim(`(${findings.length})`)}`\n );\n cancel(\n `${findings.length} problem${findings.length === 1 ? \"\" : \"s\"} in ${CONFIG_FILE}.`\n );\n return EXIT_REFUSED;\n}\n\nexport async function runDoctor(argv: string[]): Promise<number> {\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n intro(pc.bgCyan(pc.black(\" saasaloy doctor \")));\n\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n\n const path = opts.path ?? DEFAULT_PATH;\n try {\n if (!(await pathExists(path))) {\n cancel(\n `No such path: ${path} — point \\`doctor\\` at a module folder or at a directory of them.`\n );\n return EXIT_REFUSED;\n }\n\n // A path carrying a `saasaloy.json` is a project, not a registry, so the project\n // rules run instead of the descriptor ones (#107). The default path is the current\n // directory, so `saasaloy doctor` picks project or registry mode by what's there.\n if (await pathExists(join(path, CONFIG_FILE))) {\n return await reportProject(path);\n }\n\n const target = await resolveDoctorTarget(path);\n if (target.names.length === 0) {\n cancel(\n `No module folders in ${path} — a module folder is one that carries a registry-item.json.`\n );\n return EXIT_REFUSED;\n }\n\n const reports = await checkTarget(target);\n const bad = reports.filter((report) => report.findings.length > 0);\n const total = bad.reduce((sum, report) => sum + report.findings.length, 0);\n\n if (bad.length === 0) {\n const checked = reports.map((report) => report.module);\n note(\n wrapForNote(\n checked.map((name) => `${pc.green(\"✔\")} ${name}`).join(\"\\n\")\n ),\n `Checked ${checked.length} module${checked.length === 1 ? \"\" : \"s\"}`\n );\n outro(pc.green(\"No problems found.\"));\n return EXIT_OK;\n }\n\n for (const report of bad) {\n note(\n wrapForNote(renderReport(report, target.registryDir)),\n `${pc.yellow(report.module)} ${pc.dim(`(${report.findings.length})`)}`\n );\n }\n log.info(\n pc.dim(\n `Checked ${reports.length} module${reports.length === 1 ? \"\" : \"s\"}; ${reports.length - bad.length} clean.`\n )\n );\n cancel(\n `${total} problem${total === 1 ? \"\" : \"s\"} in ${bad.length} module${bad.length === 1 ? \"\" : \"s\"}.`\n );\n return EXIT_REFUSED;\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { mkdtemp, readdir, readFile, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { basename, join, posix } from \"node:path\";\nimport { hashContent, readIfPresent, resolveWithinRoot } from \"./fs-utils.js\";\nimport type { LockBase, Lockfile } from \"./lock.js\";\nimport { saveLock } from \"./lock.js\";\nimport type { ManagedEntry, Manifest } from \"./manifest.js\";\nimport { saveManifest } from \"./manifest.js\";\nimport {\n BASE_DECLARATION,\n baseTemplateDir,\n copyTemplate,\n renderedName,\n templateVars,\n} from \"./scaffold.js\";\nimport type { WrittenFile } from \"./scaffold.js\";\nimport type { ModuleComparison, ModuleUpdateInput } from \"./updater.js\";\n\nexport { BASE_DECLARATION } from \"./scaffold.js\";\n\n// The base template's provenance (#120, ADR 0032). `saasaloy init` copies the template\n// bundled inside the CLI package, and until this file nothing recorded what it copied:\n// no manifest entry, no lock entry, no version stamp (ADR 0022's \"one-time gift\"). So\n// `outdated`, `update` and `doctor` could say nothing about the largest body of code the\n// tool generates. The base now records like a module — one `managed` entry per rendered\n// file under the reserved module name `base` — plus a lock record keyed by CLI version\n// and template hash, because the base ships with the CLI rather than in a registry.\n//\n// Three rules keep the record honest:\n// - hashes are of *rendered* bytes: `{{PROJECT_NAME}}` is substituted at copy time, and\n// hashing the template source would mark every substituted file drifted forever;\n// - `templateHash` is over the template *as shipped*, so two CLI builds carrying an\n// identical template compare equal and a republished template is still detected;\n// - seed files (`_saasaloy-base.json`'s `seedFiles`) are recorded but never updated —\n// the owner is meant to rewrite them.\n\n/** The reserved module name base files record under. Never in `saasaloy.json`'s `installed`. */\nexport const BASE_MODULE = \"base\";\n\n/** `saasaloy.json`'s `base` value — the one base app the template ships. */\nexport const DEFAULT_BASE_NAME = \"web\";\n\n/** The lock's record of the base — re-exported under the name the plan uses. */\nexport type BaseRecord = LockBase;\n\n/** Why the base's merge plan is two-way, stamped on its section header. */\nexport const BASE_NO_MERGE_BASE =\n \"the template ships with the CLI; the previous version isn't on disk\";\n\n/** The one intent line a drifted base file carries, for the agent reading the plan. */\nexport const BASE_INTENT =\n \"the base template changed; keep local edits, take the upstream change\";\n\nexport interface BaseDeclaration {\n /** Project-relative POSIX paths the owner is meant to rewrite; recorded, never updated. */\n seedFiles: string[];\n}\n\n/** One file the template ships, before and after the `_` → `.` rename. */\nexport interface TemplateFile {\n /** Template-relative POSIX source path — the manifest's `from`. */\n from: string;\n /** Project-relative POSIX path once rendered — the manifest key. */\n target: string;\n /** Absolute path of the source file inside the template. */\n abs: string;\n}\n\n/**\n * Read `_saasaloy-base.json` off a template. A template with none has no seed files. The\n * declaration ships inside this package, so a malformed one is a build error worth a\n * plain throw rather than a refusal aimed at the user.\n */\nexport async function readBaseDeclaration(\n templateDir: string\n): Promise<BaseDeclaration> {\n const raw = await readIfPresent(join(templateDir, BASE_DECLARATION));\n if (raw === undefined) {\n return { seedFiles: [] };\n }\n const parsed = JSON.parse(raw) as { seedFiles?: unknown };\n const seedFiles = parsed.seedFiles;\n if (\n !Array.isArray(seedFiles) ||\n !seedFiles.every((entry) => typeof entry === \"string\" && entry !== \"\")\n ) {\n throw new Error(\n `${BASE_DECLARATION} must declare \"seedFiles\" as a list of project-relative paths.`\n );\n }\n return { seedFiles: seedFiles as string[] };\n}\n\n/** Every file the template ships, sorted by target, the declaration excluded. */\nexport async function listTemplateFiles(\n templateDir: string\n): Promise<TemplateFile[]> {\n const out: TemplateFile[] = [];\n const walk = async (\n dir: string,\n fromPrefix: string,\n targetPrefix: string,\n isRoot: boolean\n ): Promise<void> => {\n for (const entry of await readdir(dir, { withFileTypes: true })) {\n if (isRoot && entry.name === BASE_DECLARATION) {\n continue;\n }\n const from = posix.join(fromPrefix, entry.name);\n const target = posix.join(targetPrefix, renderedName(entry.name));\n const abs = join(dir, entry.name);\n if (entry.isDirectory()) {\n await walk(abs, from, target, false);\n } else if (entry.isFile()) {\n out.push({ from, target, abs });\n }\n }\n };\n await walk(templateDir, \"\", \"\", true);\n return out.toSorted((a, b) => (a.target < b.target ? -1 : 1));\n}\n\n// Computed on demand and cached per process: the template is about fifty files and the\n// walk is milliseconds, and a runtime hash cannot drift from the files the way a\n// build-time constant could.\nconst hashCache = new Map<string, Promise<string>>();\n\n/**\n * sha256 over the sorted `(target, sha256 of source bytes)` pairs of the template as\n * shipped. Source bytes, not rendered: this identifies the template, and a project name\n * must not change it.\n */\nexport function templateHash(templateDir: string): Promise<string> {\n let cached = hashCache.get(templateDir);\n if (!cached) {\n cached = (async () => {\n const digest = createHash(\"sha256\");\n for (const file of await listTemplateFiles(templateDir)) {\n digest.update(file.target);\n digest.update(\"\\0\");\n digest.update(hashContent(await readFile(file.abs, \"utf-8\")));\n digest.update(\"\\n\");\n }\n return digest.digest(\"hex\");\n })();\n hashCache.set(templateDir, cached);\n }\n return cached;\n}\n\n/** The lock's `base` record for a template at `templateHash`, rendered by `cliVersion`. */\nexport function baseRecord(\n cliVersion: string,\n hash: string,\n name = DEFAULT_BASE_NAME\n): BaseRecord {\n return { name, cliVersion, templateHash: hash };\n}\n\n/** The manifest entries the base owns, keyed by target. */\nexport function baseEntries(manifest: Manifest): Record<string, ManagedEntry> {\n const out: Record<string, ManagedEntry> = {};\n for (const [target, entry] of Object.entries(manifest.managed)) {\n if (entry.module === BASE_MODULE) {\n out[target] = entry;\n }\n }\n return out;\n}\n\n/** What `recordBaseFiles` needs of a file: the same three fields `copyTemplate` returns. */\nexport type BaseFileRecord = Pick<WrittenFile, \"target\" | \"from\" | \"hash\">;\n\n/**\n * Replace the base's manifest entries with `files`. Entries other modules own are left\n * alone; a base entry `files` no longer names is dropped, so the record is always the\n * template that was last rendered or adopted.\n */\nexport function recordBaseFiles(\n manifest: Manifest,\n files: readonly BaseFileRecord[]\n): void {\n for (const [target, entry] of Object.entries(manifest.managed)) {\n if (entry.module === BASE_MODULE) {\n delete manifest.managed[target];\n }\n }\n for (const file of files) {\n manifest.managed[file.target] = {\n module: BASE_MODULE,\n hash: file.hash,\n from: file.from,\n };\n }\n}\n\n/**\n * Whether the project carries a usable base record: the lock's `base` *and* at least one\n * manifest entry for it. A lock record with no manifest entries has nothing to classify\n * against, so it counts as untracked and `update` re-adopts.\n */\nexport function isBaseTracked(lock: Lockfile, manifest: Manifest): boolean {\n return (\n lock.base !== undefined && Object.keys(baseEntries(manifest)).length > 0\n );\n}\n\n/**\n * Base targets the manifest tracks that are not on disk — the files `update` classifies as\n * `restore`. `compareBase` needs this because `templateHash` only says whether the *template*\n * moved: a file deleted from disk (or recorded absent by `adoptBase`) leaves the hash equal,\n * so without this the base reads `current` and the restore never runs (#120).\n *\n * Seed files are excluded. They are recorded but never updated, so a missing one produces no\n * plan action and would leave the base permanently `outdated` with nothing to apply.\n */\nexport async function missingBaseTargets(\n root: string,\n manifest: Manifest,\n templateDir: string\n): Promise<string[]> {\n const seed = new Set((await readBaseDeclaration(templateDir)).seedFiles);\n const missing: string[] = [];\n for (const target of Object.keys(baseEntries(manifest))) {\n if (seed.has(target)) {\n continue;\n }\n if ((await readIfPresent(resolveWithinRoot(root, target))) === undefined) {\n missing.push(target);\n }\n }\n return missing.toSorted((a, b) => (a < b ? -1 : 1));\n}\n\n/** The template rendered with a project's own vars, in a temp dir the caller removes. */\nexport interface RenderedTemplate {\n dir: string;\n files: WrittenFile[];\n cleanup: () => Promise<void>;\n}\n\n/**\n * Render the bundled template for `root` into a temp dir, source names kept, so each\n * file's `from` resolves inside the render and its `hash` is of the bytes `init` would\n * have written for this project.\n */\nexport async function renderTemplate(\n root: string,\n templateDir: string\n): Promise<RenderedTemplate> {\n const dir = await mkdtemp(join(tmpdir(), \"saasaloy-base-render-\"));\n const files = await copyTemplate(\n templateDir,\n dir,\n templateVars(basename(root)),\n { keepNames: true }\n );\n return {\n dir,\n files,\n cleanup: () => rm(dir, { recursive: true, force: true }),\n };\n}\n\nexport interface AdoptBaseArgs {\n root: string;\n /** The running CLI's version — the record says the base was adopted here. */\n cliVersion: string;\n /** Loaded state, mutated in place and saved unless `dryRun`. */\n manifest: Manifest;\n lock: Lockfile;\n /** Report only: neither state file is written. */\n dryRun?: boolean;\n /** Defaults to the bundled template; tests point it at a fixture. */\n templateDir?: string;\n}\n\nexport interface AdoptBaseResult {\n /** Targets recorded at the hash they have on disk. */\n adopted: string[];\n /** Template files not on disk, recorded at their rendered hash so the next update restores them. */\n absent: string[];\n record: BaseRecord;\n}\n\n/**\n * Give an unrecorded project a base record at the running CLI (#120). Every file today's\n * template ships is recorded at the hash it has on disk — edits and all — so the next\n * `update` classifies against what is really there and never manufactures a wall of\n * conflicts on a customised project. A file the template no longer ships is not recorded;\n * a template file missing from disk is recorded at its rendered hash, which `update`\n * reads as \"tracked but missing\" and restores. Adoption records the present, not the\n * past: it skips at most one generation of base changes.\n */\nexport async function adoptBase(args: AdoptBaseArgs): Promise<AdoptBaseResult> {\n const { root, cliVersion, manifest, lock, dryRun = false } = args;\n const templateDir = args.templateDir ?? (await baseTemplateDir());\n const render = await renderTemplate(root, templateDir);\n try {\n const files: BaseFileRecord[] = [];\n const adopted: string[] = [];\n const absent: string[] = [];\n for (const file of render.files) {\n const mine = await readIfPresent(resolveWithinRoot(root, file.target));\n if (mine === undefined) {\n absent.push(file.target);\n files.push({ target: file.target, from: file.from, hash: file.hash });\n } else {\n adopted.push(file.target);\n files.push({\n target: file.target,\n from: file.from,\n hash: hashContent(mine),\n });\n }\n }\n const record = baseRecord(\n cliVersion,\n await templateHash(templateDir),\n lock.base?.name ?? DEFAULT_BASE_NAME\n );\n if (!dryRun) {\n recordBaseFiles(manifest, files);\n lock.base = record;\n await saveManifest(root, manifest);\n await saveLock(root, lock);\n }\n return { adopted, absent, record };\n } finally {\n await render.cleanup();\n }\n}\n\nexport interface BaseUpdateArgs {\n root: string;\n manifest: Manifest;\n lock: Lockfile;\n /** The template the running CLI ships — `baseTemplateDir()`, or a fixture. */\n templateDir: string;\n /** The running CLI's version, written into the record a clean run moves to. */\n cliVersion: string;\n /** The `outdated` row from `compareBase`, carried into the plan for the summary. */\n comparison: ModuleComparison;\n}\n\nexport interface BaseUpdateHandle {\n input: ModuleUpdateInput;\n /** Remove the rendered template; call once the plan has been built and executed. */\n cleanup: () => Promise<void>;\n}\n\n/**\n * The base as a `ModuleUpdateInput` the existing classifier can run (#120). The bundled\n * template is rendered for this project into a temp dir and described as a one-scaffold\n * descriptor rooted at the project, so `listModuleFiles` places each file where `init`\n * put it. There is no `base` revision — the old template lives inside the old CLI, which\n * is not on disk — so the plan takes the `noMergeBase` path modules already have. Seed\n * files are left out of the descriptor and named in `ignoreTargets`, and every recorded\n * patch against a shipped file is queued for re-apply.\n *\n * No `dependencies` are declared: the root `package.json` is itself a managed base file,\n * so a pin bump rides its overwrite or its merge plan rather than a second write path.\n */\nexport async function baseUpdateInput(\n args: BaseUpdateArgs\n): Promise<BaseUpdateHandle> {\n const { root, manifest, lock, templateDir, cliVersion, comparison } = args;\n const render = await renderTemplate(root, templateDir);\n const seed = new Set((await readBaseDeclaration(templateDir)).seedFiles);\n const shipped = render.files.filter((file) => !seed.has(file.target));\n const targets = new Set(shipped.map((file) => file.target));\n return {\n input: {\n comparison,\n theirs: {\n dir: render.dir,\n item: {\n name: BASE_MODULE,\n type: \"saasaloy:capability\",\n scaffolds: [\n {\n workspace: \".\",\n files: shipped.map((file) => ({\n path: file.from,\n target: file.target,\n })),\n },\n ],\n },\n },\n noMergeBase: BASE_NO_MERGE_BASE,\n intent: [],\n ignoreTargets: seed,\n reapplyPatches: manifest.patches.filter((patch) =>\n targets.has(patch.file)\n ),\n baseRecord: baseRecord(\n cliVersion,\n await templateHash(templateDir),\n lock.base?.name ?? DEFAULT_BASE_NAME\n ),\n },\n cleanup: render.cleanup,\n };\n}\n","import { mkdir, readdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join, posix } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport cliPackage from \"../../package.json\" with { type: \"json\" };\nimport { hashContent, pathExists } from \"./fs-utils.js\";\n\n// Copy a template tree into a target dir, applying two conventions:\n// - files named `_foo` become `.foo` (npm refuses to publish literal dotfiles\n// like `.gitignore` inside a package, so templates store them de-dotted)\n// - `{{VAR}}` tokens in file contents are replaced from `vars`\n// All template files are UTF-8 text, so every file gets token substitution.\n\nexport type TemplateVars = Record<string, string>;\n\n/**\n * The base template's own declaration (#120): which of its files are seed — meant to be\n * rewritten by the owner, so never updated — as an explicit path list. It describes the\n * template and is not part of it, so `copyTemplate` skips it by this exact name, before\n * the `_` → `.` rename would turn it into a dotfile in someone's project.\n */\nexport const BASE_DECLARATION = \"_saasaloy-base.json\";\n\n// The base template is bundled at <pkg>/templates/base. At runtime import.meta.url is\n// <pkg>/dist/index.js, so `../templates` resolves; under vitest it is <pkg>/src/lib/\n// scaffold.ts, so the template sits one level further up. Try both, the way\n// `lib/schema.ts` finds its schemas — without it `init` is only reachable from a build,\n// which is what kept it untested (#47).\nconst TEMPLATE_DIR_CANDIDATES = [\"../templates/base\", \"../../templates/base\"];\n\n/** Absolute path of the bundled base template. `init` copies it; `doctor` reads its aliases. */\nexport async function baseTemplateDir(): Promise<string> {\n for (const candidate of TEMPLATE_DIR_CANDIDATES) {\n const dir = fileURLToPath(new URL(candidate, import.meta.url));\n if (await pathExists(join(dir, \"package.json\"))) {\n return dir;\n }\n }\n // Fall back to the packaged location, for a sensible ENOENT naming the real path.\n return fileURLToPath(new URL(TEMPLATE_DIR_CANDIDATES[0]!, import.meta.url));\n}\n\n// The substitutions the base template expects. CLI_VERSION stamps the DESIGN.md\n// seed, so a generated contract records which CLI wrote it.\nexport function templateVars(projectName: string): TemplateVars {\n return { PROJECT_NAME: projectName, CLI_VERSION: cliPackage.version };\n}\n\n/** The `_name` → `.name` rename, applied to one path segment. */\nexport function renderedName(name: string): string {\n return name.startsWith(\"_\") ? `.${name.slice(1)}` : name;\n}\n\n/** One file `copyTemplate` wrote, described the way the manifest records it (#120). */\nexport interface WrittenFile {\n /** Absolute path of the file as written. */\n path: string;\n /** Project-relative POSIX path after the rename — the manifest key. */\n target: string;\n /** Template-relative POSIX path before the rename — the manifest's `from`. */\n from: string;\n /** sha256 of the rendered bytes, so hashing the template source is never mistaken for it. */\n hash: string;\n}\n\nexport interface CopyTemplateOptions {\n /**\n * Write under the source names (`_gitignore` stays `_gitignore`). `update` renders the\n * template this way so each file's `from` resolves inside the render, while `target`\n * still says where it lands in the project.\n */\n keepNames?: boolean;\n}\n\nexport async function copyTemplate(\n srcDir: string,\n destDir: string,\n vars: TemplateVars,\n options: CopyTemplateOptions = {}\n): Promise<WrittenFile[]> {\n return copyTree(srcDir, destDir, vars, options, \"\", \"\", true);\n}\n\nasync function copyTree(\n srcDir: string,\n destDir: string,\n vars: TemplateVars,\n options: CopyTemplateOptions,\n fromPrefix: string,\n targetPrefix: string,\n isRoot: boolean\n): Promise<WrittenFile[]> {\n const written: WrittenFile[] = [];\n await mkdir(destDir, { recursive: true });\n const entries = await readdir(srcDir, { withFileTypes: true });\n for (const entry of entries) {\n // The declaration describes the template; it is never part of a project.\n if (isRoot && entry.name === BASE_DECLARATION) {\n continue;\n }\n const srcPath = join(srcDir, entry.name);\n const outName = renderedName(entry.name);\n const destPath = join(destDir, options.keepNames ? entry.name : outName);\n const from = posix.join(fromPrefix, entry.name);\n const target = posix.join(targetPrefix, outName);\n if (entry.isDirectory()) {\n written.push(\n ...(await copyTree(\n srcPath,\n destPath,\n vars,\n options,\n from,\n target,\n false\n ))\n );\n } else if (entry.isFile()) {\n const raw = await readFile(srcPath, \"utf-8\");\n const rendered = applyVars(raw, vars);\n await writeFile(destPath, rendered, \"utf-8\");\n written.push({\n path: destPath,\n target,\n from,\n hash: hashContent(rendered),\n });\n }\n }\n return written;\n}\n\nfunction applyVars(content: string, vars: TemplateVars): string {\n return content.replaceAll(\n /\\{\\{(\\w+)\\}\\}/g,\n (match, key: string) => vars[key] ?? match\n );\n}\n","{\n \"name\": \"saasaloy\",\n \"version\": \"0.1.2\",\n \"description\": \"Composable SaaS accelerator kit.\",\n \"keywords\": [\n \"saas\",\n \"scaffold\",\n \"starter\",\n \"cli\",\n \"astro\",\n \"cloudflare-workers\",\n \"drizzle\",\n \"generator\"\n ],\n \"homepage\": \"https://github.com/mimukit/saasaloy#readme\",\n \"bugs\": {\n \"url\": \"https://github.com/mimukit/saasaloy/issues\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/mimukit/saasaloy.git\",\n \"directory\": \"packages/cli\"\n },\n \"license\": \"MIT\",\n \"type\": \"module\",\n \"bin\": {\n \"saasaloy\": \"./dist/index.js\"\n },\n \"files\": [\n \"dist\",\n \"templates\",\n \"schemas/*.schema.json\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"engines\": {\n \"node\": \">=24.13.0\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"prepack\": \"pnpm -w run readme:cli && pnpm run build\",\n \"dev\": \"tsup --watch\",\n \"start\": \"node ./dist/index.js\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run --coverage\",\n \"test:e2e\": \"vitest run --config vitest.integration.config.ts test/e2e\",\n \"test:matrix\": \"vitest run --config vitest.integration.config.ts test/matrix\",\n \"release\": \"release-it\",\n \"release:dry\": \"release-it --dry-run\"\n },\n \"devDependencies\": {\n \"@release-it/conventional-changelog\": \"12.0.0\",\n \"@types/node\": \"26.1.1\",\n \"@vitest/coverage-v8\": \"4.1.10\",\n \"release-it\": \"21.0.2\",\n \"tsup\": \"8.5.1\",\n \"vitest\": \"4.1.10\"\n },\n \"dependencies\": {\n \"@clack/prompts\": \"1.7.0\",\n \"ajv\": \"8.17.1\",\n \"diff\": \"9.0.0\",\n \"giget\": \"3.3.0\",\n \"jsonc-parser\": \"3.3.1\",\n \"magicast\": \"0.5.3\",\n \"picocolors\": \"1.1.1\"\n }\n}\n","import { readFile } from \"node:fs/promises\";\nimport { basename, dirname, join, posix, resolve } from \"node:path\";\nimport { BASE_MODULE, baseEntries, isBaseTracked } from \"./base.js\";\nimport {\n hashContent,\n joinModulePath,\n pathExists,\n readDirNames,\n readIfPresent,\n resolveWithinRoot,\n} from \"./fs-utils.js\";\nimport type { LockBase, Lockfile } from \"./lock.js\";\nimport type { Manifest } from \"./manifest.js\";\nimport { loadConfig } from \"./saasaloy-config.js\";\nimport { baseTemplateDir } from \"./scaffold.js\";\nimport { isValidRange } from \"./semver.js\";\nimport { validateRegistryItem } from \"./schema.js\";\nimport type { SaasaloyConfig } from \"./schema.js\";\n\n// The checks behind `saasaloy doctor`. Split from the command so the rules are testable\n// without a terminal, and so a future `doctor owner/repo/name` (the consumer path, a\n// follow-up) can reuse them against a downloaded folder.\n//\n// Everything here is local: a module folder, its siblings in the same registry, and the\n// alias map the base template establishes. Nothing is fetched, so an author can run it\n// before publishing rather than finding out on a stranger's machine.\n//\n// `checkProject` at the foot of the file reads the other side — a consumer project's own\n// state files — and is the one check here that says nothing about a descriptor.\n\nexport interface Finding {\n /** The module folder the finding belongs to. */\n module: string;\n /**\n * Where inside the descriptor, in the shape ajv prints (`/files/0/target`). A project\n * check points into the project's own state instead, e.g. `/installed/waitlist`.\n */\n where: string;\n message: string;\n}\n\nexport interface ModuleReport {\n module: string;\n /** Absolute path of the module folder. */\n dir: string;\n findings: Finding[];\n}\n\n/** The aliases a descriptor's `files[].target` may name. */\nexport interface AliasSources {\n /** `@web`, `@ui` — established by `saasaloy init`, before any module applies. */\n base: Record<string, string>;\n /** Every alias a scaffold in this registry registers, e.g. `@api` from the api module. */\n fromScaffolds: Record<string, string>;\n}\n\n// Every skill folder a module ships is prefixed, so two registries' skills can sit in one\n// `.claude/skills/` without colliding (ADR 0014).\nconst SKILL_PREFIX = \"saasaloy-\";\n\n// `dependencies` and `devDependencies` are exact-pinned `name@version` (ADR 0017). The\n// schema enforces the same shape; this repeats it so the report says what is wrong with\n// the entry rather than only that a pattern did not match.\nconst PINNED_DEP = /^(@[^/@]+\\/)?[^/@]+@\\d+\\.\\d+\\.\\d+(-[\\w.-]+)?(\\+[\\w.-]+)?$/;\n\nfunction finding(module: string, where: string, message: string): Finding {\n return { message, module, where };\n}\n\n// The descriptor is parsed before the schema has vouched for it — that is the point of\n// doctor — so every field read below goes through one of these instead of a cast the\n// JSON can violate. A wrong-shaped field reads as absent here and the schema pass names\n// it; iterating `\"scaffolds\": {}` directly would throw and swallow the whole report.\nfunction asArray(value: unknown): unknown[] {\n return Array.isArray(value) ? value : [];\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\n/**\n * Names of the module folders under `dir` — a folder counts when it carries a descriptor,\n * the same rule `LocalRegistrySource.listModules` uses.\n */\nexport async function registryModuleNames(dir: string): Promise<string[]> {\n const names: string[] = [];\n for (const name of await readDirNames(dir)) {\n if (await pathExists(join(dir, name, \"registry-item.json\"))) {\n names.push(name);\n }\n }\n return names.toSorted();\n}\n\n/**\n * The alias map a descriptor in `registryDir` may target. The base template's own\n * `saasaloy.json` supplies the aliases every project starts with; each capability's\n * `scaffolds[].aliases` supplies the ones a project gains by installing it.\n */\nexport async function collectAliases(\n registryDir: string,\n names: string[]\n): Promise<AliasSources> {\n const templateDir = await baseTemplateDir();\n let base: Record<string, string> = {};\n if (await pathExists(join(templateDir, \"saasaloy.json\"))) {\n base = (await loadConfig(templateDir)).aliases;\n }\n\n const fromScaffolds: Record<string, string> = {};\n for (const name of names) {\n const item = asRecord(await readDescriptor(join(registryDir, name)));\n for (const scaffold of asArray(item.scaffolds)) {\n const aliases = asRecord(asRecord(scaffold).aliases);\n for (const [alias, prefix] of Object.entries(aliases)) {\n if (typeof prefix === \"string\") {\n fromScaffolds[alias] = prefix;\n }\n }\n }\n }\n return { base, fromScaffolds };\n}\n\n/** Parse a module folder's descriptor, or `undefined` when it is missing or unreadable. */\nasync function readDescriptor(dir: string): Promise<unknown> {\n const file = join(dir, \"registry-item.json\");\n if (!(await pathExists(file))) {\n return undefined;\n }\n try {\n return JSON.parse(await readFile(file, \"utf-8\")) as unknown;\n } catch {\n return undefined;\n }\n}\n\nexport interface CheckModuleInput {\n /** Absolute path of the module folder. */\n dir: string;\n /** Every module name the surrounding registry offers, for `dependsOn` resolution. */\n siblings: string[];\n aliases: AliasSources;\n}\n\n/**\n * Every violation one module folder carries. Schema errors and structural errors are\n * reported together rather than the schema pass short-circuiting: an author fixing a\n * descriptor wants the whole list, not the first line of it.\n */\nexport async function checkModule(\n input: CheckModuleInput\n): Promise<ModuleReport> {\n const { aliases, dir, siblings } = input;\n const module = basename(dir);\n const findings: Finding[] = [];\n const report = (): ModuleReport => ({ dir, findings, module });\n\n const file = join(dir, \"registry-item.json\");\n if (!(await pathExists(file))) {\n findings.push(\n finding(module, \"(root)\", `no registry-item.json in ${module}/`)\n );\n return report();\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(await readFile(file, \"utf-8\")) as unknown;\n } catch (error) {\n findings.push(\n finding(\n module,\n \"(root)\",\n `registry-item.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n return report();\n }\n\n const schema = await validateRegistryItem(parsed);\n const schemaFindings = schema.errors.map((error) => {\n const [where, ...rest] = error.split(\": \");\n return finding(module, where ?? \"(root)\", rest.join(\": \"));\n });\n\n // The structural checks below read the parsed object even when the schema rejected it.\n // A descriptor usually fails on one property, and reporting only that hides the\n // missing file or the unknown alias the author would fix in the same pass.\n const item = asRecord(parsed);\n\n if (typeof item.name === \"string\" && item.name !== module) {\n findings.push(\n finding(\n module,\n \"/name\",\n `declares name \"${item.name}\" — the folder and the descriptor name must match`\n )\n );\n }\n\n const known = { ...aliases.base, ...aliases.fromScaffolds };\n const knownList = Object.keys(known).toSorted().join(\", \") || \"(none)\";\n for (const [index, entry] of asArray(item.files).entries()) {\n await checkSourcePath(findings, module, dir, `/files/${index}/path`, entry);\n const target = asRecord(entry).target;\n const alias = typeof target === \"string\" ? target.split(\"/\")[0] : undefined;\n if (alias && !(alias in known)) {\n findings.push(\n finding(\n module,\n `/files/${index}/target`,\n `unknown alias \"${alias}\" in \"${String(target)}\" — known aliases: ${knownList}`\n )\n );\n }\n }\n\n for (const [index, scaffold] of asArray(item.scaffolds).entries()) {\n for (const [fileIndex, entry] of asArray(\n asRecord(scaffold).files\n ).entries()) {\n await checkSourcePath(\n findings,\n module,\n dir,\n `/scaffolds/${index}/files/${fileIndex}/path`,\n entry\n );\n }\n }\n\n const siblingSet = new Set(siblings);\n for (const [field, names] of [\n [\"dependsOn\", item.dependsOn],\n [\"requiresOneOf\", item.requiresOneOf],\n [\"conflictsWith\", item.conflictsWith],\n ] as const) {\n for (const [index, name] of asArray(names).entries()) {\n if (typeof name === \"string\" && !siblingSet.has(name)) {\n findings.push(\n finding(\n module,\n `/${field}/${index}`,\n `names \"${name}\", which this registry does not offer`\n )\n );\n }\n }\n }\n\n for (const [field, deps] of [\n [\"dependencies\", item.dependencies],\n [\"devDependencies\", item.devDependencies],\n ] as const) {\n for (const [index, dep] of asArray(deps).entries()) {\n if (typeof dep === \"string\" && !PINNED_DEP.test(dep)) {\n findings.push(\n finding(\n module,\n `/${field}/${index}`,\n `\"${dep}\" is not exact-pinned — write it as name@1.2.3 (ADR 0017; \\`pnpm deps:update\\` fills the version)`\n )\n );\n }\n }\n }\n\n // `requires.saasaloy` is checked structurally only: is it a string, and is it a range\n // this CLI can evaluate (#50). It is deliberately *not* compared against the running\n // version — doctor is the author's tool, and a registry author's CLI is not the\n // consumer's. An author on 0.9 writing `>=2` for a field landing next quarter is right,\n // and a doctor that failed them for it would teach them to stop declaring the field.\n const requires = asRecord(item.requires).saasaloy;\n if (requires !== undefined) {\n if (typeof requires !== \"string\") {\n findings.push(\n finding(\n module,\n \"/requires/saasaloy\",\n `must be a semver range string, not ${Array.isArray(requires) ? \"an array\" : typeof requires}`\n )\n );\n } else if (!isValidRange(requires)) {\n findings.push(\n finding(\n module,\n \"/requires/saasaloy\",\n `\"${requires}\" isn't a semver range — write one like \">=0.3\", \">=0.3 <2\", \"^1.2.0\" or \"1.x\"`\n )\n );\n }\n }\n\n // Every `devVars` key is a local value for an env var the module declares. A key with\n // no `envVars` entry is written to nothing, which the schema documents and cannot check.\n for (const key of Object.keys(asRecord(item.devVars))) {\n if (!(key in asRecord(item.envVars))) {\n findings.push(\n finding(\n module,\n `/devVars/${key}`,\n `has no matching entry in envVars, so nothing describes it`\n )\n );\n }\n }\n\n for (const [index, skill] of asArray(asRecord(item.agent).skills).entries()) {\n if (typeof skill !== \"string\") {\n continue;\n }\n const folder = posix.basename(skill);\n if (!folder.startsWith(SKILL_PREFIX)) {\n findings.push(\n finding(\n module,\n `/agent/skills/${index}`,\n `skill folder \"${folder}\" must start with \"${SKILL_PREFIX}\" (ADR 0014)`\n )\n );\n }\n const missing = await missingModulePath(dir, skill);\n if (missing) {\n findings.push(\n finding(\n module,\n `/agent/skills/${index}`,\n missing === \"escape\"\n ? `\"${skill}\" escapes the module folder`\n : `no such folder: ${skill}`\n )\n );\n }\n }\n\n // Schema errors come first, minus any the checks above already explained better at the\n // same path. An unpinned dependency fails a schema pattern *and* a rule here; printing\n // both leaves the author reading a raw regex next to the sentence that answers it.\n const explained = new Set(findings.map((found) => found.where));\n findings.unshift(\n ...schemaFindings.filter((found) => !explained.has(found.where))\n );\n\n return report();\n}\n\nasync function checkSourcePath(\n findings: Finding[],\n module: string,\n dir: string,\n where: string,\n entry: unknown\n): Promise<void> {\n const path = asRecord(entry).path;\n if (typeof path !== \"string\" || path === \"\") {\n return;\n }\n const missing = await missingModulePath(dir, path);\n if (missing) {\n findings.push(\n finding(\n module,\n where,\n missing === \"escape\"\n ? `\"${path}\" escapes the module folder — the applier refuses this descriptor`\n : `no such file: ${path}`\n )\n );\n }\n}\n\n/**\n * Whether a descriptor-authored path is absent under the module folder, and why. The\n * applier resolves the same paths through `joinModulePath`, so a `../` that slips past\n * doctor here would be reported present and then refused on a stranger's machine.\n */\nasync function missingModulePath(\n dir: string,\n relPosix: string\n): Promise<false | \"absent\" | \"escape\"> {\n let abs: string;\n try {\n abs = joinModulePath(dir, relPosix);\n } catch {\n return \"escape\";\n }\n return (await pathExists(abs)) ? false : \"absent\";\n}\n\nexport interface ProjectCheckArgs {\n config: SaasaloyConfig;\n manifest: Manifest;\n}\n\n/**\n * The one project-state rule (#107): a module `saasaloy.json` lists as installed that owns\n * no file in `.saasaloy/manifest.json`. `add` warns once when it reclaims a target whose\n * file was hand-deleted, and that warning scrolls away; this is how the state stays\n * findable afterwards. The inverse — files tracked for a module that is not installed —\n * is `checkPartialInstalls` below (#49).\n *\n * Already-loaded state comes in rather than a root path, so the rule tests with plain\n * objects like the descriptor rules above it. `config.base` is not walked, so the base app\n * can never be reported.\n */\nexport function checkProject(args: ProjectCheckArgs): Finding[] {\n const { config, manifest } = args;\n const owners = new Set(\n Object.values(manifest.managed).map((entry) => entry.module)\n );\n // `managed` is the file ledger, and the criterion is about files. A module owning a\n // patch or a link but no file is still a module that owns no files.\n return config.installed\n .filter((name) => !owners.has(name))\n .map((name) =>\n finding(\n name,\n \"/installed\",\n `installed but owns no files in .saasaloy/manifest.json — run \\`saasaloy remove ${name}\\` to drop it.`\n )\n );\n}\n\nexport interface DoctorTarget {\n /** The registry directory the modules sit in. */\n registryDir: string;\n /** Module folder names to check — one, or every module the registry offers. */\n names: string[];\n}\n\n/**\n * Read `path` as either one module folder or a registry of them. A folder carrying a\n * `registry-item.json` is the module; anything else is a directory of module folders.\n */\nexport async function resolveDoctorTarget(path: string): Promise<DoctorTarget> {\n const dir = resolve(path);\n if (await pathExists(join(dir, \"registry-item.json\"))) {\n return { names: [basename(dir)], registryDir: dirname(dir) };\n }\n return { names: await registryModuleNames(dir), registryDir: dir };\n}\n\nexport interface ProjectState {\n /** The project's `.saasaloy/manifest.json`, as `loadManifest` returns it. */\n manifest: Manifest;\n /** `saasaloy.json`'s `installed` list. */\n installed: string[];\n}\n\n/**\n * The inverse of `checkProject`'s rule (#49): a module the manifest tracks work for,\n * absent from `installed`, is a partial install.\n *\n * `add` leaves that state on purpose. It never rolls back, so a run that failed mid-apply\n * — or one whose file drifted between the plan and the write — keeps the files it wrote\n * and withholds the install. The bookkeeping is honest and the project is unfinished, and\n * only a re-run finishes it, which is what this says out loud. Two files, or twenty, are\n * one finding: the module is the unit the user acts on.\n */\nexport function checkPartialInstalls(state: ProjectState): Finding[] {\n const installed = new Set(state.installed);\n const tracked = new Set<string>();\n for (const entry of Object.values(state.manifest.managed)) {\n tracked.add(entry.module);\n }\n // A patch counts as tracked work too. A module whose files all held as conflicts can\n // still have landed a dependency in a package.json, and that is on disk either way.\n for (const patch of state.manifest.patches) {\n tracked.add(patch.module);\n }\n // The base records its files under a reserved name and is never in `installed` (#120).\n return [...tracked]\n .filter((name) => !installed.has(name) && name !== BASE_MODULE)\n .toSorted()\n .map((name) =>\n finding(\n name,\n `/installed/${name}`,\n `partial install — re-run \\`saasaloy add ${name}\\``\n )\n );\n}\n\n/** Check every module named by `target`, in name order. */\nexport async function checkTarget(\n target: DoctorTarget\n): Promise<ModuleReport[]> {\n const siblings = await registryModuleNames(target.registryDir);\n const aliases = await collectAliases(target.registryDir, siblings);\n const reports: ModuleReport[] = [];\n for (const name of target.names) {\n reports.push(\n await checkModule({\n aliases,\n dir: join(target.registryDir, name),\n siblings,\n })\n );\n }\n return reports;\n}\n\nexport interface BaseCheckArgs {\n root: string;\n lock: Lockfile;\n manifest: Manifest;\n /** The seed paths the running template declares — listed, never hashed. */\n seedFiles: string[];\n}\n\nexport interface BaseReport {\n /** `untracked` when the project carries no usable record; `update` writes one. */\n status: \"tracked\" | \"untracked\";\n record?: LockBase;\n /** Managed base files whose bytes no longer match their recorded hash. */\n drifted: string[];\n /** Managed base files the record names that are not on disk. */\n missing: string[];\n /** How many managed base files still match their recorded hash. */\n matching: number;\n /** Seed files — declared by the template, or recorded as such — reported but not checked. */\n seed: string[];\n}\n\n/**\n * The base half of `doctor <project>` (#120): is there a record, which CLI wrote it, and\n * which managed base files moved since. A drifted file is information, not a problem —\n * the owner is meant to edit the base — so this never contributes a `Finding`, and it\n * never writes: an untracked base is reported and left for `update` to adopt.\n */\nexport async function checkBase(args: BaseCheckArgs): Promise<BaseReport> {\n const { root, lock, manifest } = args;\n const seedSet = new Set(args.seedFiles);\n if (!isBaseTracked(lock, manifest) || !lock.base) {\n return {\n status: \"untracked\",\n drifted: [],\n missing: [],\n matching: 0,\n seed: args.seedFiles.toSorted(),\n };\n }\n const drifted: string[] = [];\n const missing: string[] = [];\n const seed: string[] = [];\n let matching = 0;\n for (const [target, entry] of Object.entries(baseEntries(manifest))) {\n if (seedSet.has(target)) {\n seed.push(target);\n continue;\n }\n const mine = await readIfPresent(resolveWithinRoot(root, target));\n if (mine === undefined) {\n missing.push(target);\n } else if (hashContent(mine) === entry.hash) {\n matching++;\n } else {\n drifted.push(target);\n }\n }\n return {\n status: \"tracked\",\n record: lock.base,\n drifted: drifted.toSorted(),\n missing: missing.toSorted(),\n matching,\n seed: seed.toSorted(),\n };\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport {\n cancel,\n intro,\n isCancel,\n log,\n note,\n outro,\n select,\n text,\n} from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { parseDevVars } from \"../lib/dev-vars.js\";\nimport {\n apiWorkspace,\n appendVars,\n discoverWorkspaces,\n findWranglerWorkspaces,\n isSet,\n productionSecretCommands,\n routeVariable,\n targetFileName,\n workspaceForPath,\n workspacesByModule,\n} from \"../lib/env-vars.js\";\nimport type { Declaration } from \"../lib/env-vars.js\";\nimport {\n EXIT_OK,\n EXIT_REFUSED,\n exitCodeFor,\n formatFailure,\n} from \"../lib/exit.js\";\nimport {\n assertNoSymlinkPath,\n pathExists,\n resolveWithinRoot,\n} from \"../lib/fs-utils.js\";\nimport { isPathIgnored } from \"../lib/gitignore.js\";\nimport { loadLock } from \"../lib/lock.js\";\nimport type { Lockfile } from \"../lib/lock.js\";\nimport { loadManifest } from \"../lib/manifest.js\";\nimport { findProjectRoot } from \"../lib/project.js\";\nimport {\n LocalRegistrySource,\n REGISTRY_ENV,\n RemoteRegistrySource,\n} from \"../lib/registry.js\";\nimport type { RegistrySource } from \"../lib/registry.js\";\nimport { loadConfig } from \"../lib/saasaloy-config.js\";\nimport type { LoadedConfig } from \"../lib/saasaloy-config.js\";\nimport { isInteractive, wrapForNote } from \"../lib/tui.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\n\n// `saasaloy env` — fill in the variables the installed modules declare (#50).\n//\n// Before this, `add` rendered `apps/api/.dev.vars.example` and stopped there: the project\n// knew what it needed and no file on disk had it. A `PUBLIC_*` value was worse off still,\n// because it belongs in the frontend's `.env`, which the example file never described.\n//\n// The command fills blanks and nothing else. A value already typed is never rewritten,\n// whatever the descriptor's `devVars` suggests, because the person who typed it knew\n// something the descriptor did not. It refuses to write at all unless git ignores the\n// target, and it prints the production commands rather than running them: putting a\n// secret into a live account is a deploy, and this is a scaffolding tool.\n\nexport interface Options {\n /** `--check`: report what is missing and exit non-zero, prompting for nothing. */\n check: boolean;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\"--check\", \"--help\", \"-h\"]);\nconst USAGE = \"saasaloy env [--check]\";\nconst HELP: CommandHelp = {\n name: \"env\",\n describe: DESCRIPTIONS.env,\n usage: USAGE,\n flags: {\n \"--check\": \"report missing variables and exit non-zero, without prompting\",\n },\n};\n\nexport function parseArgs(argv: string[]): Options {\n const unknown: string[] = [];\n for (const arg of argv) {\n // Every declared variable is covered; there is no single-variable form to name.\n if (!arg.startsWith(\"-\") || !KNOWN_FLAGS.has(arg)) {\n unknown.push(arg);\n }\n }\n return { check: argv.includes(\"--check\"), unknown };\n}\n\n/** A variable that still needs an answer, and where the answer goes. */\nexport interface Pending {\n declaration: Declaration;\n /** Project-relative workspace root, or undefined when routing found no target. */\n workspace?: string;\n /** Project-relative target file, or undefined alongside an undefined workspace. */\n file?: string;\n /** Set when routing found several plausible workspaces and a person must pick. */\n choices?: string[];\n}\n\n/**\n * One report line per pending variable: the name, the module that declared it, and\n * either the file it will land in or why no file could be chosen.\n *\n * Pure and exported so `--check`'s output is tested without a project on disk.\n */\nexport function renderPending(pending: Pending[]): string[] {\n return pending.map((entry) => {\n const head = `${pc.cyan(entry.declaration.name)} ${pc.dim(`— declared by ${entry.declaration.module}`)}`;\n if (entry.file) {\n return `${head}\\n ${pc.dim(`→ ${entry.file}`)}`;\n }\n const why = entry.choices\n ? `several workspaces fit (${entry.choices.join(\", \")}) — run \\`saasaloy env\\` to pick one`\n : \"no target workspace found — this module wrote no files under a known alias\";\n return `${head}\\n ${pc.yellow(`→ ${why}`)}`;\n });\n}\n\n/**\n * Read every installed module's descriptor and flatten its `envVars` into declarations.\n *\n * The lock records provenance but not `envVars`, so the descriptions have to come from\n * the registry — the same path `update` takes. Each module is pinned to the SHA the lock\n * resolved, so the wording matches the code actually on disk rather than whatever `main`\n * says today.\n *\n * A module that cannot be read is reported and skipped. One unreachable repo must not\n * hide every other module's variables, and `--check` still gates on the ones it saw.\n */\nasync function loadDeclarations(\n config: LoadedConfig,\n lock: Lockfile,\n sources: RegistrySource[],\n onSkip: (message: string) => void\n): Promise<Declaration[]> {\n const override = process.env[REGISTRY_ENV];\n const local: RegistrySource | undefined = override\n ? new LocalRegistrySource(\n isAbsolute(override) ? override : resolve(process.cwd(), override)\n )\n : undefined;\n const resolvers = new Map<string, RegistrySource>();\n\n const declarations: Declaration[] = [];\n for (const name of config.installed) {\n const entry = lock.modules[name];\n let source: RegistrySource | undefined = local;\n if (source === undefined) {\n if (!entry) {\n onSkip(\n `${name} has no lock entry — nothing to read its variables from.`\n );\n continue;\n }\n if (entry.source === \"local\") {\n onSkip(\n `${name} was installed from a local checkout — set ${REGISTRY_ENV} to read its variables.`\n );\n continue;\n }\n const [owner, repo] = entry.source.split(\"/\");\n if (!owner || !repo) {\n onSkip(\n `${name}'s lock source \"${entry.source}\" isn't an owner/repo coordinate.`\n );\n continue;\n }\n // Pin to the resolved SHA, not the ref: the descriptor has to describe the bytes\n // this project installed, and the ref has moved on by definition when it moved.\n const ref = entry.resolved === \"local\" ? entry.ref : entry.resolved;\n const key = `${entry.source}@${ref}`;\n let cached = resolvers.get(key);\n if (!cached) {\n cached = new RemoteRegistrySource(owner, repo, ref);\n resolvers.set(key, cached);\n sources.push(cached);\n }\n source = cached;\n }\n\n try {\n const { item } = await source.readModule(name);\n for (const [variable, description] of Object.entries(\n item.envVars ?? {}\n )) {\n declarations.push({\n description,\n module: name,\n name: variable,\n ...(item.devVars?.[variable] === undefined\n ? {}\n : { devValue: item.devVars[variable] }),\n });\n }\n } catch (error) {\n onSkip(\n `${name}'s descriptor could not be read — ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n // Two modules can declare the same variable (`PUBLIC_API_URL` comes from both `admin`\n // and `waitlist`). First declaration wins, so the prompt is asked once.\n const seen = new Set<string>();\n return declarations.filter((declaration) => {\n if (seen.has(declaration.name)) {\n return false;\n }\n seen.add(declaration.name);\n return true;\n });\n}\n\n/** The workspace an alias sits in, used to find the base app for a `PUBLIC_*` fallback. */\nfunction workspaceForAlias(\n aliases: Record<string, string>,\n alias: string,\n workspaces: string[]\n): string | undefined {\n const value = aliases[alias];\n if (!value) {\n return undefined;\n }\n return workspaceForPath(\n value.endsWith(\"/\") ? value : `${value}/`,\n workspaces\n );\n}\n\n/**\n * Write the answers, one file at a time, and report `[file, count]` per file touched.\n *\n * `contents` carries what each file held when the run read it, so the append is built\n * from the same bytes the \"already set\" decision was made against. Exported because it\n * is the half of `env` that touches disk: the tests drive it directly, since the prompt\n * loop above it needs a terminal that no test process has.\n */\nexport async function writeAnswers(\n root: string,\n answers: Map<string, [string, string][]>,\n contents: Map<string, string | undefined>\n): Promise<[string, number][]> {\n const written: [string, number][] = [];\n for (const [file, additions] of [...answers].toSorted(([a], [b]) =>\n a.localeCompare(b)\n )) {\n if (additions.length === 0) {\n continue;\n }\n const abs = resolveWithinRoot(root, file);\n await assertNoSymlinkPath(root, abs);\n await mkdir(dirname(abs), { recursive: true });\n await writeFile(abs, appendVars(contents.get(file), additions), \"utf-8\");\n written.push([file, additions.length]);\n }\n return written;\n}\n\nasync function readTarget(\n root: string,\n file: string\n): Promise<string | undefined> {\n const abs = resolveWithinRoot(root, file);\n // Same guard `dev-vars` takes: the lexical check proves the path is inside the project,\n // and the read that follows would still traverse a planted symlink out of it.\n await assertNoSymlinkPath(root, abs);\n return (await pathExists(abs)) ? readFile(abs, \"utf-8\") : undefined;\n}\n\nexport async function runEnv(argv: string[]): Promise<number> {\n // Parse before help answers, so a typo'd flag alongside `--help` still reports the typo.\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n intro(pc.bgCyan(pc.black(\" saasaloy env \")));\n\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n\n const sources: RegistrySource[] = [];\n try {\n const root = await findProjectRoot();\n const config = await loadConfig(root);\n const manifest = await loadManifest(root);\n const lock = await loadLock(root);\n\n const declarations = await loadDeclarations(\n config,\n lock,\n sources,\n (msg) => {\n log.warn(msg);\n }\n );\n if (declarations.length === 0) {\n outro(pc.dim(\"No module declares an environment variable.\"));\n return EXIT_OK;\n }\n\n const workspaces = await discoverWorkspaces(root, config.aliases);\n const wranglerWorkspaces = await findWranglerWorkspaces(root, workspaces);\n const byModule = workspacesByModule(manifest, workspaces);\n const api = apiWorkspace(config.aliases);\n const base = workspaceForAlias(\n config.aliases,\n `@${config.base ?? \"web\"}`,\n workspaces\n );\n\n // One read per target file, reused for every variable that routes to it, so two\n // variables landing in the same `.dev.vars` see the same \"already set\" answer.\n const contents = new Map<string, string | undefined>();\n const readOnce = async (file: string): Promise<string | undefined> => {\n if (!contents.has(file)) {\n contents.set(file, await readTarget(root, file));\n }\n return contents.get(file);\n };\n\n const pending: Pending[] = [];\n const routed: { name: string; workspace: string }[] = [];\n for (const declaration of declarations) {\n const route = routeVariable({\n candidates: byModule.get(declaration.module) ?? [],\n name: declaration.name,\n wranglerWorkspaces,\n ...(api ? { apiWorkspace: api } : {}),\n ...(base ? { baseWorkspace: base } : {}),\n });\n if (route.kind === \"resolved\") {\n const file = `${route.workspace}/${targetFileName(declaration.name)}`;\n routed.push({ name: declaration.name, workspace: route.workspace });\n const values = parseDevVars((await readOnce(file)) ?? \"\");\n if (isSet(values, declaration.name)) {\n continue;\n }\n pending.push({ declaration, file, workspace: route.workspace });\n continue;\n }\n pending.push({\n declaration,\n ...(route.kind === \"ambiguous\" ? { choices: route.choices } : {}),\n });\n }\n\n // The production block is a report, so it prints on every run — a `--check` in CI is\n // exactly where someone wants the deploy commands to hand to an operator.\n const secretCommands = productionSecretCommands(routed);\n const printSecrets = (): void => {\n if (secretCommands.length > 0) {\n note(\n wrapForNote(\n `${secretCommands.join(\"\\n\")}\\n\\n${pc.dim(\"Printed, never run — copy these when you deploy.\")}`\n ),\n \"Production secrets\"\n );\n }\n };\n\n if (pending.length === 0) {\n printSecrets();\n outro(pc.green(\"Every declared variable is set.\"));\n return EXIT_OK;\n }\n\n // Prove every file is ignored before asking for a single secret. Typing an API key\n // into a prompt that then refuses to write it wastes the key as much as the typing —\n // and the same refusal is the right answer under `--check`, where it says the deploy\n // gate would have written a secret into a tracked file.\n const files = [\n ...new Set(pending.flatMap((entry) => (entry.file ? [entry.file] : []))),\n ].toSorted();\n for (const file of files) {\n if (!(await isPathIgnored(root, file))) {\n cancel(\n `${pc.cyan(file)} isn't gitignored — refusing to write a secret into a tracked file. ` +\n `Add it to .gitignore (the base template already does) and run again.`\n );\n return EXIT_REFUSED;\n }\n }\n\n // `--check` and a session with no terminal take the same path. A prompt nobody can\n // answer is a hang, and a hang in CI is worse than a refusal.\n if (opts.check || !isInteractive()) {\n note(wrapForNote(renderPending(pending).join(\"\\n\")), \"Missing\");\n printSecrets();\n const count = pending.length;\n cancel(\n `${count} variable${count === 1 ? \"\" : \"s\"} still unset — run \\`saasaloy env\\` to fill ${count === 1 ? \"it\" : \"them\"} in.`\n );\n return EXIT_REFUSED;\n }\n\n const answers = new Map<string, [string, string][]>();\n for (const entry of pending) {\n let file = entry.file;\n if (!file) {\n if (!entry.choices) {\n log.warn(\n `${pc.cyan(entry.declaration.name)} — no target workspace found; set it by hand.`\n );\n continue;\n }\n const picked = await select({\n message: `Which workspace reads ${entry.declaration.name}?`,\n options: entry.choices.map((workspace) => ({\n label: workspace,\n value: workspace,\n })),\n });\n if (isCancel(picked)) {\n cancel(\"Cancelled — nothing written.\");\n return EXIT_REFUSED;\n }\n file = `${picked}/${targetFileName(entry.declaration.name)}`;\n if (!(await isPathIgnored(root, file))) {\n cancel(\n `${pc.cyan(file)} isn't gitignored — refusing to write a secret into a tracked file.`\n );\n return EXIT_REFUSED;\n }\n const values = parseDevVars((await readOnce(file)) ?? \"\");\n if (isSet(values, entry.declaration.name)) {\n continue;\n }\n }\n\n // The message is the descriptor's own wording. `env` does not paraphrase it: the\n // module author wrote the sentence that explains what the value is for, and a\n // second wording here would drift from the one in `.dev.vars.example`.\n const answer = await text({\n defaultValue: entry.declaration.devValue ?? \"\",\n message: `${pc.cyan(entry.declaration.name)} ${pc.dim(`→ ${file}`)}\\n${entry.declaration.description}`,\n ...(entry.declaration.devValue === undefined\n ? {}\n : { initialValue: entry.declaration.devValue }),\n });\n if (isCancel(answer)) {\n cancel(\"Cancelled — nothing written.\");\n return EXIT_REFUSED;\n }\n answers.set(file, [\n ...(answers.get(file) ?? []),\n [entry.declaration.name, answer],\n ]);\n }\n\n const written = await writeAnswers(root, answers, contents);\n if (written.length > 0) {\n note(\n wrapForNote(\n written\n .map(\n ([file, count]) =>\n `${pc.cyan(file)} ${pc.dim(`— ${count} added`)}`\n )\n .join(\"\\n\")\n ),\n \"Written\"\n );\n }\n printSecrets();\n outro(pc.green(\"Environment filled in.\"));\n return EXIT_OK;\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n } finally {\n for (const source of sources) {\n await source.cleanup?.();\n }\n }\n}\n","import { posix } from \"node:path\";\nimport { pathExists, resolveWithinRoot } from \"./fs-utils.js\";\nimport type { Manifest } from \"./manifest.js\";\n\n// The routing half of `saasaloy env` (#50): which file does a declared variable belong\n// in, and is it already set?\n//\n// Two facts settle every case. A `PUBLIC_*` variable is a build-time value the frontend\n// bundles, so it belongs in that app's `.env`, which Astro and Vite read. Everything else\n// is a secret the Worker reads at runtime, so it belongs in `.dev.vars`, which is\n// wrangler's file and nothing else's. There is no `secret: true` flag on the descriptor\n// and there is not going to be one: the prefix already says it, and a second way to say\n// the same thing is a second way to disagree with it.\n//\n// *Which* app is the part that has to be inferred, because `envVars` is a flat\n// `NAME → description` map with no app scope. The manifest knows: it records every file\n// a module wrote, keyed by project-relative path, so the workspaces a module touched are\n// recoverable from it. This file turns that into a target and says plainly when it\n// cannot.\n\nexport const ENV_FILE = \".env\";\nexport const DEV_VARS_FILE = \".dev.vars\";\n\n/** The literal, case-sensitive prefix that marks a build-time public value. */\nexport const PUBLIC_PREFIX = \"PUBLIC_\";\n\n/** Where an app workspace lives by convention — the half of the repo that is not a lib. */\nconst APPS_DIR = \"apps/\";\n\nconst WRANGLER_CONFIGS = [\"wrangler.jsonc\", \"wrangler.json\", \"wrangler.toml\"];\n\nexport function isPublicVar(name: string): boolean {\n return name.startsWith(PUBLIC_PREFIX);\n}\n\n/** `.env` for a public build-time value, `.dev.vars` for a secret. */\nexport function targetFileName(name: string): string {\n return isPublicVar(name) ? ENV_FILE : DEV_VARS_FILE;\n}\n\n/** One variable a module declares, carrying the description the prompt will read out. */\nexport interface Declaration {\n name: string;\n /** The declaring module's own wording — `env` never paraphrases it. */\n description: string;\n /** The module that declared it. */\n module: string;\n /** The local-dev value the descriptor supplies, if any. */\n devValue?: string;\n}\n\nexport type Route =\n | { kind: \"resolved\"; workspace: string }\n | { kind: \"ambiguous\"; choices: string[] }\n | { kind: \"unknown\" };\n\nexport interface RouteArgs {\n name: string;\n /** Workspaces the declaring module wrote files into, project-relative. */\n candidates: string[];\n /** Of those, the ones holding a wrangler config. */\n wranglerWorkspaces: string[];\n /** The project's Worker workspace, from the `@api` alias. */\n apiWorkspace?: string;\n /** The base app's workspace — where a `PUBLIC_*` value lands when nothing else fits. */\n baseWorkspace?: string;\n}\n\n/** A route to a workspace, or nothing when the caller had no workspace to offer. */\nfunction resolved(workspace: string | undefined): Route | undefined {\n return workspace ? { kind: \"resolved\", workspace } : undefined;\n}\n\n/**\n * The workspace whose `.env` or `.dev.vars` a variable belongs in.\n *\n * A `PUBLIC_*` value is bundled by exactly one frontend, so it looks for an app the\n * declaring module wrote into, falling back to the project's base app — `waitlist` ships\n * a component into `apps/web` and its `PUBLIC_API_URL` is what that component reads.\n *\n * A secret is read by whatever runs it, which for `.dev.vars` means a wrangler process.\n * So it prefers a candidate holding a wrangler config, breaks a tie towards the api\n * workspace, and falls back to the api workspace outright: `packages/email` declares\n * `PLUNK_API_KEY` and writes no app file at all, but the Worker importing that package\n * is what needs the key on disk.\n *\n * Pure, and it says `ambiguous` rather than guessing — the caller prompts.\n */\nexport function routeVariable(args: RouteArgs): Route {\n const { candidates, wranglerWorkspaces } = args;\n\n if (isPublicVar(args.name)) {\n const apps = candidates.filter(\n (w) => w.startsWith(APPS_DIR) && w !== args.apiWorkspace\n );\n if (apps.length === 1) {\n return { kind: \"resolved\", workspace: apps[0]! };\n }\n if (apps.length > 1) {\n return { choices: apps.toSorted(), kind: \"ambiguous\" };\n }\n return resolved(args.baseWorkspace) ?? fromCandidatesAlone(candidates);\n }\n\n const wrangler = candidates.filter((w) => wranglerWorkspaces.includes(w));\n if (wrangler.length === 1) {\n return { kind: \"resolved\", workspace: wrangler[0]! };\n }\n if (wrangler.length > 1) {\n return (\n (args.apiWorkspace && wrangler.includes(args.apiWorkspace)\n ? resolved(args.apiWorkspace)\n : undefined) ?? { choices: wrangler.toSorted(), kind: \"ambiguous\" }\n );\n }\n return resolved(args.apiWorkspace) ?? fromCandidatesAlone(candidates);\n}\n\nfunction fromCandidatesAlone(candidates: string[]): Route {\n if (candidates.length === 1) {\n return { kind: \"resolved\", workspace: candidates[0]! };\n }\n return candidates.length === 0\n ? { kind: \"unknown\" }\n : { choices: candidates.toSorted(), kind: \"ambiguous\" };\n}\n\n/**\n * The workspace roots this project has, derived from the alias map: an alias points at a\n * source directory (`packages/db/src`), and the workspace is the nearest ancestor that\n * carries a `package.json`. Walking up rather than assuming `dirname` keeps a nested\n * alias (`@ui` → `packages/ui/src/components`) pointing at the right workspace.\n *\n * The project root is deliberately excluded. It has a `package.json` too, and a variable\n * routed there would write `.dev.vars` beside `pnpm-workspace.yaml`, where no wrangler\n * process ever looks.\n */\nexport async function discoverWorkspaces(\n root: string,\n aliases: Record<string, string>\n): Promise<string[]> {\n const found = new Set<string>();\n for (const value of Object.values(aliases)) {\n const workspace = await walkUpToWorkspace(root, value);\n if (workspace) {\n found.add(workspace);\n }\n }\n return [...found].toSorted();\n}\n\nasync function walkUpToWorkspace(\n root: string,\n relPosixPath: string\n): Promise<string | undefined> {\n let current = relPosixPath.replace(/\\/+$/, \"\");\n while (current && current !== \".\" && current !== \"/\") {\n if (await pathExists(resolveWithinRoot(root, `${current}/package.json`))) {\n return current;\n }\n const parent = posix.dirname(current);\n if (parent === current) {\n return undefined;\n }\n current = parent;\n }\n return undefined;\n}\n\n/** Of `workspaces`, those holding a wrangler config — the ones that read `.dev.vars`. */\nexport async function findWranglerWorkspaces(\n root: string,\n workspaces: string[]\n): Promise<string[]> {\n const found: string[] = [];\n for (const workspace of workspaces) {\n for (const config of WRANGLER_CONFIGS) {\n if (await pathExists(resolveWithinRoot(root, `${workspace}/${config}`))) {\n found.push(workspace);\n break;\n }\n }\n }\n return found;\n}\n\n/**\n * Which workspaces each module wrote into, read back from the manifest's `managed` map.\n * The key is the project-relative path the file landed on, so the longest workspace that\n * prefixes it is the workspace that owns it. A file under no known workspace (a root\n * `README`, an `infra/` scaffold with no alias) contributes nothing rather than a guess.\n */\nexport function workspacesByModule(\n manifest: Manifest,\n workspaces: string[]\n): Map<string, string[]> {\n const byModule = new Map<string, Set<string>>();\n for (const [path, entry] of Object.entries(manifest.managed)) {\n const workspace = workspaceForPath(path, workspaces);\n if (!workspace) {\n continue;\n }\n const set = byModule.get(entry.module) ?? new Set<string>();\n set.add(workspace);\n byModule.set(entry.module, set);\n }\n return new Map(\n [...byModule].map(([module, set]) => [module, [...set].toSorted()])\n );\n}\n\n/** The longest workspace that contains `path`, or undefined when none does. */\nexport function workspaceForPath(\n path: string,\n workspaces: string[]\n): string | undefined {\n let best: string | undefined;\n for (const workspace of workspaces) {\n if (\n path.startsWith(`${workspace}/`) &&\n (best === undefined || workspace.length > best.length)\n ) {\n best = workspace;\n }\n }\n return best;\n}\n\n/** The api workspace, re-exported so `env` reads one name for the Worker's root. */\nexport { apiWorkspace } from \"./dev-vars.js\";\n\n/**\n * Is this variable already answered? A key present with an empty value is a placeholder\n * someone left behind, not an answer, so `env` offers to fill it. Anything else is a\n * value a person typed, and `env` never rewrites one.\n */\nexport function isSet(values: Record<string, string>, name: string): boolean {\n return (values[name] ?? \"\").trim() !== \"\";\n}\n\n/**\n * Is this line the empty placeholder for `name` — `KEY=`, with nothing but spaces after\n * the `=`? A commented-out line is not one: `# KEY=` has a key of `# KEY`.\n */\nfunction isPlaceholderFor(line: string, name: string): boolean {\n const equals = line.indexOf(\"=\");\n return (\n equals !== -1 &&\n line.slice(0, equals).trim() === name &&\n line.slice(equals + 1).trim() === \"\"\n );\n}\n\n/**\n * The file's new content, with `additions` written in.\n *\n * Every existing line survives byte for byte, comments and ordering included. This is a\n * file a person edits by hand, unlike `.dev.vars.example`, which is regenerated from the\n * descriptors — re-rendering it here would throw away their formatting to say the same\n * thing.\n *\n * One exception, and it is the reason this is not a plain append: a `.dev.vars` copied\n * from `.dev.vars.example` holds `KEY=` for every variable, and `isSet` calls those\n * unset. Appending would leave the file carrying two lines for one key, the first of them\n * a lie. So a placeholder is filled where it stands, and only a key the file has never\n * heard of goes on the end.\n */\nexport function appendVars(\n existing: string | undefined,\n additions: [string, string][]\n): string {\n if (additions.length === 0) {\n return existing ?? \"\";\n }\n if (!existing || existing.trim() === \"\") {\n return `${additions.map(([name, value]) => `${name}=${value}`).join(\"\\n\")}\\n`;\n }\n\n const lines = existing.split(\"\\n\");\n const appended: string[] = [];\n for (const [name, value] of additions) {\n const line = `${name}=${value}`;\n const index = lines.findIndex((existingLine) =>\n isPlaceholderFor(existingLine, name)\n );\n if (index === -1) {\n appended.push(line);\n } else {\n lines[index] = line;\n }\n }\n\n const kept = lines.join(\"\\n\");\n const head = kept.endsWith(\"\\n\") ? kept : `${kept}\\n`;\n return appended.length === 0 ? head : `${head}${appended.join(\"\\n\")}\\n`;\n}\n\n/**\n * The production block: one `wrangler secret put` per secret, grouped by the workspace\n * you have to run it from.\n *\n * Printed, never run. Putting a secret into a live Cloudflare account is a deploy, and a\n * scaffolding tool that deploys on your behalf is a scaffolding tool you cannot trust\n * with a token. The lines are here to be copied.\n */\nexport function productionSecretCommands(\n targets: { name: string; workspace: string }[]\n): string[] {\n const byWorkspace = new Map<string, string[]>();\n for (const { name, workspace } of targets) {\n if (isPublicVar(name)) {\n continue;\n }\n byWorkspace.set(workspace, [...(byWorkspace.get(workspace) ?? []), name]);\n }\n const lines: string[] = [];\n for (const workspace of [...byWorkspace.keys()].toSorted()) {\n lines.push(`# from ${workspace}`);\n for (const name of (byWorkspace.get(workspace) ?? []).toSorted()) {\n lines.push(`wrangler secret put ${name}`);\n }\n }\n return lines;\n}\n","import { readFile } from \"node:fs/promises\";\nimport { dirname, join, relative, resolve, sep } from \"node:path\";\nimport { pathExists } from \"./fs-utils.js\";\n\n// Does git ignore this path? `saasaloy env` writes real secrets into `.dev.vars` and\n// `.env`, so it has to prove the file will not be committed before it writes a single\n// byte (#50). The base template's `_gitignore` already carves both out, but a project is\n// a person's to edit, and an `env` that wrote a live API key into a tracked file would be\n// the worst bug this CLI could ship.\n//\n// The check is done here, in process, rather than by shelling out to `git check-ignore`.\n// `env`'s whole contract is that it prints deployment commands and never runs anything,\n// and that promise is only checkable if the command path starts no child process at all. So this\n// file implements the subset of gitignore's pattern language the question needs:\n// comments, negation, anchoring, directory-only patterns, `*`, `?`, and `**`.\n//\n// Four deliberate gaps, none of which changes the answer for an env file: character\n// classes (`[a-z]`) are matched literally, a backslash escape (`\\#.env`, a trailing\n// `.env\\ `) is matched literally rather than unescaped, `.git/info/exclude` and the\n// global excludes file are not read, and a `.gitignore` inside an already-ignored\n// directory is still read. Each can only make the check stricter (report \"not ignored\"\n// for something git would ignore), which refuses rather than leaks.\n\ninterface Rule {\n /** Tested against the path relative to the `.gitignore`'s own directory. */\n test: RegExp;\n /** A leading `!` — re-includes what an earlier rule excluded. */\n negated: boolean;\n /** A trailing `/` — matches a directory and never a file. */\n dirOnly: boolean;\n}\n\ninterface Layer {\n /** Project-relative POSIX directory the `.gitignore` sits in; `\"\"` at the root. */\n base: string;\n rules: Rule[];\n}\n\nconst REGEX_SPECIAL = /[.+^${}()|[\\]\\\\]/g;\n\n/** Translate a gitignore glob body into a regex source, `/` never matched by `*`. */\nfunction globToRegex(glob: string): string {\n let out = \"\";\n let index = 0;\n while (index < glob.length) {\n const char = glob[index]!;\n if (char === \"*\" && glob[index + 1] === \"*\") {\n // `**/` crosses directory boundaries; a bare `**` matches the rest of the path.\n index += 2;\n if (glob[index] === \"/\") {\n index += 1;\n out += \"(?:.*/)?\";\n } else {\n out += \".*\";\n }\n continue;\n }\n if (char === \"*\") {\n out += \"[^/]*\";\n } else if (char === \"?\") {\n out += \"[^/]\";\n } else {\n out += char.replace(REGEX_SPECIAL, \"\\\\$&\");\n }\n index += 1;\n }\n return out;\n}\n\n/**\n * One `.gitignore` line as a rule, or undefined for a blank line or a comment.\n *\n * Exported for the tests: the anchoring rule (a pattern with an interior `/` is relative\n * to the `.gitignore`, one without matches a basename at any depth) is the part most\n * likely to be got wrong, and it is worth pinning directly.\n */\nexport function compilePattern(pattern: string): Rule | undefined {\n const trimmed = pattern.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) {\n return undefined;\n }\n const negated = trimmed.startsWith(\"!\");\n let body = negated ? trimmed.slice(1) : trimmed;\n const dirOnly = body.endsWith(\"/\");\n if (dirOnly) {\n body = body.slice(0, -1);\n }\n if (!body) {\n return undefined;\n }\n // A `/` anywhere but the very end anchors the pattern to this `.gitignore`'s directory.\n const anchored = body.includes(\"/\");\n if (body.startsWith(\"/\")) {\n body = body.slice(1);\n }\n const prefix = anchored ? \"\" : \"(?:.*/)?\";\n return {\n dirOnly,\n negated,\n test: new RegExp(`^${prefix}${globToRegex(body)}$`),\n };\n}\n\nexport function parseGitignore(text: string): Rule[] {\n const rules: Rule[] = [];\n for (const line of text.split(\"\\n\")) {\n const rule = compilePattern(line);\n if (rule) {\n rules.push(rule);\n }\n }\n return rules;\n}\n\n/**\n * Whether the layers exclude one path. Shallow `.gitignore`s are applied before deep\n * ones and, within a file, the last matching line wins — git's own precedence, which is\n * what makes `!.env.example` after `.env.*` mean anything.\n */\nfunction excludes(layers: Layer[], candidate: string, isDir: boolean): boolean {\n let ignored = false;\n for (const layer of layers) {\n if (layer.base && !candidate.startsWith(`${layer.base}/`)) {\n continue;\n }\n const rel = layer.base ? candidate.slice(layer.base.length + 1) : candidate;\n for (const rule of layer.rules) {\n if (rule.dirOnly && !isDir) {\n continue;\n }\n if (rule.test.test(rel)) {\n ignored = !rule.negated;\n }\n }\n }\n return ignored;\n}\n\n/**\n * The repository the project sits in, or undefined when it sits in none.\n *\n * The `.git` marker is not always at the project root. `saasaloy init .` inside an\n * existing repository deliberately does not nest a second one, and the CLI's own\n * `.dev/playground` is exactly that shape — so anchoring at the project root would report\n * every file as unignored and refuse every write.\n */\nexport async function findRepoRoot(start: string): Promise<string | undefined> {\n let current = resolve(start);\n for (;;) {\n if (await pathExists(join(current, \".git\"))) {\n return current;\n }\n const parent = dirname(current);\n if (parent === current) {\n return undefined;\n }\n current = parent;\n }\n}\n\n/**\n * Would git ignore `relPosixPath` (project-relative, need not exist yet)?\n *\n * A path under no repository at all answers false. That is the conservative reading:\n * `env` uses this to decide whether writing a secret is safe, and \"there is no repository\n * here yet\" is not proof that the file will stay out of one. Refusing says so; a person\n * who wants the file anyway can `git init` or write it by hand.\n */\nexport async function isPathIgnored(\n root: string,\n relPosixPath: string\n): Promise<boolean> {\n const repoRoot = await findRepoRoot(root);\n if (!repoRoot) {\n return false;\n }\n // Everything below is measured from the repository root, so a `.gitignore` above the\n // project is applied — and a project inside an already-ignored directory answers yes,\n // which is what git itself says.\n const fromRepo = relative(\n repoRoot,\n join(resolve(root), ...relPosixPath.split(\"/\"))\n );\n const segments = fromRepo.split(sep).filter(Boolean);\n if (segments.length === 0 || segments.includes(\"..\")) {\n return false;\n }\n\n const layers: Layer[] = [];\n for (let depth = 0; depth < segments.length; depth += 1) {\n const parts = segments.slice(0, depth);\n const file = join(repoRoot, ...parts, \".gitignore\");\n if (await pathExists(file)) {\n layers.push({\n base: parts.join(\"/\"),\n rules: parseGitignore(await readFile(file, \"utf-8\")),\n });\n }\n }\n\n // Top down: git cannot re-include a path inside an excluded directory, so the first\n // ancestor that matches settles it for everything below.\n for (let depth = 1; depth <= segments.length; depth += 1) {\n const candidate = segments.slice(0, depth).join(\"/\");\n if (excludes(layers, candidate, depth < segments.length)) {\n return true;\n }\n }\n return false;\n}\n","import { spawn } from \"node:child_process\";\nimport { readdir } from \"node:fs/promises\";\nimport { basename, join, resolve } from \"node:path\";\nimport {\n cancel,\n intro,\n isCancel,\n note,\n outro,\n select,\n spinner,\n text,\n} from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport type { LinkState } from \"../lib/fs-utils.js\";\nimport {\n classifyLink,\n createDirLink,\n pathExists,\n readDirNames,\n} from \"../lib/fs-utils.js\";\nimport { EXIT_FAILURE, EXIT_OK, EXIT_REFUSED } from \"../lib/exit.js\";\nimport { baseRecord, recordBaseFiles, templateHash } from \"../lib/base.js\";\nimport { loadLock, saveLock } from \"../lib/lock.js\";\nimport { logger } from \"../lib/logger.js\";\nimport { loadManifest, saveManifest } from \"../lib/manifest.js\";\nimport {\n baseTemplateDir,\n copyTemplate,\n templateVars,\n} from \"../lib/scaffold.js\";\nimport { stripAnsi, wrapForNote } from \"../lib/tui.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { readVersion } from \"../version.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\n\n// `saasaloy init <name>` — scaffold the near-inert base (Astro landing + @repo/ui\n// + @repo/tsconfig) and print next steps. The base ships committed AGENTS.md/CLAUDE.md\n// (fixed common rules); nothing is generated. Two things are written rather than copied:\n// the `.claude/skills/*` link for the skills the base carries — a per-machine symlink, not\n// a file (see linkAgentSkills) — and the base's provenance, one manifest entry per rendered\n// file plus the lock's `base` record, so `outdated`/`update`/`doctor` can speak about the\n// base afterwards (#120, ADR 0032). Churny modules (api, database, auth, admin, features)\n// are added later via `saasaloy add`, which copies their own skills in.\n\n// wrangler and npm package names share this constraint.\nconst NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;\n\nexport interface InitOptions {\n name?: string;\n force: boolean;\n noInstall: boolean;\n noGit: boolean;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\n \"--force\",\n \"--no-install\",\n \"--no-git\",\n \"--help\",\n \"-h\",\n]);\nconst USAGE = \"saasaloy init [<name>] [--force] [--no-install] [--no-git]\";\nconst HELP: CommandHelp = {\n name: \"init\",\n describe: DESCRIPTIONS.init,\n usage: USAGE,\n flags: {\n \"--force\": \"scaffold into a directory that is not empty\",\n \"--no-install\": \"never run pnpm install, and never ask\",\n \"--no-git\": \"do not run git init in the new project\",\n },\n};\n\n/** Split `init`'s argv into options. Exported for its own tests, like `add`'s. */\nexport function parseArgs(argv: string[]): InitOptions {\n const positional: string[] = [];\n const unknown: string[] = [];\n for (const arg of argv) {\n if (!arg.startsWith(\"-\")) {\n positional.push(arg);\n } else if (!KNOWN_FLAGS.has(arg)) {\n unknown.push(arg);\n }\n }\n unknown.push(...positional.slice(1));\n return {\n force: argv.includes(\"--force\"),\n name: positional[0],\n // Skip the install prompt entirely and never run pnpm install — for scripted/CI\n // scaffolds (e.g. `pnpm play:init`) that manage installs themselves.\n noInstall: argv.includes(\"--no-install\"),\n noGit: argv.includes(\"--no-git\"),\n unknown,\n };\n}\n\ninterface RunResult {\n ok: boolean;\n message?: string;\n}\n\n// Run one child process in the scaffolded project. Output is buffered (not streamed)\n// so only the caller's spinner shows; both streams are captured so a failure can\n// report *why*. pnpm writes its `ERR_PNPM_*` diagnostics to stdout, not stderr,\n// so we keep both. Never throws — failures come back as { ok: false } so init\n// can carry on regardless.\nfunction run(command: string, args: string[], cwd: string): Promise<RunResult> {\n return new Promise((resolvePromise) => {\n // On Windows pnpm is `pnpm.cmd`, which bare spawn won't resolve — go via the shell there.\n const child = spawn(command, args, {\n cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n shell: process.platform === \"win32\",\n });\n let stdout = \"\";\n let stderr = \"\";\n child.stdout?.on(\"data\", (chunk) => {\n stdout += chunk.toString();\n });\n child.stderr?.on(\"data\", (chunk) => {\n stderr += chunk.toString();\n });\n child.on(\"error\", (err) => {\n // e.g. pnpm not on PATH.\n resolvePromise({ ok: false, message: err.message });\n });\n child.on(\"close\", (code) => {\n if (code === 0) {\n resolvePromise({ ok: true });\n } else {\n // Prefer pnpm's own diagnostics (stderr, then stdout); fall back to the code.\n const details = [stderr.trim(), stdout.trim()]\n .filter(Boolean)\n .join(\"\\n\");\n resolvePromise({\n ok: false,\n message:\n details ||\n `${command} ${args.join(\" \")} exited with code ${code ?? \"unknown\"}`,\n });\n }\n });\n });\n}\n\nfunction runPnpmInstall(cwd: string): Promise<RunResult> {\n return run(\"pnpm\", [\"install\"], cwd);\n}\n\n// Make the scaffold a git repository. ADR 0024, CONTRIBUTING and the QA plans all say\n// `init` does this, and until #98 none of them was true: husky's `prepare` hook has no\n// repository to install into, and turbo falls back to a global cache key that can serve\n// a stale build of a previous template. Skipped when the target already sits inside a\n// working tree — `saasaloy init .` inside an existing repo must not nest a second one.\nasync function initGitRepo(\n cwd: string\n): Promise<{ created: boolean; message?: string }> {\n const inside = await run(\"git\", [\"rev-parse\", \"--is-inside-work-tree\"], cwd);\n if (inside.ok) {\n return { created: false };\n }\n const result = await run(\"git\", [\"init\"], cwd);\n if (!result.ok) {\n return { created: false, message: result.message ?? \"git init failed\" };\n }\n return { created: true };\n}\n\ninterface SkillLinkResult {\n linked: string[];\n conflicts: string[];\n failures: { name: string; message: string }[];\n // `.agents/skills` itself couldn't be read, so no per-skill result is meaningful.\n // A *missing* directory isn't this: that's the ordinary \"template ships no skills\" case\n // and comes back as an empty result.\n unreadable?: string;\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n// Some of the base's skills run in an order — saasaloy-setup writes the brief that\n// saasaloy-landing-copy reads — and readdir's alphabetical order happens to invert exactly\n// that pair. There is no manifest to read a dependency out of, and inventing one for two\n// skills would cost more than it explains, so the order is listed here. Anything not named\n// keeps its directory order, after these.\nconst SKILL_ORDER = [\"saasaloy-setup\"];\n\nfunction bySkillOrder(a: string, b: string): number {\n const rank = (name: string) => {\n const at = SKILL_ORDER.indexOf(name);\n return at === -1 ? SKILL_ORDER.length : at;\n };\n return rank(a) - rank(b);\n}\n\n// Point `.claude/skills/<name>` at each skill the base ships, so Claude Code discovers it\n// the moment scaffolding finishes rather than after the owner reads AGENTS.md.\n//\n// The real files travel in `.agents/skills/` — readable by every agent, and committed,\n// because the template's .gitignore ignores `.claude/skills/` (the link is a per-machine\n// artefact, and a skill shipped there would land ignored and never reach the owner's repo).\n// ADR 0015 is the same arrangement `saasaloy add` uses for module skills; `init` calls the\n// two helpers directly rather than going through the applier, because the base has no\n// descriptor to plan from — its files are recorded straight off the render (ADR 0032).\n//\n// Never throws, and never fails `init`: a link is a convenience, and losing it costs\n// discovery, not files. A path already occupied by something that isn't ours is reported\n// and left exactly as it was.\nasync function linkAgentSkills(target: string): Promise<SkillLinkResult> {\n const result: SkillLinkResult = { linked: [], conflicts: [], failures: [] };\n const skillsDir = join(target, \".agents\", \"skills\");\n let names: string[];\n try {\n names = await readDirNames(skillsDir);\n } catch (error) {\n // readDirNames returns [] when the directory is simply absent, so reaching here means\n // it exists and we couldn't read it. Say so: the alternative is init printing no links\n // and no reason, which reads exactly like a template that ships no skills.\n result.unreadable = errorMessage(error);\n return result;\n }\n for (const name of names.toSorted(bySkillOrder)) {\n const linkAbs = join(target, \".claude\", \"skills\", name);\n const targetAbs = join(skillsDir, name);\n // classifyLink can reject after its lstat succeeds (the readlink behind it), so it\n // belongs inside the non-fatal path too — a link is a convenience, and no failure\n // here may take `init` down after the scaffold already landed.\n let state: LinkState;\n try {\n state = await classifyLink(linkAbs, targetAbs);\n } catch (error) {\n result.failures.push({ name, message: errorMessage(error) });\n continue;\n }\n if (state === \"conflict\") {\n result.conflicts.push(name);\n continue;\n }\n if (state === \"correct\") {\n result.linked.push(name);\n continue;\n }\n try {\n await createDirLink(linkAbs, targetAbs);\n result.linked.push(name);\n } catch (error) {\n result.failures.push({ name, message: errorMessage(error) });\n }\n }\n return result;\n}\n\nexport async function runInit(argv: string[]): Promise<number> {\n // Parse before help answers, so a typo'd flag alongside `--help` still reports the typo.\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n const { force, noInstall, noGit } = opts;\n let nameArg = opts.name;\n\n intro(pc.bgCyan(pc.black(\" saasaloy init \")));\n\n // `init` ignored every flag it did not know, while `add`/`remove`/`update` rejected\n // theirs — so `saasaloy init app --forse` scaffolded into a non-empty directory and\n // said nothing. One rule now (#98).\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n\n // No name given — ask for it rather than erroring out.\n if (!nameArg) {\n const answer = await text({\n message: \"Project name?\",\n placeholder: \"my-app (use `.` for the current directory)\",\n validate: (value) => {\n const trimmed = value?.trim() ?? \"\";\n if (!trimmed) {\n return \"Enter a project name (or `.` for the current directory).\";\n }\n // Mirror the arg path: name is the basename of the resolved target.\n const name = basename(resolve(process.cwd(), trimmed));\n if (!NAME_PATTERN.test(name)) {\n return \"Use lowercase letters, digits, and hyphens (e.g. my-app).\";\n }\n // clack accepts a value only on an explicit `undefined`, and consistent-return\n // rejects a bare `return`.\n // oxlint-disable-next-line unicorn/no-useless-undefined\n return undefined;\n },\n });\n if (isCancel(answer)) {\n cancel(\"init cancelled\");\n return EXIT_FAILURE;\n }\n nameArg = answer.trim();\n }\n\n // nameArg may be a bare name (`my-app`), `.`, or a path (`./apps/my-app`).\n const target = resolve(process.cwd(), nameArg);\n const projectName = basename(target);\n\n if (!NAME_PATTERN.test(projectName)) {\n cancel(\n `Invalid project name \"${projectName}\". Use lowercase letters, digits, and hyphens (e.g. my-app).`\n );\n return EXIT_REFUSED;\n }\n\n if (await pathExists(target)) {\n const entries = (await readdir(target)).filter((e) => e !== \".git\");\n if (entries.length > 0 && !force) {\n cancel(\n `Directory ${nameArg} is not empty. Re-run with --force to scaffold into it anyway.`\n );\n return EXIT_REFUSED;\n }\n }\n\n const s = spinner();\n s.start(`Scaffolding ${pc.cyan(projectName)}`);\n const templateDir = await baseTemplateDir();\n const written = await copyTemplate(\n templateDir,\n target,\n templateVars(projectName)\n );\n s.stop(\n `Scaffolded ${pc.cyan(projectName)} ${pc.dim(\"(apps/web · packages/ui · packages/tsconfig)\")}`\n );\n\n // Record what was just rendered: every file at the hash of its rendered bytes, and the\n // template's own hash beside the CLI version. These ledgers decide whether later\n // updates may overwrite files, so init must not report success if either write fails.\n try {\n const manifest = await loadManifest(target);\n const lock = await loadLock(target);\n recordBaseFiles(manifest, written);\n lock.base = baseRecord(\n await readVersion(),\n await templateHash(templateDir)\n );\n await saveManifest(target, manifest);\n await saveLock(target, lock);\n } catch (error) {\n cancel(\n `Couldn't record the base template: ${errorMessage(error)}\\n` +\n `The scaffold files remain, but init cannot safely complete without its project state.`\n );\n return EXIT_FAILURE;\n }\n\n // Before the install: husky's `prepare` script runs during `pnpm install` and needs a\n // repository to install its hooks into.\n if (!noGit) {\n const git = await initGitRepo(target);\n if (git.created) {\n logger.step(`Initialized a git repository ${pc.dim(\"(git init)\")}.`);\n } else if (git.message) {\n logger.warn(\n `Couldn't run ${pc.cyan(\"git init\")}: ${git.message} ` +\n `${pc.dim(\"(the scaffold is fine — run it yourself)\")}.`\n );\n }\n }\n\n const skills = await linkAgentSkills(target);\n if (skills.linked.length > 0) {\n const lines = skills.linked.map(\n (name) =>\n `${pc.cyan(`.claude/skills/${name}`)} ${pc.dim(`→ .agents/skills/${name}`)}`\n );\n note(\n wrapForNote(\n `${lines.join(\"\\n\")}\\n\\n${pc.dim(\"Symlinked for Claude Code — the skill files live in `.agents/skills/`.\")}`\n ),\n \"Skill links\"\n );\n }\n if (skills.unreadable) {\n logger.warn(\n `Couldn't read ${pc.cyan(\".agents/skills\")}: ${skills.unreadable} ` +\n `${pc.dim(\"(the skill files are still there — fix the permissions and re-run init to link them)\")}.`\n );\n }\n for (const name of skills.conflicts) {\n logger.warn(\n `Skill link ${pc.cyan(`.claude/skills/${name}`)} already exists and isn't ours — ` +\n `left untouched ${pc.dim(\"(remove it, then re-run to link the skill)\")}.`\n );\n }\n for (const failure of skills.failures) {\n logger.warn(\n `Couldn't link skill ${pc.cyan(failure.name)}: ${failure.message} ` +\n `${pc.dim(\"(the skill files are still in `.agents/skills/`)\")}.`\n );\n }\n\n // Offer to install now; on decline (or cancel) fall back to the printed steps.\n // `select` (not `confirm`) so each choice renders on its own line.\n let installed = false;\n const wantsInstall = noInstall\n ? false\n : await select({\n message: \"Install dependencies now?\",\n options: [\n { value: true, label: `Yes, run ${pc.cyan(\"pnpm install\")}` },\n { value: false, label: \"No, I'll run it later\" },\n ],\n initialValue: true,\n });\n if (!isCancel(wantsInstall) && wantsInstall) {\n const install = spinner();\n install.start(`Installing dependencies ${pc.dim(\"(pnpm install)\")}`);\n const result = await runPnpmInstall(target);\n if (result.ok) {\n installed = true;\n install.stop(`Installed dependencies ${pc.dim(\"(pnpm install)\")}`);\n } else {\n // Don't break the flow — report and let the user finish it by hand.\n install.stop(pc.yellow(\"pnpm install did not finish\"));\n logger.warn(\n `Couldn't install dependencies automatically — run ${pc.cyan(\"pnpm install\")} yourself.`\n );\n if (result.message) {\n // Show pnpm's own diagnostics inside a box, tail-trimmed and soft-wrapped to\n // the terminal width so a long line (e.g. a registry URL) can't break the rail.\n const lines = stripAnsi(result.message)\n .split(\"\\n\")\n .filter((line) => line.trim() !== \"\");\n // Color each line individually: clack's `note` splits on \\n and prefixes each\n // line, so a single wrapping color would only tint the first one.\n const body = wrapForNote(lines.slice(-12).join(\"\\n\"))\n .split(\"\\n\")\n .map((line) => pc.dim(pc.red(line)))\n .join(\"\\n\");\n note(body, \"pnpm install output\");\n }\n }\n }\n\n const steps = [\n nameArg === \".\" ? null : `cd ${nameArg}`,\n installed ? null : \"pnpm install\",\n `pnpm dev ${pc.dim(\"# run dev servers\")}`,\n // The skills the base ships are the fastest way to make the scaffold yours, and an\n // owner who never opens AGENTS.md would otherwise not know they exist.\n // Pad to the same column the commands above comment at (`pnpm dev` + 21 spaces).\n ...skills.linked.map(\n (name) => `${`/${name}`.padEnd(29)}${pc.dim(\"# in Claude Code\")}`\n ),\n ]\n .filter((line): line is string => line !== null)\n .map((line) => pc.cyan(line))\n .join(\"\\n\");\n\n note(steps, \"Next steps\");\n outro(pc.green(`🎉 Created ${projectName} successfully.`));\n return EXIT_OK;\n}\n","// Console reporter, now grafted onto @clack/prompts' `log.*` (connected-rail\n// output) with picocolors for small accents. The call sites are unchanged from\n// the original dependency-free shim — same `info/step/success/warn/error` shape.\n\nimport { log } from \"@clack/prompts\";\nimport pc from \"picocolors\";\n\nexport const logger = {\n error(message: string): void {\n log.error(message);\n },\n info(message: string): void {\n log.info(message);\n },\n step(message: string): void {\n // clack's `log.step` prefixes a green rail marker; keep the copy dim so\n // these read as sub-lines under a heading, matching the old two-space indent.\n log.step(pc.dim(message));\n },\n success(message: string): void {\n log.success(message);\n },\n warn(message: string): void {\n log.warn(message);\n },\n};\n","import { cancel, intro, note, outro } from \"@clack/prompts\";\nimport { join } from \"node:path\";\nimport pc from \"picocolors\";\nimport {\n EXIT_OK,\n EXIT_REFUSED,\n exitCodeFor,\n formatFailure,\n} from \"../lib/exit.js\";\nimport { pathExists } from \"../lib/fs-utils.js\";\nimport { findProjectRoot } from \"../lib/project.js\";\nimport {\n createRegistrySource,\n parseCoordinate,\n REGISTRY_ENV,\n} from \"../lib/registry.js\";\nimport type { RegistrySource } from \"../lib/registry.js\";\nimport { CONFIG_FILE, loadConfig } from \"../lib/saasaloy-config.js\";\nimport { wrapForNote } from \"../lib/tui.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\n\n// `saasaloy list [owner/repo[@ref]]` — enumerate the modules a registry offers, the same\n// seam `add`'s picker reads from. With no argument it lists the default repo (or the local\n// modules/ checkout when SAASALOY_REGISTRY_DIR is set); an `owner/repo` coordinate lists a\n// third-party registry. Names only — matching the picker — so it stays a cheap, one-call\n// lookup (no per-module descriptor fetch).\n//\n// Run inside a project, each name is marked installed or not, and anything installed the\n// registry no longer offers is listed under its own heading. Before #98 the output was a\n// flat list a user had to diff against saasaloy.json by hand.\n\ninterface Options {\n source?: string;\n /** Show only what this project has installed. */\n installed: boolean;\n /** Show only what the registry offers and this project has not installed. */\n available: boolean;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\"--installed\", \"--available\", \"--help\", \"-h\"]);\nconst USAGE = \"saasaloy list [<owner/repo[@ref]>] [--installed] [--available]\";\nconst HELP: CommandHelp = {\n name: \"list\",\n describe: DESCRIPTIONS.list,\n usage: USAGE,\n flags: {\n \"--installed\": \"list only the modules this project has installed\",\n \"--available\": \"list only the modules this project has not installed\",\n },\n};\n\nfunction parseArgs(argv: string[]): Options {\n const positional: string[] = [];\n const unknown: string[] = [];\n for (const arg of argv) {\n if (!arg.startsWith(\"-\")) {\n positional.push(arg);\n } else if (!KNOWN_FLAGS.has(arg)) {\n unknown.push(arg);\n }\n }\n unknown.push(...positional.slice(1));\n return {\n available: argv.includes(\"--available\"),\n installed: argv.includes(\"--installed\"),\n source: positional[0],\n unknown,\n };\n}\n\ninterface LocalProject {\n /** Modules `saasaloy add` applied here. */\n installed: Set<string>;\n /** The base app `init` scaffolded, which is not a module and is in no registry. */\n base?: string;\n}\n\n/**\n * What this project has, or nothing when `list` is run outside one. Listing a registry\n * from anywhere is the point of the command, so a missing `saasaloy.json` is not an error\n * here — it only means nothing can be marked.\n *\n * Test for the file rather than catching whatever `loadConfig` throws. A bare catch also\n * swallowed a corrupt or schema-invalid `saasaloy.json`, so a project whose config would\n * stop every other command read here as \"not in a project\" and quietly lost its installed\n * marks. Only absence is tolerated; a file that is there and will not load is reported\n * through the caller's own error path (#98).\n */\nasync function localProject(): Promise<LocalProject> {\n const root = await findProjectRoot();\n if (!(await pathExists(join(root, CONFIG_FILE)))) {\n return { installed: new Set() };\n }\n const config = await loadConfig(root);\n return {\n installed: new Set(config.installed),\n ...(config.base === undefined ? {} : { base: config.base }),\n };\n}\n\nexport async function runList(argv: string[]): Promise<number> {\n // Parse before help answers, so a typo'd flag alongside `--help` still reports the typo.\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n intro(pc.bgCyan(pc.black(\" saasaloy list \")));\n\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n if (opts.installed && opts.available) {\n cancel(\n `\\`--installed\\` and \\`--available\\` exclude each other — pass neither to see both.`\n );\n return EXIT_REFUSED;\n }\n\n let source: RegistrySource | undefined;\n try {\n const coord = parseCoordinate(opts.source);\n source = createRegistrySource(coord);\n if (process.env[REGISTRY_ENV] && (coord.owner || coord.repo)) {\n note(\n `Ignoring source \"${coord.owner}/${coord.repo}\" — ${REGISTRY_ENV} override is set.`,\n pc.yellow(\"Warning\")\n );\n }\n\n const project = await localProject();\n const installed = project.installed;\n const modules = await source.listModules();\n if (modules.length === 0) {\n note(`No modules found in ${source.label}.`, \"Registry\");\n outro(pc.dim(\"0 modules\"));\n return EXIT_OK;\n }\n\n const offeredAndInstalled = modules.filter((name) => installed.has(name));\n const offeredOnly = modules.filter((name) => !installed.has(name));\n // Installed here but absent from this registry: a module from another registry, or\n // one renamed upstream. Say so rather than omitting it. The base app is not in this\n // list — it is not a module, so no registry ever offers it.\n const elsewhere = [...installed]\n .filter((name) => !modules.includes(name))\n .toSorted();\n\n const shown: string[] = [];\n if (!opts.available) {\n shown.push(...offeredAndInstalled);\n }\n if (!opts.installed) {\n shown.push(...offeredOnly);\n }\n\n const lines = shown.map((name) =>\n installed.has(name)\n ? `${pc.green(\"✔\")} ${pc.cyan(name)} ${pc.dim(\"installed\")}`\n : `${pc.dim(\"·\")} ${pc.cyan(name)}`\n );\n if (lines.length === 0) {\n lines.push(\n pc.dim(\n opts.installed\n ? \"nothing from this registry is installed here\"\n : \"every module this registry offers is already installed\"\n )\n );\n }\n if (elsewhere.length > 0 && !opts.available) {\n lines.push(\n \"\",\n pc.dim(`installed but not in this registry: ${elsewhere.join(\", \")}`)\n );\n }\n if (project.base && !opts.available && !opts.installed) {\n lines.push(\n \"\",\n pc.dim(\n `base app: ${project.base} — scaffolded by \\`saasaloy init\\`, not a module`\n )\n );\n }\n\n note(\n wrapForNote(lines.join(\"\\n\")),\n `Modules ${pc.dim(`(${source.label})`)}`\n );\n const total = shown.length;\n outro(\n pc.dim(\n `${total} module${total === 1 ? \"\" : \"s\"}${\n opts.installed || opts.available\n ? \"\"\n : ` · ${offeredAndInstalled.length} installed`\n }`\n )\n );\n return EXIT_OK;\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n } finally {\n await source?.cleanup?.();\n }\n}\n","import { join, relative, resolve } from \"node:path\";\nimport {\n cancel,\n intro,\n isCancel,\n note,\n outro,\n select,\n text,\n} from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { checkTarget, resolveDoctorTarget } from \"../lib/doctor.js\";\nimport type { Finding } from \"../lib/doctor.js\";\nimport {\n EXIT_OK,\n EXIT_REFUSED,\n exitCodeFor,\n formatFailure,\n} from \"../lib/exit.js\";\nimport { pathExists } from \"../lib/fs-utils.js\";\nimport {\n isModuleType,\n MODULE_TYPES,\n nameProblem,\n parseDependsOn,\n REGISTRY_DIR,\n requiresRange,\n TYPE_HINTS,\n writeModule,\n} from \"../lib/new-module.js\";\nimport type { ModuleSpec, ModuleType } from \"../lib/new-module.js\";\nimport { findProjectRoot } from \"../lib/project.js\";\nimport { CONFIG_FILE } from \"../lib/saasaloy-config.js\";\nimport { isInteractive, wrapForNote } from \"../lib/tui.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { readVersion } from \"../version.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\n\n// `saasaloy new module <name>` — the registry author's scaffold (#50).\n//\n// Before this the only way to start a module was the `create-module` skill, which made\n// authoring a Claude Code feature rather than a CLI one. That is the wrong shape for a\n// registry meant to take third-party modules: the skeleton is mechanical, so the tool\n// writes it and the skill keeps the judgment about tiers, conventions and slices.\n//\n// It refuses inside a generated project. `modules/` in a consumer's repo means nothing —\n// a project installs modules, it does not host them — and a scaffold landing there is a\n// folder the user finds later with no idea what put it there.\n//\n// The noun is a positional rather than a second registry key, so `COMMANDS` stays the flat\n// map `cli.ts` renders help and the picker from. `module` is the only noun today.\n\n/** The nouns `new` knows. Named in the refusal, so adding one updates the message too. */\nconst NOUNS = [\"module\"] as const;\n\nexport interface Options {\n /** The noun — `module`. Absent when the picker handed off with an empty argv. */\n noun?: string;\n name?: string;\n /** `--type <tier>`: skips the two-tier prompt, for a non-interactive run. */\n type?: string;\n /** `--depends-on <a,b>`: skips the dependency prompt. */\n dependsOn?: string;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\"--help\", \"-h\"]);\nconst VALUE_FLAGS = new Set([\"--type\", \"--depends-on\"]);\nconst USAGE = \"saasaloy new module <name> [--type <tier>] [--depends-on <a,b>]\";\nconst HELP: CommandHelp = {\n name: \"new\",\n describe: DESCRIPTIONS.new,\n usage: USAGE,\n flags: {\n \"--type <tier>\": `${MODULE_TYPES.join(\" | \")} — skips the prompt`,\n \"--depends-on <a,b>\":\n \"comma-separated capabilities this module needs — skips the prompt\",\n },\n};\n\nexport function parseArgs(argv: string[]): Options {\n const positional: string[] = [];\n const unknown: string[] = [];\n const values: Record<string, string> = {};\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!;\n if (!arg.startsWith(\"-\")) {\n positional.push(arg);\n continue;\n }\n // `--type feature` and `--type=feature` both work, and a value flag with nothing\n // usable after it is a usage error rather than a silently empty tier — otherwise\n // `--type --depends-on api` scaffolds a module whose type is \"--depends-on\".\n const eq = arg.indexOf(\"=\");\n const flag = eq === -1 ? arg : arg.slice(0, eq);\n if (VALUE_FLAGS.has(flag)) {\n const next = eq === -1 ? argv[i + 1] : arg.slice(eq + 1);\n if (!next || (eq === -1 && next.startsWith(\"-\"))) {\n unknown.push(`${flag} (missing value)`);\n continue;\n }\n if (eq === -1) {\n i++;\n }\n values[flag] = next;\n continue;\n }\n if (!KNOWN_FLAGS.has(arg)) {\n unknown.push(arg);\n }\n }\n // Two positionals are the whole grammar: the noun and the name.\n unknown.push(...positional.slice(2));\n\n return {\n ...(positional[0] === undefined ? {} : { noun: positional[0] }),\n ...(positional[1] === undefined ? {} : { name: positional[1] }),\n ...(values[\"--type\"] === undefined ? {} : { type: values[\"--type\"] }),\n ...(values[\"--depends-on\"] === undefined\n ? {}\n : { dependsOn: values[\"--depends-on\"] }),\n unknown,\n };\n}\n\n/** One doctor finding as a line, in the shape `saasaloy doctor` prints. */\nexport function renderFindings(findings: Finding[]): string[] {\n return findings.map(\n (found) => `${pc.yellow(found.where)} ${pc.dim(found.message)}`\n );\n}\n\nexport async function runNew(argv: string[]): Promise<number> {\n // Parse before help answers, so a typo'd flag alongside `--help` still reports the typo.\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n intro(pc.bgCyan(pc.black(\" saasaloy new \")));\n\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n\n // The picker hands off an empty argv, and `module` is the only noun there is, so an\n // absent noun is the one the user meant rather than a question worth asking.\n const noun = opts.noun ?? NOUNS[0];\n if (!(NOUNS as readonly string[]).includes(noun)) {\n cancel(\n `\\`saasaloy new ${noun}\\` isn't a thing — the noun is one of: ${NOUNS.join(\", \")}.`\n );\n return EXIT_REFUSED;\n }\n\n try {\n // A generated project carries `saasaloy.json`, at its root or above the current\n // directory. `new module` authors a *registry* module, so landing one in a consumer's\n // repo would write a folder nothing there reads.\n const projectRoot = await findProjectRoot();\n if (await pathExists(join(projectRoot, CONFIG_FILE))) {\n cancel(\n `${CONFIG_FILE} is present at ${pc.cyan(projectRoot)} — this is a generated project, and ` +\n `\\`new module\\` authors a module for a registry. Run it in the registry repo instead. Nothing was written.`\n );\n return EXIT_REFUSED;\n }\n\n let name = opts.name;\n if (name === undefined) {\n if (!isInteractive()) {\n cancel(`No module name — usage: \\`${USAGE}\\`.`);\n return EXIT_REFUSED;\n }\n const answer = await text({\n message: \"Module name (lowercase, digits, hyphens)\",\n validate: (value) => nameProblem((value ?? \"\").trim()),\n });\n if (isCancel(answer)) {\n cancel(\"Cancelled — nothing written.\");\n return EXIT_REFUSED;\n }\n name = answer.trim();\n }\n\n const problem = nameProblem(name);\n if (problem) {\n cancel(`${problem} Nothing was written.`);\n return EXIT_REFUSED;\n }\n\n // Prove the folder is free before asking anything: a scaffold that merges into a\n // descriptor somebody wrote would overwrite it, and answering three prompts first\n // only to be refused wastes them.\n const registryDir = resolve(process.cwd(), REGISTRY_DIR);\n const dir = join(registryDir, name);\n if (await pathExists(dir)) {\n cancel(\n `${pc.cyan(`${REGISTRY_DIR}/${name}`)} already exists — pick another name, or edit it by hand. Nothing was written.`\n );\n return EXIT_REFUSED;\n }\n\n let type: ModuleType;\n if (opts.type !== undefined) {\n if (!isModuleType(opts.type)) {\n cancel(\n `\\`--type ${opts.type}\\` isn't a tier — use ${MODULE_TYPES.join(\" or \")}.`\n );\n return EXIT_REFUSED;\n }\n type = opts.type;\n } else if (isInteractive()) {\n const picked = await select({\n message: \"Which tier is this?\",\n options: MODULE_TYPES.map((value) => ({\n hint: TYPE_HINTS[value],\n label: value,\n value,\n })),\n });\n if (isCancel(picked)) {\n cancel(\"Cancelled — nothing written.\");\n return EXIT_REFUSED;\n }\n type = picked;\n } else {\n // No terminal to ask on: a prompt nobody can answer is a hang, and a hang in CI is\n // worse than a refusal that names the flag carrying the same answer.\n cancel(\n `No tier — pass ${MODULE_TYPES.map((tier) => `\\`--type ${tier}\\``).join(\" or \")} when there is no terminal to ask.`\n );\n return EXIT_REFUSED;\n }\n\n let dependsOn: string[] = [];\n if (opts.dependsOn !== undefined) {\n dependsOn = parseDependsOn(opts.dependsOn);\n } else if (isInteractive()) {\n const answer = await text({\n defaultValue: \"\",\n message: `Capabilities it depends on ${pc.dim(\"(comma-separated, blank for none)\")}`,\n placeholder: \"api, database\",\n });\n if (isCancel(answer)) {\n cancel(\"Cancelled — nothing written.\");\n return EXIT_REFUSED;\n }\n dependsOn = parseDependsOn(answer);\n }\n\n const requires = requiresRange(await readVersion());\n const spec: ModuleSpec = {\n dependsOn,\n name,\n type,\n ...(requires === undefined ? {} : { requires }),\n };\n const written = await writeModule(dir, spec);\n\n const shown = relative(process.cwd(), dir) || dir;\n note(\n wrapForNote(\n written\n .map((path) => `${pc.green(\"create\")} ${shown}/${path}`)\n .join(\"\\n\")\n ),\n \"Written\"\n );\n\n // Run the same checks `saasaloy doctor` runs, in-process. Shelling out to the binary\n // would need one installed, and the scaffold's whole promise is that what it writes\n // passes — proving it here means a change to either side is caught by the other.\n const reports = await checkTarget(await resolveDoctorTarget(dir));\n const findings = reports.flatMap((report) => report.findings);\n if (findings.length > 0) {\n // Reported, not refused: the files are on disk and correct enough to edit, and the\n // usual cause is a `dependsOn` naming a module this registry does not offer — the\n // author's own input, which they fix in the descriptor rather than by rerunning.\n note(wrapForNote(renderFindings(findings).join(\"\\n\")), \"Doctor\");\n outro(\n pc.yellow(\n `Scaffolded with ${findings.length} finding${findings.length === 1 ? \"\" : \"s\"} — fix ${shown}/registry-item.json, then \\`saasaloy doctor ${shown}\\`.`\n )\n );\n return EXIT_OK;\n }\n\n outro(\n pc.green(\n `${name} is ready — put its payload in ${shown}/files/ and write its runbook in ${shown}/skills/.`\n )\n );\n return EXIT_OK;\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n }\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { UNKNOWN_VERSION } from \"./cli-requires.js\";\nimport { parseVersion } from \"./semver.js\";\n\n// The scaffold behind `saasaloy new module <name>` (#50).\n//\n// Authoring a module was Claude-Code-only before this: the `create-module` skill knew the\n// shape of a descriptor and nothing else did, which locked out every contributor not\n// running that agent. The skeleton is mechanical — a descriptor, a `files/` folder, a\n// prefixed skill — so the CLI writes it and the skill keeps the part that is judgment.\n//\n// One scaffolder, not two. `create-module` now runs this command for the skeleton, so a\n// change to the shape lands here and both paths get it.\n//\n// Everything is pure up to `writeModule`, so the descriptor and the skill are asserted as\n// strings in a test with no directory to inspect afterwards.\n\n/** The two tiers a descriptor's `type` may name, in the order the picker offers them. */\nexport const MODULE_TYPES = [\n \"saasaloy:capability\",\n \"saasaloy:feature\",\n] as const;\n\nexport type ModuleType = (typeof MODULE_TYPES)[number];\n\n/** What each tier is, for the picker's hint and for the refusal that lists them. */\nexport const TYPE_HINTS: Record<ModuleType, string> = {\n \"saasaloy:capability\":\n \"scaffolds an app or package and establishes an extension point\",\n \"saasaloy:feature\":\n \"drops files into the conventions existing capabilities already establish\",\n};\n\nexport function isModuleType(value: string): value is ModuleType {\n return (MODULE_TYPES as readonly string[]).includes(value);\n}\n\n// The same pattern `registry-item.schema.json` puts on `name`. Checked before anything is\n// written, so a rejected name costs no directory to clean up.\nconst NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;\n\nexport function isValidModuleName(name: string): boolean {\n return NAME_PATTERN.test(name);\n}\n\n/** Why a name cannot be used, as a sentence, or `undefined` when it can. */\nexport function nameProblem(name: string): string | undefined {\n return isValidModuleName(name)\n ? undefined\n : `\"${name}\" isn't a module name — use lowercase letters, digits and hyphens, starting with a letter or digit.`;\n}\n\n/** Every module skill folder is prefixed, so two registries' skills never collide (ADR 0014). */\nexport const SKILL_PREFIX = \"saasaloy-\";\n\n/** The registry folder a module is written into, relative to the current directory. */\nexport const REGISTRY_DIR = \"modules\";\n\n/**\n * The `requires.saasaloy` range a freshly scaffolded descriptor declares, or `undefined`\n * when it should declare none.\n *\n * The floor is the running CLI's own minor, because that is the only version the author\n * has actually run the module against. A wider range would be a claim nobody checked.\n *\n * Two versions produce no range at all. `\"unknown\"` means the CLI could not read its own\n * package.json, and a floor invented from nothing would refuse every consumer. `0.0.x` is\n * the unpublished placeholder this repo still carries, and `>=0.0` constrains nothing, so\n * the field is left out rather than written as decoration.\n */\nexport function requiresRange(cliVersion: string): string | undefined {\n if (cliVersion === UNKNOWN_VERSION) {\n return undefined;\n }\n const parsed = parseVersion(cliVersion);\n if (!parsed || (parsed.major === 0 && parsed.minor === 0)) {\n return undefined;\n }\n return `>=${parsed.major}.${parsed.minor}`;\n}\n\nexport interface ModuleSpec {\n name: string;\n type: ModuleType;\n /** Capabilities this module needs first. Empty means the field is left out entirely. */\n dependsOn: string[];\n /** The `requires.saasaloy` range, from `requiresRange`. Omitted when undefined. */\n requires?: string;\n}\n\n/**\n * The descriptor as JSON text.\n *\n * Every optional field the scaffold has nothing to say about is left out rather than\n * written empty. `additionalProperties: false` means a placeholder is a lie the schema\n * cannot catch, and an author deleting `\"envVars\": {}` is work the scaffold created.\n * Key order follows the schema's own: identity, then requirements, then contents.\n */\nexport function renderDescriptor(spec: ModuleSpec): string {\n const item: Record<string, unknown> = {\n $schema: \"https://saasaloy.dev/schemas/registry-item.schema.json\",\n name: spec.name,\n type: spec.type,\n };\n if (spec.requires) {\n item.requires = { saasaloy: spec.requires };\n }\n if (spec.dependsOn.length > 0) {\n item.dependsOn = spec.dependsOn;\n }\n // The skill folder is scaffolded alongside, so the descriptor points at it from the\n // start — a module that ships a runbook nothing references is the easy thing to forget.\n item.agent = { skills: [`skills/${SKILL_PREFIX}${spec.name}`] };\n return `${JSON.stringify(item, null, 2)}\\n`;\n}\n\nconst TIER_WORD: Record<ModuleType, string> = {\n \"saasaloy:capability\": \"capability\",\n \"saasaloy:feature\": \"feature\",\n};\n\n/**\n * The skill stub, in the frontmatter shape every module's `SKILL.md` already uses: a\n * `name` identical to the folder, and a `description` saying when to load it. The body is\n * a checklist of the sections a module runbook owes its reader, because a stub with\n * headings and no prose is easier to finish than a blank page.\n */\nexport function renderSkill(spec: ModuleSpec): string {\n const tier = TIER_WORD[spec.type];\n const depends =\n spec.dependsOn.length > 0\n ? `It depends on ${spec.dependsOn.join(\", \")}, which \\`add\\` resolves and installs first.`\n : `It declares no \\`dependsOn\\` yet — add every capability it needs before publishing.`;\n return `---\nname: ${SKILL_PREFIX}${spec.name}\ndescription: Runbook for the ${spec.name} module. Use when adding, changing, or debugging ${spec.name} in a Saasaloy project. TODO — describe what this module does and name the files and settings it owns, so the agent loads this on the right task.\n---\n\n# ${spec.name} — TODO one-line summary\n\n\\`${spec.name}\\` is a **${tier} module** (\\`${spec.type}\\`). ${depends}\n\nTODO — say in two sentences what a consumer gets from installing it.\n\n## What it installs\n\nTODO — list each file this module writes and where it lands. Keep it in step with\n\\`registry-item.json\\`'s \\`files[]\\`; this table is what a reader checks before editing one.\n\n| File | Lands at |\n| --- | --- |\n| TODO | TODO |\n\n## Wire-up\n\nRequired only when this module targets \\`@ui/blocks/\\`, because \\`add\\` writes the block and\nnever edits a page. Name four things: the file to edit, the import line verbatim, the tag\nwith its client directive, and a suggested anchor marked as a suggestion. Delete this\nsection when the module ships no block.\n\n## Settings\n\nTODO — every key in \\`envVars\\`, what it is for, and what \\`saasaloy env\\` does with it.\nDelete this section when the module declares none.\n\n## What \\`remove\\` leaves behind\n\nTODO — anything the user has to undo by hand, such as an import they added during wire-up.\n`;\n}\n\n/** One file the scaffold writes: a module-relative POSIX path and its content. */\nexport interface ScaffoldedFile {\n path: string;\n content: string;\n}\n\n/**\n * Every file the scaffold writes, in the order it writes them. Exported apart from the\n * write so the layout is asserted without a temp directory.\n *\n * `files/.gitkeep` is deliberate: the folder is where the payload goes, and git will not\n * carry an empty directory, so the author would otherwise have to create it themselves\n * before the first file.\n */\nexport function moduleFiles(spec: ModuleSpec): ScaffoldedFile[] {\n return [\n { content: renderDescriptor(spec), path: \"registry-item.json\" },\n { content: \"\", path: \"files/.gitkeep\" },\n {\n content: renderSkill(spec),\n path: `skills/${SKILL_PREFIX}${spec.name}/SKILL.md`,\n },\n ];\n}\n\n/**\n * Write the scaffold under `dir` and return the paths written, module-relative.\n *\n * The caller proves `dir` does not exist first: this refuses to merge into a module folder\n * that is already there, since overwriting a descriptor somebody wrote is not a scaffold.\n */\nexport async function writeModule(\n dir: string,\n spec: ModuleSpec\n): Promise<string[]> {\n const files = moduleFiles(spec);\n for (const file of files) {\n const abs = join(dir, ...file.path.split(\"/\"));\n await mkdir(dirname(abs), { recursive: true });\n await writeFile(abs, file.content, \"utf-8\");\n }\n return files.map((file) => file.path);\n}\n\n/**\n * Split a `--depends-on` value into module names. Both `a,b` and `a, b` are accepted, and\n * an empty segment is dropped rather than becoming a `dependsOn` entry named `\"\"` that\n * doctor then reports as a module the registry does not offer.\n */\nexport function parseDependsOn(raw: string): string[] {\n return raw\n .split(\",\")\n .map((name) => name.trim())\n .filter((name) => name !== \"\");\n}\n","import { cancel, intro, log, note, outro } from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport {\n EXIT_OK,\n EXIT_REFUSED,\n exitCodeFor,\n formatFailure,\n} from \"../lib/exit.js\";\nimport { BASE_MODULE, missingBaseTargets, templateHash } from \"../lib/base.js\";\nimport { loadLock } from \"../lib/lock.js\";\nimport { loadManifest } from \"../lib/manifest.js\";\nimport { findProjectRoot } from \"../lib/project.js\";\nimport { REGISTRY_ENV, RemoteRegistrySource } from \"../lib/registry.js\";\nimport type { RegistrySource } from \"../lib/registry.js\";\nimport { loadConfig } from \"../lib/saasaloy-config.js\";\nimport { baseTemplateDir } from \"../lib/scaffold.js\";\nimport { wrapForNote } from \"../lib/tui.js\";\nimport { compareBase, compareInstalled } from \"../lib/updater.js\";\nimport type { ModuleComparison, UpdateStatus } from \"../lib/updater.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { readVersion } from \"../version.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\n\n// `saasaloy outdated` — \"has anything moved?\", answered without touching a file (#50).\n//\n// The lock already records the exact SHA every installed module resolved to, so the\n// answer is one `compareInstalled` call away — the same call `update` opens with. Before\n// this command there was no way to ask, which made `update` a thing you ran speculatively\n// and then read a merge plan to find out it had nothing to do.\n//\n// It is a report, so a bare run exits 0 whatever it finds: \"three modules moved\" is news,\n// not a failure. `--check` is the CI form — it exits 2 when anything is outdated, so a\n// pipeline can gate on drift without parsing the table.\n//\n// An unreachable source is a row, never a throw. One dead repo must not hide the state of\n// every other module, and it does not fail `--check` either: a network blip is not drift.\n//\n// The base template is the first row (#120): the lock's `templateHash` against the one\n// the running CLI ships, with the two CLI versions as labels. An `untracked` base — a\n// project scaffolded before the record existed — is news, not drift, and `update` is\n// what records it; this command never writes.\n\nexport interface Options {\n /** `--check`: exit non-zero when any module has moved, for CI. */\n check: boolean;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\"--check\", \"--help\", \"-h\"]);\nconst USAGE = \"saasaloy outdated [--check]\";\nconst HELP: CommandHelp = {\n name: \"outdated\",\n describe: DESCRIPTIONS.outdated,\n usage: USAGE,\n flags: {\n \"--check\":\n \"exit non-zero when the base template or any module has moved (for CI)\",\n },\n};\n\nexport function parseArgs(argv: string[]): Options {\n const unknown: string[] = [];\n for (const arg of argv) {\n // The report covers every installed module; there is no single-module form to name.\n if (!arg.startsWith(\"-\") || !KNOWN_FLAGS.has(arg)) {\n unknown.push(arg);\n }\n }\n return { check: argv.includes(\"--check\"), unknown };\n}\n\nconst SHA_PATTERN = /^[0-9a-f]{40}$/i;\n\n/** A 40-hex value is a commit; anything else (`local`, `unknown`) is printed as it stands. */\nfunction short(value: string): string {\n return SHA_PATTERN.test(value) ? value.slice(0, 7) : value;\n}\n\n// Colour carries the same meaning it does in `update`'s summary: green is settled, yellow\n// is work waiting, red is a question the tool could not answer, dim is deliberately frozen.\nconst STATUS_COLOR: Record<UpdateStatus, (text: string) => string> = {\n current: pc.green,\n outdated: pc.yellow,\n pinned: pc.dim,\n local: pc.dim,\n unresolvable: pc.red,\n untracked: pc.yellow,\n};\n\nconst HEADERS = [\"MODULE\", \"STATUS\", \"REF\", \"CURRENT\", \"LATEST\"] as const;\n\nfunction cells(comparison: ModuleComparison): string[] {\n return [\n comparison.name,\n comparison.status,\n short(comparison.ref),\n short(comparison.current),\n short(comparison.latest),\n ];\n}\n\n/**\n * The table, one row per module, plus an indented note under every row carrying a\n * `detail` — which is every status but `current` and `outdated`, so a `local` entry says\n * it was skipped and an unreachable one says why.\n *\n * Pure and exported so the renderer is tested against a stubbed comparison covering all\n * five statuses, with no network and no project on disk. Alignment is measured on the\n * plain strings and colour applied after, or the escape codes would count as width.\n */\nexport function renderComparisons(comparisons: ModuleComparison[]): string[] {\n if (comparisons.length === 0) {\n return [pc.dim(\"No modules installed — `saasaloy add <module>` first.\")];\n }\n\n const rows = comparisons.map(cells);\n const widths = HEADERS.map((header, column) =>\n Math.max(header.length, ...rows.map((row) => row[column]?.length ?? 0))\n );\n const pad = (text: string, column: number): string =>\n text.padEnd(widths[column] ?? 0);\n\n const lines = [\n pc.dim(\n HEADERS.map((header, column) => pad(header, column))\n .join(\" \")\n .trimEnd()\n ),\n ];\n for (const [index, row] of rows.entries()) {\n const comparison = comparisons[index]!;\n const color = STATUS_COLOR[comparison.status];\n lines.push(\n [\n pc.cyan(pad(row[0]!, 0)),\n color(pad(row[1]!, 1)),\n pc.dim(pad(row[2]!, 2)),\n pad(row[3]!, 3),\n comparison.status === \"outdated\"\n ? pc.yellow(pad(row[4]!, 4))\n : pad(row[4]!, 4),\n ]\n .join(\" \")\n .trimEnd()\n );\n if (comparison.detail) {\n // A leading-space indent would not survive `wrapForNote`, which splits on spaces\n // and drops the empty leading words. A glyph carries the \"belongs to the row above\"\n // relationship through the wrap.\n lines.push(pc.dim(` ↳ ${comparison.detail}`));\n }\n }\n return lines;\n}\n\n/**\n * How many rows moved, the base included. Only `outdated` counts: a pinned module is\n * frozen on purpose, a local one has no commit to compare, an unresolvable one is a\n * failure to answer rather than an answer, and an untracked base has nothing to compare\n * yet — gating CI on any of the four would fail builds for no drift.\n */\nexport function countDrift(comparisons: ModuleComparison[]): number {\n return comparisons.filter((c) => c.status === \"outdated\").length;\n}\n\n/** The closing line for a report with drift: which of the base and the modules moved. */\nexport function describeDrift(comparisons: ModuleComparison[]): string {\n const baseMoved = comparisons.some(\n (c) => c.name === BASE_MODULE && c.status === \"outdated\"\n );\n const modules = comparisons.filter(\n (c) => c.name !== BASE_MODULE && c.status === \"outdated\"\n ).length;\n const moduleText = `${modules} module${modules === 1 ? \"\" : \"s\"}`;\n const subject = baseMoved\n ? modules > 0\n ? `The base template and ${moduleText}`\n : \"The base template\"\n : moduleText;\n return `${subject} moved — run \\`saasaloy update\\` to apply.`;\n}\n\n/**\n * Rewrite the rows a registry override produced so they read `local`.\n *\n * `compareInstalled` marks every override row `outdated`, because that is what the word\n * means to `update`: re-apply the module from the checkout. `outdated` asks a different\n * question, and a working copy has no commit to compare against, so the honest answer is\n * `local` — the same answer a locally installed module gets without the override. Left as\n * `outdated`, every row counted as drift and `--check` exited 2 with nothing moved.\n *\n * Rows the override never touched (a module with no lock entry stays `unresolvable`) are\n * returned as they came.\n */\nexport function asLocalRows(\n comparisons: ModuleComparison[]\n): ModuleComparison[] {\n return comparisons.map((comparison) =>\n comparison.status === \"outdated\"\n ? { ...comparison, status: \"local\" as const }\n : comparison\n );\n}\n\nfunction splitSlug(slug: string): [string, string] | undefined {\n const parts = slug.split(\"/\");\n return parts.length === 2 && parts[0] && parts[1]\n ? [parts[0], parts[1]]\n : undefined;\n}\n\nexport async function runOutdated(argv: string[]): Promise<number> {\n // Parse before help answers, so a typo'd flag alongside `--help` still reports the typo.\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n intro(pc.bgCyan(pc.black(\" saasaloy outdated \")));\n\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n\n const sources: RegistrySource[] = [];\n try {\n const root = await findProjectRoot();\n const config = await loadConfig(root);\n const lock = await loadLock(root);\n const manifest = await loadManifest(root);\n const registryOverride = !!process.env[REGISTRY_ENV];\n if (registryOverride && config.installed.length > 0) {\n log.warn(\n `${REGISTRY_ENV} is set, so every module reads as local — unset it to compare against the registry.`\n );\n }\n\n // The base first: it ships with the CLI, so its comparison needs no network and no\n // registry, and it is the one row every project has.\n const templateDir = await baseTemplateDir();\n const baseRow = compareBase({\n lock,\n manifest,\n runningHash: await templateHash(templateDir),\n runningVersion: await readVersion(),\n missingTargets: await missingBaseTargets(root, manifest, templateDir),\n });\n\n // One source per (repo, ref), the same cache `update` keeps, so a bare run resolves\n // each ref once however many modules share it.\n const resolvers = new Map<string, RemoteRegistrySource>();\n const remote = (slug: string, ref: string): RemoteRegistrySource => {\n const key = `${slug}@${ref}`;\n const cached = resolvers.get(key);\n if (cached) {\n return cached;\n }\n const parts = splitSlug(slug);\n if (!parts) {\n throw new Error(\n `Lock entry source \"${slug}\" isn't an owner/repo coordinate.`\n );\n }\n const source = new RemoteRegistrySource(parts[0], parts[1], ref);\n resolvers.set(key, source);\n sources.push(source);\n return source;\n };\n\n let moduleRows: ModuleComparison[] = [];\n if (config.installed.length > 0) {\n const compared = await compareInstalled({\n installed: config.installed,\n lock,\n registryOverride,\n resolveRef: (_name, entry, ref) =>\n remote(entry.source, ref).resolveSha(),\n });\n moduleRows = registryOverride ? asLocalRows(compared) : compared;\n }\n\n note(wrapForNote(renderComparisons([baseRow]).join(\"\\n\")), \"Base\");\n note(wrapForNote(renderComparisons(moduleRows).join(\"\\n\")), \"Modules\");\n\n const comparisons = [baseRow, ...moduleRows];\n const drift = countDrift(comparisons);\n if (drift === 0) {\n if (registryOverride && moduleRows.length > 0) {\n // Nothing was compared, so there is nothing to gate on — `--check` exits 0 too.\n outro(pc.dim(\"Nothing to compare — every module reads as local.\"));\n } else if (baseRow.status === \"untracked\") {\n outro(\n pc.yellow(\n \"Modules are up to date; the base is untracked — run `saasaloy update` to record it.\"\n )\n );\n } else {\n outro(pc.green(\"Everything is up to date.\"));\n }\n return EXIT_OK;\n }\n\n const summary = describeDrift(comparisons);\n // `--check` is the only thing that turns news into a non-zero exit. A bare run is a\n // report, and a report that failed the shell would be unusable interactively.\n if (opts.check) {\n cancel(summary);\n return EXIT_REFUSED;\n }\n outro(pc.yellow(summary));\n return EXIT_OK;\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n } finally {\n for (const source of sources) {\n await source.cleanup?.();\n }\n }\n}\n","import { mkdir, readFile, rm as rmPath, writeFile } from \"node:fs/promises\";\nimport { dirname, posix } from \"node:path\";\nimport {\n buildPlan,\n executePlan,\n listModuleFiles,\n previewPatches,\n} from \"./applier.js\";\nimport type { LinkAction, Plan, PlannedLink, PlannedPatch } from \"./applier.js\";\nimport {\n classifyLink,\n createDirLink,\n hashContent,\n pathExists,\n readIfPresent,\n resolveWithinRoot,\n} from \"./fs-utils.js\";\nimport { BASE_MODULE, isBaseTracked } from \"./base.js\";\nimport type { LockBase, Lockfile, LockModule } from \"./lock.js\";\nimport { samePatchEntry } from \"./manifest.js\";\nimport type { Manifest, ManifestPatch } from \"./manifest.js\";\nimport { applyPatch } from \"./patch/index.js\";\nimport { parseDep, readRootPackageJson, writeDeps } from \"./pkg-json.js\";\nimport type { DepChange, PackageJson } from \"./pkg-json.js\";\nimport type { LoadedModule } from \"./registry.js\";\nimport { classifyTrackedFile } from \"./remover.js\";\nimport type { FileRemoveAction } from \"./remover.js\";\nimport type { RegistryPatch, SaasaloyConfig } from \"./schema.js\";\n\n// The deterministic core of `saasaloy update` (issue #48), mirroring the buildPlan/\n// executePlan split of applier.ts and remover.ts. Two things live here:\n//\n// compareInstalled — \"did anything move?\", derived from the lockfile alone. Exported\n// on its own because `saasaloy outdated` (#50) is exactly this and nothing else.\n// buildUpdatePlan / executeUpdatePlan — the three-way classification the merge plan\n// is built from: *base* (the module at the lock's old SHA), *theirs* (at the new\n// SHA) and *mine* (what's on disk). The lock's `resolved` SHA is what makes a real\n// merge base reachable at all (ADR 0006, ADR 0012).\n//\n// Nothing here fetches: the command hands over already-loaded module folders, which is\n// what keeps this whole file testable offline.\n\n/** The command named in the summary and the merge plan's Verification section — never run (decision 16). */\nexport const VERIFY_COMMAND = \"pnpm typecheck\";\n\n/** Surfaced when the update moved a db schema file — never run, never applied (decision 12). */\nexport const MIGRATION_COMMAND = \"pnpm --filter @repo/db db:generate\";\n\n/** The capability that owns migrations; without it there is nothing to regenerate. */\nconst DATABASE_MODULE = \"database\";\nconst DB_ALIAS = \"@db\";\nconst DB_SCHEMA_FALLBACK = \"packages/db/src/schema/\";\n\n// --- Phase 1: did anything move? -------------------------------------------------\n\n/**\n * Where an installed module stands against its registry:\n * current — the ref still resolves to the SHA the lock recorded\n * outdated — the ref moved; there is an update to apply\n * pinned — the lock's `ref` is itself a SHA, so it is frozen by definition\n * local — installed from a working copy; nothing to re-resolve\n * unresolvable — no lock entry, or the source couldn't be reached\n * untracked — the base only: no record to compare, `update` adopts it (#120)\n */\nexport type UpdateStatus =\n \"current\" | \"outdated\" | \"pinned\" | \"local\" | \"unresolvable\" | \"untracked\";\n\nexport interface ModuleComparison {\n name: string;\n /** `owner/repo`, or `local`. */\n source: string;\n /** The ref that was resolved — the lock's, or `--ref`'s override. */\n ref: string;\n /** The SHA the lock records today. */\n current: string;\n /** The SHA the ref resolves to now; equal to `current` when nothing moved. */\n latest: string;\n status: UpdateStatus;\n /** Why, for everything that isn't a plain current/outdated — shown in the summary. */\n detail?: string;\n /**\n * `--ref` named a ref the lock isn't tracking, but it resolves to the SHA already\n * recorded — so there are no files to update and the lock's `ref` still has to move.\n * Without this a `--ref v2` onto a pin whose tag hasn't moved yet would report\n * \"already at <sha7>\" and silently leave the module pinned forever (criterion 3).\n */\n refRewrite?: boolean;\n}\n\nexport interface CompareInstalledArgs {\n /** Modules to compare — `saasaloy.json`'s `installed`, or the one module named. */\n installed: string[];\n lock: Lockfile;\n /** Resolve a module's ref to a commit SHA. Rejecting marks the module unresolvable. */\n resolveRef: (name: string, entry: LockModule, ref: string) => Promise<string>;\n /** `--ref <ref>`: the explicit unpin. Only ever set alongside a single named module. */\n overrideRef?: string;\n /** True when SAASALOY_REGISTRY_DIR is set — a working copy replaces the registry. */\n registryOverride?: boolean;\n}\n\nconst SHA_PATTERN = /^[0-9a-f]{40}$/i;\n\n/** A 40-hex `ref` is a pin, not a moving target — the lock records where it points forever. */\nfunction isSha(ref: string): boolean {\n return SHA_PATTERN.test(ref);\n}\n\nfunction short(sha: string): string {\n return isSha(sha) ? sha.slice(0, 7) : sha;\n}\n\n/**\n * Compare every installed module's lock entry against what its ref resolves to now.\n * Pure but for the injected `resolveRef`, so `outdated` (#50) can call it with the same\n * resolver and render a report instead of applying anything. Never throws: an\n * unreachable source becomes an `unresolvable` row so one dead repo can't block the rest\n * (decision 14).\n */\nexport async function compareInstalled(\n args: CompareInstalledArgs\n): Promise<ModuleComparison[]> {\n const { installed, lock, resolveRef, overrideRef, registryOverride } = args;\n const out: ModuleComparison[] = [];\n\n for (const name of installed) {\n const entry = lock.modules[name];\n if (!entry) {\n out.push({\n name,\n source: \"unknown\",\n ref: \"unknown\",\n current: \"unknown\",\n latest: \"unknown\",\n status: \"unresolvable\",\n detail:\n \"no lock entry — reinstall it with `saasaloy add` to record its provenance\",\n });\n continue;\n }\n\n // A working copy has no commit identity, so there is no base and nothing to\n // re-resolve — the update re-applies whatever the checkout holds (decision 3).\n if (registryOverride) {\n out.push({\n name,\n source: \"local\",\n ref: \"local\",\n current: entry.resolved,\n latest: \"local\",\n status: \"outdated\",\n detail: \"local install — no merge base\",\n });\n continue;\n }\n\n if (entry.source === \"local\" || entry.resolved === \"local\") {\n out.push({\n name,\n source: entry.source,\n ref: entry.ref,\n current: entry.resolved,\n latest: entry.resolved,\n status: \"local\",\n detail:\n \"installed from a working copy — set SAASALOY_REGISTRY_DIR to update it\",\n });\n continue;\n }\n\n // Pinned by definition: the ref *is* the SHA, so re-resolving it can only return\n // itself. `--ref <branch|tag>` is the sanctioned way off it (decision 10).\n if (isSha(entry.ref) && !overrideRef) {\n out.push({\n name,\n source: entry.source,\n ref: entry.ref,\n current: entry.resolved,\n latest: entry.resolved,\n status: \"pinned\",\n detail: `pinned at ${short(entry.ref)} — nothing to update (use \\`--ref <branch|tag>\\` to move it)`,\n });\n continue;\n }\n\n const ref = overrideRef ?? entry.ref;\n try {\n const latest = await resolveRef(name, entry, ref);\n const status: UpdateStatus =\n latest === entry.resolved ? \"current\" : \"outdated\";\n out.push({\n name,\n source: entry.source,\n ref,\n current: entry.resolved,\n latest,\n status,\n // An `outdated` module rewrites `ref` as part of moving `resolved`; only a\n // `current` one needs the ref move called out on its own.\n ...(status === \"current\" && ref !== entry.ref\n ? { refRewrite: true }\n : {}),\n });\n } catch (error) {\n out.push({\n name,\n source: entry.source,\n ref,\n current: entry.resolved,\n latest: entry.resolved,\n status: \"unresolvable\",\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n return out;\n}\n\n/** What the base row prints where a module prints its `owner/repo` and its ref. */\nexport const BASE_SOURCE = \"bundled template\";\nexport const BASE_REF = \"template\";\n\nexport interface CompareBaseArgs {\n lock: Lockfile;\n manifest: Manifest;\n /** `templateHash()` of the template the running CLI ships. */\n runningHash: string;\n /** `readVersion()` of the running CLI. */\n runningVersion: string;\n /**\n * Tracked base targets that are not on disk — `missingBaseTargets()`. They make the base\n * `outdated` on their own, because the template hash cannot see them (#120).\n */\n missingTargets?: readonly string[];\n}\n\n/** `0.2.0 (bbbbbbb)` — the version is the label, the hash is the verdict (#120). */\nfunction baseLabel(version: string, hash: string): string {\n return `${version} (${hash.slice(0, 7)})`;\n}\n\n/**\n * The base's row beside `compareInstalled`'s (#120). The base ships inside the CLI, so\n * its comparison is the lock's `templateHash` against the running CLI's, with the two\n * `cliVersion`s as labels: `current` when the hashes agree, `outdated` when they differ,\n * `untracked` when the project carries no usable record. Never `local`: the template has\n * no working-copy override, and a registry override says nothing about it.\n *\n * A tracked file missing from disk also makes the base `outdated`, at an unchanged hash.\n * `adoptBase` records an absent template file so the next update restores it, and the hash\n * alone would report `current` on the same CLI and never restore anything.\n */\nexport function compareBase(args: CompareBaseArgs): ModuleComparison {\n const { lock, manifest, runningHash, runningVersion } = args;\n const missing = args.missingTargets ?? [];\n const latest = baseLabel(runningVersion, runningHash);\n if (!isBaseTracked(lock, manifest) || !lock.base) {\n return {\n name: BASE_MODULE,\n source: BASE_SOURCE,\n ref: BASE_REF,\n current: \"untracked\",\n latest,\n status: \"untracked\",\n detail:\n \"not recorded — run `saasaloy update` to adopt the base at this CLI\",\n };\n }\n const moved = lock.base.templateHash !== runningHash;\n return {\n name: BASE_MODULE,\n source: BASE_SOURCE,\n ref: BASE_REF,\n current: baseLabel(lock.base.cliVersion, lock.base.templateHash),\n latest,\n status: moved || missing.length > 0 ? \"outdated\" : \"current\",\n // Say why, or an unmoved hash beside `outdated` reads as a bug.\n ...(!moved && missing.length > 0\n ? {\n detail: `${missing.length} tracked file${missing.length === 1 ? \" is\" : \"s are\"} missing from disk`,\n }\n : {}),\n };\n}\n\n/**\n * Move the lock's `ref` for modules whose ref changed but whose SHA didn't — the\n * `--ref <branch|tag>` unpin of a module the tag already points at. There is nothing to\n * apply, so no update plan is ever built for these; without this the unpin would be\n * dropped on the floor and the module would stay pinned (criterion 3). `resolved` is\n * untouched by construction: it already holds the SHA the new ref resolves to.\n * Returns the names actually rewritten.\n */\nexport function recordRefRewrites(\n lock: Lockfile,\n comparisons: ModuleComparison[]\n): string[] {\n const rewritten: string[] = [];\n for (const comparison of comparisons) {\n if (!comparison.refRewrite) {\n continue;\n }\n const entry = lock.modules[comparison.name];\n // Same guard executeUpdatePlan uses: a comparison against a different source isn't\n // describing this entry's ref.\n if (\n !entry ||\n entry.ref === comparison.ref ||\n entry.source !== comparison.source\n ) {\n continue;\n }\n entry.ref = comparison.ref;\n rewritten.push(comparison.name);\n }\n return rewritten;\n}\n\n// --- Phase 2: classify every managed file three ways ------------------------------\n\n/**\n * How a module file at the new SHA relates to base and to disk:\n * skip — base === theirs: the module didn't touch it, so neither do we,\n * drift or not (the single most important rule here)\n * create — the new version adds it and nothing occupies the path\n * overwrite — tracked and untouched since it was applied → safe deterministic write\n * restore — tracked but missing from disk → put it back (decision 6)\n * unchanged — disk already holds exactly what the new version ships\n * drift — tracked and hand-edited → merge plan, never written\n * conflict — the new version adds a file that already exists untracked → merge\n * plan, rendered two-way (decision 13)\n * delete — the new version dropped it and disk still matches → remove it\n * delete-drift — dropped upstream but hand-edited → keep it, say so (decision 5)\n * delete-missing — dropped upstream and already gone → just untrack\n */\nexport type UpdateFileAction =\n | \"skip\"\n | \"create\"\n | \"overwrite\"\n | \"restore\"\n | \"unchanged\"\n | \"drift\"\n | \"conflict\"\n | \"delete\"\n | \"delete-drift\"\n | \"delete-missing\";\n\n/** `remove`'s verdicts for a tracked file, said in `update`'s vocabulary (same classifier). */\nconst DELETE_ACTION: Record<FileRemoveAction, UpdateFileAction> = {\n delete: \"delete\",\n drift: \"delete-drift\",\n missing: \"delete-missing\",\n};\n\n/** Actions safe to write without a human in the loop (the clean path, spec §2.13). */\nconst WRITABLE: ReadonlySet<UpdateFileAction> = new Set<UpdateFileAction>([\n \"create\",\n \"overwrite\",\n \"restore\",\n \"unchanged\",\n]);\n\n/** Actions that route a file into the merge plan rather than to disk. */\nconst NEEDS_MERGE: ReadonlySet<UpdateFileAction> = new Set<UpdateFileAction>([\n \"drift\",\n \"conflict\",\n \"delete-drift\",\n]);\n\nexport interface PlannedUpdateFile {\n module: string;\n /** Module-relative POSIX source path — the manifest's `from`. */\n from: string;\n /** Project-relative POSIX path (manifest key + display). */\n target: string;\n /** Absolute destination path, validated to sit inside the project root. */\n targetAbs: string;\n action: UpdateFileAction;\n /** Content at the lock's old SHA; absent when the module didn't ship it then, or there's no base. */\n base?: string;\n /** Content at the new SHA; absent when the new version dropped the file. */\n theirs?: string;\n /** Content on disk right now; absent when the file is missing. */\n mine?: string;\n /** sha256 of `theirs` — what the manifest records once written. */\n newHash?: string;\n /**\n * Modules whose config patches also wrote to this file. A patched file's manifest\n * hash is deliberately not re-recorded (the patch belongs to another module, see\n * applier.ts), so it classifies as drift on the next update — this says so, rather\n * than letting the merge plan imply a human edited it. `remove` reverses those patches\n * from the manifest record itself (#36); `update` still has no hash to compare against.\n */\n patchedBy?: string[];\n}\n\n/**\n * A config patch bound to a concrete target file, previewed. Identical to `add`'s\n * `PlannedPatch` because one helper builds both (#98): `apply` writes it, `unchanged` is\n * a no-op, `missing` has no target file, and `matched` is the collision that routes it\n * into the merge plan (decision 1).\n */\nexport type PlannedUpdatePatch = PlannedPatch;\n\n/** A pin the module owns that moved between the two descriptor revisions (decision 8). */\nexport interface DepBump {\n name: string;\n from: string;\n to: string;\n /** True when the pin lives in package.json's `devDependencies` — the bucket it is rewritten in. */\n dev: boolean;\n}\n\nexport interface ModuleUpdatePlan {\n name: string;\n comparison: ModuleComparison;\n /** Why there is no merge base, when there isn't one — stamped on the merge plan (decision 3). */\n noMergeBase?: string;\n /** Commit subjects touching the module between the two SHAs (decision 4); `[]` when unavailable. */\n intent: string[];\n /** Every file the new version ships, classified. */\n files: PlannedUpdateFile[];\n /** Every file the new version dropped, classified. */\n removals: PlannedUpdateFile[];\n links: PlannedLink[];\n patches: PlannedUpdatePatch[];\n depAdds: DepChange[];\n devDepAdds: DepChange[];\n depBumps: DepBump[];\n /** Human-readable version disagreements, in `planDeps`'s existing phrasing. */\n depConflicts: string[];\n /** New `dependsOn` prerequisites this version introduces (decision 11). */\n prereqNames: string[];\n /** An ordinary `add` plan for those prerequisites, executed under the same confirmation. */\n prereqPlan?: Plan;\n /** Each prerequisite's own `dependsOn`, so its lock entry stays self-describing. */\n prereqDependsOn: Record<string, string[]>;\n /** Each prerequisite's own `conflictsWith`, for the same reason. */\n prereqConflictsWith: Record<string, string[]>;\n /** The new version's own `dependsOn`, written into the lock entry. */\n dependsOn?: string[];\n /**\n * The new version's own `conflictsWith`, written into the lock entry. The descriptor is\n * gone once the update lands, so the lock is the only offline record of what this\n * module refuses to sit beside — drop it here and the next `add` stops refusing a\n * second driver (#98).\n */\n conflictsWith?: string[];\n /** Removal warnings from the new descriptor, persisted only after a clean update. */\n removeWarnings: string[];\n /**\n * Env vars the new version requires that the old one did not — named before the\n * confirmation, or a version that starts requiring a secret updates in silence. With no\n * merge base there is nothing to diff against, so the whole set is reported.\n */\n newEnvVars: Record<string, string>;\n /** True when anything about this module routes to the merge plan. */\n needsMerge: boolean;\n /**\n * Set on the base's plan only (#120): the lock `base` record a clean run writes in place\n * of a `modules` entry. Its presence is also what the merge plan reads to render the\n * section as the template rather than a registry module.\n */\n baseRecord?: LockBase;\n}\n\nexport interface UpdatePlan {\n modules: ModuleUpdatePlan[];\n /** Comparisons that won't be applied (current/pinned/local/unresolvable) — for the summary. */\n skipped: ModuleComparison[];\n /** Installed modules with no lock entry — reported, never fatal (mirrors remover.ts). */\n missingLockEntries: string[];\n /** Set when a db schema file moved and `database` is installed (decision 12). */\n migrationCommand?: string;\n /** Always `pnpm typecheck` — named, never executed (decision 16). */\n verifyCommand: string;\n /** True when any module needs a merge — i.e. a merge plan document is worth emitting. */\n needsMerge: boolean;\n}\n\n/** One module's two revisions plus everything the command learned while fetching them. */\nexport interface ModuleUpdateInput {\n comparison: ModuleComparison;\n /** The module as it exists at the new SHA. */\n theirs: LoadedModule;\n /** The module at the lock's old SHA; absent when the base can't be reached. */\n base?: LoadedModule;\n /** Why `base` is absent — `local install`, `force-pushed branch`, … (decision 3). */\n noMergeBase?: string;\n /** Commit subjects touching `modules/<name>/` between the two SHAs. */\n intent?: string[];\n /** Descriptors for prerequisites the new version introduces, topologically ordered. */\n prereqs?: { order: string[]; modules: Map<string, LoadedModule> };\n /**\n * Manifest entries this module owns that the run must leave alone — the base's seed\n * files (#120). They are neither classified nor treated as dropped when `theirs` no\n * longer ships them.\n */\n ignoreTargets?: ReadonlySet<string>;\n /**\n * Recorded patches other modules applied to files `theirs` ships, re-applied after an\n * overwrite through the same idempotent loop as the module's own (#120). A patch whose\n * identity now holds a different value demotes its file to `drift`.\n */\n reapplyPatches?: ManifestPatch[];\n /** The lock `base` record to write on a clean run — the base only (#120). */\n baseRecord?: LockBase;\n}\n\nexport interface BuildUpdatePlanArgs {\n root: string;\n config: SaasaloyConfig;\n manifest: Manifest;\n lock: Lockfile;\n inputs: ModuleUpdateInput[];\n /** Modules this run considered; defaults to everything installed. Scopes `missingLockEntries`. */\n considered?: string[];\n /** Comparisons the command decided not to update — carried through for one summary. */\n skipped?: ModuleComparison[];\n /** The project root package.json, for dependency-pin bumping. */\n pkg?: PackageJson | null;\n}\n\n/** Modules that have applied a config patch to this file, per the manifest's record. */\nfunction patchersOf(manifest: Manifest, target: string): string[] {\n return [\n ...new Set(\n manifest.patches.filter((p) => p.file === target).map((p) => p.module)\n ),\n ];\n}\n\n/** Aliases a module's scaffolds register — needed to resolve its own file targets. */\nfunction scaffoldAliases(\n mod: LoadedModule | undefined\n): Record<string, string> {\n const out: Record<string, string> = {};\n for (const scaffold of mod?.item.scaffolds ?? []) {\n for (const [alias, prefix] of Object.entries(scaffold.aliases ?? {})) {\n out[alias] = prefix;\n }\n }\n return out;\n}\n\nexport async function buildUpdatePlan(\n args: BuildUpdatePlanArgs\n): Promise<UpdatePlan> {\n const { root, config, manifest, lock, inputs, pkg } = args;\n\n // Scoped to what this run looked at, so `update email` doesn't editorialize about an\n // unrelated module the base template registered without a lock entry.\n const missingLockEntries = (args.considered ?? config.installed).filter(\n (name) => !lock.modules[name]\n );\n const modules: ModuleUpdatePlan[] = [];\n const touched: string[] = [];\n\n // Both records of what this project holds, unioned: `saasaloy.json` is the live list and\n // the lock is what `add` actually resolved, and a project can be mid-way between them.\n const installed: ReadonlySet<string> = new Set([\n ...config.installed,\n ...Object.keys(lock.modules),\n ]);\n\n for (const item of inputs) {\n const plan = await planOneModule({\n root,\n config,\n manifest,\n input: item,\n pkg: pkg ?? null,\n installed,\n });\n modules.push(plan);\n for (const file of plan.files) {\n if (file.action !== \"skip\" && file.action !== \"unchanged\") {\n touched.push(file.target);\n }\n }\n for (const file of plan.removals) {\n if (file.action !== \"delete-missing\") {\n touched.push(file.target);\n }\n }\n }\n\n // Phase 5: name the migration command when a schema file moved. The `@db` alias is\n // where the database capability's scaffold actually put the schema; the literal is\n // only a fallback for a project whose alias map predates it.\n const schemaPrefix = config.aliases[DB_ALIAS]\n ? `${config.aliases[DB_ALIAS].replace(/\\/$/, \"\")}/schema/`\n : DB_SCHEMA_FALLBACK;\n const migrationCommand =\n config.installed.includes(DATABASE_MODULE) &&\n touched.some((t) => t.startsWith(schemaPrefix))\n ? MIGRATION_COMMAND\n : undefined;\n\n return {\n modules,\n skipped: args.skipped ?? [],\n missingLockEntries,\n ...(migrationCommand ? { migrationCommand } : {}),\n verifyCommand: VERIFY_COMMAND,\n needsMerge: modules.some((m) => m.needsMerge),\n };\n}\n\ninterface PlanOneArgs {\n root: string;\n config: SaasaloyConfig;\n manifest: Manifest;\n input: ModuleUpdateInput;\n pkg: PackageJson | null;\n /** Modules this project holds — what an `onlyWith` condition is tested against (#99). */\n installed: ReadonlySet<string>;\n}\n\nasync function planOneModule(args: PlanOneArgs): Promise<ModuleUpdatePlan> {\n const { root, config, manifest, input, pkg } = args;\n const { theirs, base } = input;\n const name = theirs.item.name;\n\n // Resolve targets against the aliases that will exist once this run lands: the\n // project's own, plus any scaffold alias either revision (or a new prerequisite)\n // declares — otherwise a brand-new capability's files can't be placed.\n const prereqAliases: Record<string, string> = {};\n for (const prereqName of input.prereqs?.order ?? []) {\n Object.assign(\n prereqAliases,\n scaffoldAliases(input.prereqs?.modules.get(prereqName))\n );\n }\n const aliasView = {\n ...config.aliases,\n ...scaffoldAliases(base),\n ...prereqAliases,\n ...scaffoldAliases(theirs),\n };\n\n // The same install set both revisions are filtered against (#99): what the project\n // holds, plus any prerequisite this update introduces. Filtering base and theirs alike\n // is what keeps the three-way merge honest — a variant this project never held is not\n // a file the new revision \"dropped\", and the one it does hold is the one to diff.\n const installedSet = new Set([\n ...args.installed,\n ...(input.prereqs?.order ?? []),\n name,\n ]);\n\n const theirsFiles = await listModuleFiles(theirs, aliasView, installedSet);\n const baseFiles = base\n ? await listModuleFiles(base, aliasView, installedSet)\n : undefined;\n\n const files: PlannedUpdateFile[] = [];\n for (const ref of theirsFiles.values()) {\n const theirsContent = await readFile(ref.abs, \"utf-8\");\n const baseRef = baseFiles?.get(ref.target);\n const baseContent = baseRef\n ? await readFile(baseRef.abs, \"utf-8\")\n : undefined;\n const targetAbs = resolveWithinRoot(root, ref.target);\n const managed = manifest.managed[ref.target];\n const owned = managed?.module === name ? managed : undefined;\n const mine = await readIfPresent(targetAbs);\n\n const action = classifyUpdate(\n baseContent,\n theirsContent,\n mine,\n owned?.hash\n );\n const patchedBy = patchersOf(manifest, ref.target);\n files.push({\n module: name,\n from: ref.from,\n target: ref.target,\n targetAbs,\n action,\n base: baseContent,\n theirs: theirsContent,\n mine,\n newHash: hashContent(theirsContent),\n ...(patchedBy.length > 0 ? { patchedBy } : {}),\n });\n }\n\n // Anything this module owns that the new version no longer ships. Derived from the\n // manifest rather than from base, so it stays right even with no merge base — the\n // manifest is the record of what we actually put there.\n const removals: PlannedUpdateFile[] = [];\n for (const [target, entry] of Object.entries(manifest.managed)) {\n if (\n entry.module !== name ||\n theirsFiles.has(target) ||\n input.ignoreTargets?.has(target)\n ) {\n continue;\n }\n const targetAbs = resolveWithinRoot(root, target);\n const mine = await readIfPresent(targetAbs);\n const baseRef = baseFiles?.get(target);\n removals.push({\n module: name,\n from: entry.from ?? baseRef?.from ?? target,\n target,\n targetAbs,\n action: DELETE_ACTION[classifyTrackedFile(mine, entry.hash)],\n base: baseRef ? await readFile(baseRef.abs, \"utf-8\") : undefined,\n mine,\n });\n }\n\n const links = await planLinks(root, theirs);\n const patches = await planPatches(\n root,\n name,\n theirs.item.patches ?? [],\n files\n );\n await reapplyRecordedPatches(\n root,\n input.reapplyPatches ?? [],\n files,\n patches\n );\n const deps = planDepChanges(base, theirs, pkg);\n\n // A `dependsOn` the new version introduces is folded into this same plan rather than\n // left for the user to install by hand (decision 11).\n let prereqPlan: Plan | undefined;\n const prereqDependsOn: Record<string, string[]> = {};\n const prereqConflictsWith: Record<string, string[]> = {};\n const prereqNames = (input.prereqs?.order ?? []).filter(\n (n) => !config.installed.includes(n)\n );\n if (prereqNames.length > 0 && input.prereqs) {\n prereqPlan = await buildPlan({\n root,\n install: prereqNames,\n alreadyInstalled: [],\n modules: input.prereqs.modules,\n config,\n manifest,\n });\n for (const prereq of prereqNames) {\n const prereqItem = input.prereqs.modules.get(prereq)?.item;\n const dependsOn = prereqItem?.dependsOn;\n if (dependsOn && dependsOn.length > 0) {\n prereqDependsOn[prereq] = dependsOn;\n }\n const conflictsWith = prereqItem?.conflictsWith;\n if (conflictsWith && conflictsWith.length > 0) {\n prereqConflictsWith[prereq] = conflictsWith;\n }\n }\n }\n\n const needsMerge =\n files.some((f) => NEEDS_MERGE.has(f.action)) ||\n removals.some((f) => NEEDS_MERGE.has(f.action)) ||\n patches.some((p) => p.matched !== undefined);\n\n return {\n name,\n comparison: input.comparison,\n ...(input.noMergeBase ? { noMergeBase: input.noMergeBase } : {}),\n ...(input.baseRecord ? { baseRecord: input.baseRecord } : {}),\n intent: input.intent ?? [],\n files,\n removals,\n links,\n patches,\n ...deps,\n prereqNames,\n ...(prereqPlan ? { prereqPlan } : {}),\n prereqDependsOn,\n prereqConflictsWith,\n ...(theirs.item.dependsOn && theirs.item.dependsOn.length > 0\n ? { dependsOn: theirs.item.dependsOn }\n : {}),\n ...(theirs.item.conflictsWith && theirs.item.conflictsWith.length > 0\n ? { conflictsWith: theirs.item.conflictsWith }\n : {}),\n removeWarnings: [...(theirs.item.removeWarnings ?? [])],\n newEnvVars: newEnvVars(base, theirs),\n needsMerge,\n };\n}\n\n/**\n * Patches other modules recorded against files this revision ships (#120): the base's\n * `apps/web/package.json` after `waitlist` added a script to it. `add` never re-records a\n * file's hash after a patch, so on disk it reads as drift; three verdicts fix that:\n *\n * - a file that holds exactly the new render plus its patches is `unchanged`, since the\n * patched bytes are the bytes this run would leave behind;\n * - a file this run overwrites gets every recorded patch previewed against the new\n * render, so the execute loop lands them again after the write;\n * - a preview that comes back `matched` — the identity is present with a value the\n * template now ships — demotes the file to `drift`, and `patchedBy` names the patcher\n * in the plan.\n */\nasync function reapplyRecordedPatches(\n root: string,\n recorded: ManifestPatch[],\n files: PlannedUpdateFile[],\n patches: PlannedUpdatePatch[]\n): Promise<void> {\n if (recorded.length === 0) {\n return;\n }\n const byTarget = new Map(files.map((f) => [f.target, f]));\n\n for (const file of files) {\n const ops = recorded.filter((p) => p.file === file.target);\n if (\n file.action !== \"drift\" ||\n ops.length === 0 ||\n file.theirs === undefined\n ) {\n continue;\n }\n let expected = file.theirs;\n for (const op of ops) {\n expected = applyPatch(expected, op.patch, op.file).content;\n }\n if (expected === file.mine) {\n file.action = \"unchanged\";\n }\n }\n\n for (const entry of recorded) {\n const file = byTarget.get(entry.file);\n if (!file) {\n continue;\n }\n const previews = await previewPatches({\n root,\n module: entry.module,\n ops: [entry.patch],\n planned: new Map([\n [\n file.target,\n { content: file.theirs, landsOnDisk: WRITABLE.has(file.action) },\n ],\n ]),\n });\n for (const preview of previews) {\n if (preview.matched !== undefined && file.action === \"overwrite\") {\n file.action = \"drift\";\n }\n patches.push(preview);\n }\n }\n}\n\n/**\n * Env vars `theirs` requires that `base` did not. `base` absent means the run has no\n * merge base at all (a local install, a force-pushed branch), so nothing can be called\n * old — the whole set is returned rather than staying quiet about a secret the project\n * may now need.\n */\nfunction newEnvVars(\n base: LoadedModule | undefined,\n theirs: LoadedModule\n): Record<string, string> {\n const known = new Set(Object.keys(base?.item.envVars ?? {}));\n const out: Record<string, string> = {};\n for (const [key, description] of Object.entries(theirs.item.envVars ?? {})) {\n if (base && known.has(key)) {\n continue;\n }\n out[key] = description;\n }\n return out;\n}\n\n/**\n * The three-way verdict for one file. `base === theirs` short-circuits everything: the\n * module didn't touch this file, so whatever the user did to it is none of our business\n * (the rule that keeps `update` from re-proposing every hand-edit forever).\n */\nfunction classifyUpdate(\n base: string | undefined,\n theirs: string,\n mine: string | undefined,\n managedHash: string | undefined\n): UpdateFileAction {\n if (base !== undefined && base === theirs) {\n return \"skip\";\n }\n if (mine === undefined) {\n return managedHash === undefined ? \"create\" : \"restore\";\n }\n if (mine === theirs) {\n return \"unchanged\";\n }\n if (managedHash === undefined) {\n return \"conflict\";\n }\n return hashContent(mine) === managedHash ? \"overwrite\" : \"drift\";\n}\n\nasync function planLinks(\n root: string,\n theirs: LoadedModule\n): Promise<PlannedLink[]> {\n const links: PlannedLink[] = [];\n for (const skillRel of theirs.item.agent?.skills ?? []) {\n const folderName = posix.basename(skillRel);\n const path = posix.join(\".claude/skills\", folderName);\n const target = posix.join(\".agents/skills\", folderName);\n const pathAbs = resolveWithinRoot(root, path);\n const targetAbs = resolveWithinRoot(root, target);\n const state = await classifyLink(pathAbs, targetAbs);\n const action: LinkAction =\n state === \"missing\"\n ? \"create\"\n : state === \"correct\"\n ? \"exists\"\n : \"conflict\";\n links.push({\n module: theirs.item.name,\n path,\n pathAbs,\n target,\n targetAbs,\n action,\n });\n }\n return links;\n}\n\n/**\n * Re-apply each of the new version's patches, idempotently, against the content this run\n * will leave behind — so a patch aimed at a file the same update rewrites reads the new\n * bytes. `previewPatches` is the same helper `buildPlan` uses (#98). The interesting\n * outcome is `matched`: the patch is a no-op *because* its identity already holds a\n * different value — the user edited what we would have written — which routes it to the\n * merge plan as prose rather than silently doing nothing (decision 1).\n */\nfunction planPatches(\n root: string,\n module: string,\n ops: RegistryPatch[],\n files: PlannedUpdateFile[]\n): Promise<PlannedUpdatePatch[]> {\n return previewPatches({\n root,\n module,\n ops,\n planned: new Map(\n files.map((f) => [\n f.target,\n { content: f.theirs, landsOnDisk: WRITABLE.has(f.action) },\n ])\n ),\n });\n}\n\ninterface PlannedDeps {\n depAdds: DepChange[];\n devDepAdds: DepChange[];\n depBumps: DepBump[];\n depConflicts: string[];\n}\n\n/** `name -> version` for one descriptor revision, per bucket. */\nfunction pinsOf(\n mod: LoadedModule | undefined,\n dev: boolean\n): Map<string, string> {\n const specs =\n (dev ? mod?.item.devDependencies : mod?.item.dependencies) ?? [];\n return new Map(\n specs.map((spec) => [parseDep(spec).name, parseDep(spec).version])\n );\n}\n\n/**\n * A pin moves only when the module owns it *and* the user hasn't overridden it: base's\n * descriptor says which pins the module moved, and the current package.json still\n * matching base's value proves nobody edited it since (decision 8). Anything else keeps\n * `planDeps`'s existing conflict warning rather than clobbering a deliberate override.\n */\nfunction planDepChanges(\n base: LoadedModule | undefined,\n theirs: LoadedModule,\n pkg: PackageJson | null\n): PlannedDeps {\n const depAdds: DepChange[] = [];\n const devDepAdds: DepChange[] = [];\n const depBumps: DepBump[] = [];\n const depConflicts: string[] = [];\n if (!pkg) {\n return { depAdds, devDepAdds, depBumps, depConflicts };\n }\n\n for (const dev of [false, true]) {\n const bucket = dev ? (pkg.devDependencies ?? {}) : (pkg.dependencies ?? {});\n const otherBucket = dev\n ? (pkg.dependencies ?? {})\n : (pkg.devDependencies ?? {});\n const basePins = pinsOf(base, dev);\n\n for (const [name, wanted] of pinsOf(theirs, dev)) {\n // The pin may sit in the *other* bucket — the user moved it, or an earlier version\n // of the descriptor declared it there. Bump it where it actually lives: writing it\n // into the bucket this descriptor declares would leave the stale pin behind and\n // package.json would carry the same package at two versions.\n const inDeclaredBucket = bucket[name] !== undefined;\n const current = inDeclaredBucket ? bucket[name] : otherBucket[name];\n if (current === undefined) {\n (dev ? devDepAdds : depAdds).push({ name, version: wanted });\n continue;\n }\n if (current === wanted) {\n continue;\n }\n\n const basePin = basePins.get(name);\n if (basePin !== undefined && basePin !== wanted && current === basePin) {\n depBumps.push({\n name,\n from: current,\n to: wanted,\n dev: inDeclaredBucket ? dev : !dev,\n });\n } else {\n depConflicts.push(\n `${name}: package.json already has ${current}, ignoring ${wanted}`\n );\n }\n }\n }\n return { depAdds, devDepAdds, depBumps, depConflicts };\n}\n\n// --- Phase 3: apply the clean path ------------------------------------------------\n\nexport interface ExecuteUpdateArgs {\n root: string;\n config: SaasaloyConfig;\n manifest: Manifest;\n lock: Lockfile;\n /** The root package.json, when dependency pins are being written. */\n pkg?: PackageJson | null;\n}\n\nexport interface UpdateResult {\n /** Files actually written (create/overwrite/restore). */\n written: PlannedUpdateFile[];\n /** Files already byte-identical to the new version — manifest refreshed, disk untouched. */\n refreshed: PlannedUpdateFile[];\n /** Planned-writable files edited between planning and the write — left alone. */\n lateDrift: PlannedUpdateFile[];\n /** Files deleted because the new version dropped them and disk still matched. */\n deleted: PlannedUpdateFile[];\n /** Dropped-upstream files kept because they were hand-edited — now the user's. */\n driftSurvivors: PlannedUpdateFile[];\n /** Manifest entries dropped for files that were already gone. */\n untracked: PlannedUpdateFile[];\n patched: PlannedUpdatePatch[];\n /** Patches whose target file was absent — reported, not applied. */\n patchConflicts: PlannedUpdatePatch[];\n links: PlannedLink[];\n linkConflicts: PlannedLink[];\n /** Prerequisites installed as part of the same confirmed plan. */\n prereqsInstalled: string[];\n /** Modules whose lock entry moved to the new SHA (those that fully applied). */\n lockMoved: string[];\n /** Modules whose lock `ref` was rewritten by `--ref` while `resolved` stayed put. */\n refsRecorded: string[];\n /** Dependency pins actually written into package.json. */\n depsWritten: DepChange[];\n}\n\n/**\n * Whether a planned file still looks the way it did when the plan was built. The\n * confirmation prompt (and, for a bare `update`, a long fetch) sits between the two, so\n * the verdict is re-earned immediately before the write — the same reason remover.ts\n * re-checks before deleting.\n */\nasync function stillMatches(file: PlannedUpdateFile): Promise<boolean> {\n const now = await readIfPresent(file.targetAbs);\n return now === file.mine;\n}\n\nexport async function executeUpdatePlan(\n plan: UpdatePlan,\n args: ExecuteUpdateArgs\n): Promise<UpdateResult> {\n const { root, config, manifest, lock } = args;\n const result: UpdateResult = {\n written: [],\n refreshed: [],\n lateDrift: [],\n deleted: [],\n driftSurvivors: [],\n untracked: [],\n patched: [],\n patchConflicts: [],\n links: [],\n linkConflicts: [],\n prereqsInstalled: [],\n lockMoved: [],\n refsRecorded: [],\n depsWritten: [],\n };\n\n const bumpsAsDeps: DepChange[] = [];\n const devBumpsAsDeps: DepChange[] = [];\n\n for (const mod of plan.modules) {\n // Prerequisites first: a new capability may scaffold the very alias this module's\n // files resolve against, and `executePlan` registers those aliases as it goes.\n if (mod.prereqPlan) {\n await executePlan(mod.prereqPlan, root, config, manifest);\n result.prereqsInstalled.push(...mod.prereqPlan.install);\n }\n\n let clean = !mod.needsMerge;\n\n for (const file of mod.files) {\n if (!WRITABLE.has(file.action) || file.theirs === undefined) {\n continue;\n }\n if (!(await stillMatches(file))) {\n result.lateDrift.push(file);\n clean = false;\n continue;\n }\n // `unchanged` means disk already holds these exact bytes, so rewriting it would\n // churn the file's mtime for nothing. Its manifest entry is still refreshed —\n // that is how a file installed before `from` existed acquires one.\n if (file.action === \"unchanged\") {\n result.refreshed.push(file);\n } else {\n await mkdir(dirname(file.targetAbs), { recursive: true });\n await writeFile(file.targetAbs, file.theirs, \"utf-8\");\n result.written.push(file);\n }\n manifest.managed[file.target] = {\n module: file.module,\n hash: file.newHash ?? hashContent(file.theirs),\n from: file.from,\n };\n }\n\n for (const file of mod.removals) {\n if (file.action === \"delete\") {\n if (await stillMatches(file)) {\n await rmPath(file.targetAbs, { force: true });\n result.deleted.push(file);\n } else {\n // Edited while the confirmation was up — the delete was authorized for\n // different bytes, so it doesn't authorize removing these.\n result.driftSurvivors.push(file);\n clean = false;\n }\n } else if (file.action === \"delete-drift\") {\n result.driftSurvivors.push(file);\n } else {\n result.untracked.push(file);\n }\n // Untrack in every case: the new version no longer ships this file, so a survivor\n // becomes plain user-owned content rather than something `update` keeps offering\n // to delete on every run.\n delete manifest.managed[file.target];\n }\n\n for (const p of mod.patches) {\n if (!(await pathExists(p.fileAbs))) {\n result.patchConflicts.push(p);\n clean = false;\n continue;\n }\n // Re-apply against fresh disk state, never the preview — the engine is idempotent,\n // so a patch that already landed is a clean no-op.\n const source = await readFile(p.fileAbs, \"utf-8\");\n const { content, changed } = applyPatch(source, p.patch, p.file);\n if (!changed) {\n continue;\n }\n await writeFile(p.fileAbs, content, \"utf-8\");\n result.patched.push(p);\n const entry: ManifestPatch = {\n module: p.module,\n file: p.file,\n patch: p.patch,\n };\n if (\n !manifest.patches.some((existing) => samePatchEntry(existing, entry))\n ) {\n manifest.patches.push(entry);\n }\n }\n\n for (const link of mod.links) {\n if (link.action === \"conflict\") {\n result.linkConflicts.push(link);\n continue;\n }\n if (link.action === \"create\") {\n await createDirLink(link.pathAbs, link.targetAbs);\n }\n manifest.links[link.target] = link.path;\n result.links.push(link);\n }\n\n for (const bump of mod.depBumps) {\n (bump.dev ? devBumpsAsDeps : bumpsAsDeps).push({\n name: bump.name,\n version: bump.to,\n });\n }\n\n if (clean) {\n if (mod.removeWarnings.length > 0) {\n manifest.removeWarnings[mod.name] = [...mod.removeWarnings];\n } else {\n delete manifest.removeWarnings[mod.name];\n }\n }\n\n // The base has no `modules` entry: its record is the lock's `base` object, and it moves\n // under the same rule as a module's `resolved` — only once everything landed (#120).\n if (mod.baseRecord) {\n if (clean && mod.comparison.latest !== mod.comparison.current) {\n lock.base = mod.baseRecord;\n result.lockMoved.push(mod.name);\n }\n continue;\n }\n\n // Move `resolved` only for a module that fully landed. While anything still needs\n // merging, the *old* SHA is the only merge base a re-run has — advancing it would\n // strand the drifted files with nothing to diff against (decision 15's boundary).\n if (clean && mod.comparison.latest !== mod.comparison.current) {\n lock.modules[mod.name] = {\n source: mod.comparison.source,\n ref: mod.comparison.ref,\n resolved: mod.comparison.latest,\n ...(mod.dependsOn ? { dependsOn: mod.dependsOn } : {}),\n ...(mod.conflictsWith ? { conflictsWith: mod.conflictsWith } : {}),\n };\n result.lockMoved.push(mod.name);\n } else {\n // `ref` is intent (\"track this branch\") and `resolved` is fact (\"these bytes are\n // on disk\"), so an explicit `--ref` unpin is recorded even when files are still\n // out for merge — otherwise the user would have to repeat `--ref` on the re-run\n // the merge plan tells them to do (decision 10).\n const entry = lock.modules[mod.name];\n if (\n entry &&\n entry.ref !== mod.comparison.ref &&\n mod.comparison.source === entry.source\n ) {\n entry.ref = mod.comparison.ref;\n result.refsRecorded.push(mod.name);\n }\n }\n // A prerequisite installed here is pinned to the same SHA as the module that needs it.\n for (const prereq of mod.prereqPlan?.install ?? []) {\n const dependsOn = mod.prereqDependsOn[prereq];\n const conflictsWith = mod.prereqConflictsWith[prereq];\n lock.modules[prereq] = {\n source: mod.comparison.source,\n ref: mod.comparison.ref,\n resolved: mod.comparison.latest,\n ...(dependsOn && dependsOn.length > 0 ? { dependsOn } : {}),\n ...(conflictsWith && conflictsWith.length > 0 ? { conflictsWith } : {}),\n };\n }\n }\n\n // The root package.json is a base file, so a run that overwrote it above must not\n // then write dependency pins into the copy read before the run started — that copy\n // would clobber the new render (#120).\n let pkg = args.pkg;\n if (pkg && result.written.some((file) => file.target === \"package.json\")) {\n pkg = await readRootPackageJson(root);\n }\n const allAdds = plan.modules.flatMap((m) => m.depAdds);\n const allDevAdds = plan.modules.flatMap((m) => m.devDepAdds);\n if (\n pkg &&\n (allAdds.length > 0 ||\n allDevAdds.length > 0 ||\n bumpsAsDeps.length > 0 ||\n devBumpsAsDeps.length > 0)\n ) {\n const added = [...allAdds, ...bumpsAsDeps];\n const devAdded = [...allDevAdds, ...devBumpsAsDeps];\n // `writeDeps` merges by key, so an existing pin is rewritten in place — which is\n // exactly what a bump is.\n await writeDeps(root, pkg, added, devAdded);\n result.depsWritten = [...added, ...devAdded];\n }\n\n return result;\n}\n","import { readdir, readFile, rm as rmPath, writeFile } from \"node:fs/promises\";\nimport { dirname, join, relative, sep } from \"node:path\";\nimport {\n assertNoSymlinkPath,\n classifyLink,\n hashContent,\n pathExists,\n resolveWithinRoot,\n} from \"./fs-utils.js\";\nimport type { Lockfile } from \"./lock.js\";\nimport { samePatchEntry } from \"./manifest.js\";\nimport type { Manifest, ManifestPatch } from \"./manifest.js\";\nimport { isReversibleKind, reversePatch } from \"./patch/index.js\";\nimport type { PatchResult } from \"./patch/index.js\";\nimport type { SaasaloyConfig } from \"./schema.js\";\n\n// The deterministic core of `saasaloy remove` — mirrors applier.ts's buildPlan/\n// executePlan split, but for undoing a module's application (issue #27). Fully\n// offline: the module descriptor isn't available at remove time (a remote add's\n// temp dir is long gone), so everything here derives from the three local state\n// files — manifest.managed/links/patches, saasaloy.json's installed[]/aliases, and\n// the lock's dependsOn.\n\n// How an owned managed file relates to what's actually on disk right now:\n// delete — on-disk content matches the manifest hash → safe to remove\n// drift — exists but hand-edited since (hash mismatch) → needs confirmation\n// missing — already gone → nothing to delete, just untrack\nexport type FileRemoveAction = \"delete\" | \"drift\" | \"missing\";\n\n/**\n * The verdict for one tracked file, from its on-disk content (`undefined` when the file\n * is gone) and the hash the manifest recorded when we wrote it. `update`'s delete side\n * asks exactly this question, so it calls this too and renames the answers to its own\n * `delete`/`delete-drift`/`delete-missing` — one definition of \"is this still the file\n * Saasaloy put there?\" for both commands.\n */\nexport function classifyTrackedFile(\n content: string | undefined,\n manifestHash: string\n): FileRemoveAction {\n if (content === undefined) {\n return \"missing\";\n }\n return hashContent(content) === manifestHash ? \"delete\" : \"drift\";\n}\n\nexport interface PlannedRemoveFile {\n module: string;\n /** Project-relative POSIX path (manifest key). */\n target: string;\n /** Absolute path on disk. */\n targetAbs: string;\n action: FileRemoveAction;\n /** On-disk content at plan time — undefined when `missing`. Used by `--diff`. */\n oldContent?: string;\n}\n\n// How a module-owned `.claude/skills/<name>` symlink relates to what's on disk:\n// remove — the correct link is there → safe to unlink\n// missing — nothing there → nothing to unlink\n// conflict — something else occupies the path → leave it, warn (symmetric with `add`)\nexport type LinkRemoveAction = \"remove\" | \"missing\" | \"conflict\";\n\nexport interface PlannedRemoveLink {\n module: string;\n /** manifest.links key — the real folder the link points at (e.g. .agents/skills/<name>). */\n target: string;\n targetAbs: string;\n /** manifest.links value — the symlink path (e.g. .claude/skills/<name>). */\n path: string;\n pathAbs: string;\n action: LinkRemoveAction;\n}\n\n// How a recorded config patch relates to what's on disk right now:\n// revert — a reversible kind with an edit still there → `diff` previews the undo\n// refused — the inverse declined it (the line is the user's now), or the file no longer\n// parses at all → `reason` says which, and names the file\n// gone — nothing left to undo (target file absent, or already hand-reverted)\n// drop — a kind with no inverse (the two `package.json` ones) → record goes, edit stays\nexport type PatchRemoveAction = \"revert\" | \"refused\" | \"gone\" | \"drop\";\n\nexport interface PlannedRemovePatch {\n /** The manifest entry this untracks. Dropped on execute whatever the action. */\n entry: ManifestPatch;\n action: PatchRemoveAction;\n /** Unified diff of the would-be reversal; `\"\"` for every action but `revert`. */\n diff: string;\n /** Why the inverse refused — set only when `action` is `refused`. */\n reason?: string;\n}\n\nexport interface RemovePlan {\n module: string;\n /** Descriptor warnings persisted at add or clean update time. */\n warnings: string[];\n files: PlannedRemoveFile[];\n links: PlannedRemoveLink[];\n /** This module's entries in manifest.patches, each classified against fresh disk state.\n * Every entry is dropped on execute; an entry of a reversible kind is also *reversed*\n * on disk first (see `reversePatch`), and `diff` previews that reversal so\n * `--dry-run`/`--diff` show the edit rather than only labelling it. */\n patches: PlannedRemovePatch[];\n /** Installed modules whose lock `dependsOn` names this module — block removal\n * without `--force`. */\n dependents: string[];\n /** Installed modules with no lock entry at all — dependent detection is\n * incomplete for them (they might depend on this module; we can't tell). */\n missingLockEntries: string[];\n}\n\nexport interface BuildRemovePlanArgs {\n root: string;\n /** Module to remove. */\n name: string;\n config: SaasaloyConfig;\n manifest: Manifest;\n lock: Lockfile;\n}\n\n// The manifest attributes overwritten files to the last writer, so whichever module\n// owns a file directly under a link's target folder is the current owner of the\n// link too (the `saasaloy-<module>` folder convention makes collisions unlikely —\n// verified across modules/, same assumption the applier makes).\nfunction ownerOfLinkTarget(\n manifest: Manifest,\n linkTargetKey: string\n): string | undefined {\n const prefix = `${linkTargetKey}/`;\n for (const [target, entry] of Object.entries(manifest.managed)) {\n if (target.startsWith(prefix)) {\n return entry.module;\n }\n }\n return undefined;\n}\n\nexport async function buildRemovePlan(\n args: BuildRemovePlanArgs\n): Promise<RemovePlan> {\n const { root, name, config, manifest, lock } = args;\n\n const files: PlannedRemoveFile[] = [];\n for (const [target, entry] of Object.entries(manifest.managed)) {\n if (entry.module !== name) {\n continue;\n }\n const targetAbs = resolveWithinRoot(root, target);\n const oldContent = (await pathExists(targetAbs))\n ? await readFile(targetAbs, \"utf-8\")\n : undefined;\n files.push({\n module: name,\n target,\n targetAbs,\n action: classifyTrackedFile(oldContent, entry.hash),\n oldContent,\n });\n }\n\n const links: PlannedRemoveLink[] = [];\n for (const [target, path] of Object.entries(manifest.links)) {\n if (ownerOfLinkTarget(manifest, target) !== name) {\n continue;\n }\n const targetAbs = resolveWithinRoot(root, target);\n const pathAbs = resolveWithinRoot(root, path);\n const state = await classifyLink(pathAbs, targetAbs);\n links.push({\n module: name,\n target,\n targetAbs,\n path,\n pathAbs,\n action:\n state === \"missing\"\n ? \"missing\"\n : state === \"correct\"\n ? \"remove\"\n : \"conflict\",\n });\n }\n\n const patches: PlannedRemovePatch[] = [];\n for (const entry of manifest.patches) {\n if (entry.module !== name) {\n continue;\n }\n patches.push(await previewPatchRemoval(root, entry));\n }\n\n // A reverse-dependency map over the lock's `dependsOn`, restricted to what's\n // actually installed — an uninstalled module's stale dependsOn doesn't matter.\n const dependents: string[] = [];\n const missingLockEntries: string[] = [];\n for (const installedName of config.installed) {\n if (installedName === name) {\n continue;\n }\n const lockEntry = lock.modules[installedName];\n if (!lockEntry) {\n missingLockEntries.push(installedName);\n continue;\n }\n if (lockEntry.dependsOn?.includes(name)) {\n dependents.push(installedName);\n }\n }\n\n return {\n module: name,\n warnings: [...(manifest.removeWarnings[name] ?? [])],\n files,\n links,\n patches,\n dependents,\n missingLockEntries,\n };\n}\n\n/**\n * Classify one recorded patch against fresh disk state, and preview its reversal.\n *\n * `reversePatch` is pure, so the plan can carry the exact edit `--diff` promises without\n * writing anything — the repo requires every destructive operation to be previewable.\n * `executeRemovePlan` re-reads and recomputes rather than trusting this diff, because the\n * file can change between planning and executing.\n */\nasync function previewPatchRemoval(\n root: string,\n entry: ManifestPatch\n): Promise<PlannedRemovePatch> {\n if (!isReversibleKind(entry.patch.kind)) {\n return { entry, action: \"drop\", diff: \"\" };\n }\n\n const fileAbs = resolveWithinRoot(root, entry.file);\n if (!(await pathExists(fileAbs))) {\n return { entry, action: \"gone\", diff: \"\" };\n }\n await assertNoSymlinkPath(root, fileAbs);\n\n const source = await readFile(fileAbs, \"utf-8\");\n const attempt = tryReversePatch(source, entry);\n if (!attempt.ok) {\n return { entry, action: \"refused\", diff: \"\", reason: attempt.reason };\n }\n const result = attempt.result;\n if (result?.changed) {\n return { entry, action: \"revert\", diff: result.diff };\n }\n if (result?.reason !== undefined) {\n return { entry, action: \"refused\", diff: \"\", reason: result.reason };\n }\n return { entry, action: \"gone\", diff: \"\" };\n}\n\n/** Either the codemod's own verdict, or the parser's complaint about the target file. */\ntype ReversalAttempt =\n { ok: true; result: PatchResult | undefined } | { ok: false; reason: string };\n\n/**\n * `reversePatch` behind a guard. Every inverse parses the target file first, and both\n * parsers throw on syntax they can't read — a state only a hand edit produces, which the\n * remove contract routes to warn-and-skip, not to an aborted command. Turning the throw\n * into a refusal reason keeps the codemods pure (`string => string`, no error channel)\n * and names the file, so the warning says which of the workspace's files to open. Both\n * the plan and the execute pass call this: the plan so a broken target is previewed as a\n * refusal, the execute so a file broken *between* the two doesn't throw after files and\n * links are already deleted.\n */\nfunction tryReversePatch(\n source: string,\n entry: ManifestPatch\n): ReversalAttempt {\n try {\n return { ok: true, result: reversePatch(source, entry.patch, entry.file) };\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return {\n ok: false,\n reason: `${entry.file} does not parse (${detail}) — fix the file, then hand-revert this patch`,\n };\n }\n}\n\nexport interface ExecuteRemoveArgs {\n root: string;\n config: SaasaloyConfig;\n manifest: Manifest;\n lock: Lockfile;\n /**\n * Manifest targets classified `drift` that the caller confirmed to actually\n * delete (an interactive per-file `confirm`). Anything drift-classified but not\n * in this set survives on disk, untracked — the non-interactive `--yes` default\n * (drift never gets silently clobbered).\n */\n deleteDrifted?: ReadonlySet<string>;\n}\n\nexport interface RemoveResult {\n /** Files actually deleted from disk (hash-clean, or drift confirmed for deletion). */\n deleted: PlannedRemoveFile[];\n /** Drift files left on disk, untracked — the designed survivor outcome. */\n driftSurvivors: PlannedRemoveFile[];\n /** Files already gone from disk — just untracked, nothing to delete. */\n missingUntracked: PlannedRemoveFile[];\n /** `.claude/skills` symlinks actually removed. */\n linksRemoved: PlannedRemoveLink[];\n /** Symlinks left untouched because something else occupies their path. */\n linkConflicts: PlannedRemoveLink[];\n /** Patch entries actually undone on disk — a route link, a wrangler binding, a plugin\n * array element, a const array element, each with the import it needed. */\n patchesReversed: ManifestPatch[];\n /** Patch entries untracked without being undone: a kind with no inverse (the two\n * `package.json` ones), a target file that's gone, an edit already hand-reverted, or a\n * refusal. The command warns. */\n patchesDropped: ManifestPatch[];\n /** The subset of `patchesDropped` the inverse codemod actively declined, each with its\n * reason — a route the user repointed at their own handler, say. Separates \"there was\n * nothing left to undo\" from \"that line is yours now\" in what the command prints. */\n patchRefusals: RemovePatchRefusal[];\n /** Project-relative POSIX directories pruned because this run emptied them. */\n prunedDirs: string[];\n /** saasaloy.json alias names dropped because their prefix directory vanished. */\n droppedAliases: string[];\n}\n\n/** A patch entry left on disk on purpose, and the inverse codemod's account of why. */\nexport interface RemovePatchRefusal {\n patch: ManifestPatch;\n reason: string;\n}\n\nfunction toPosixRelative(root: string, abs: string): string {\n return relative(root, abs).split(sep).join(\"/\");\n}\n\n// Walk up from each deleted path's directory, removing directories that are now\n// empty, stopping at the first non-empty ancestor and never past `root`. Only\n// directories a deletion in *this run* could have emptied are ever candidates —\n// we start exclusively from paths this run actually removed.\nasync function pruneEmptyDirs(\n root: string,\n deletedAbsPaths: string[]\n): Promise<string[]> {\n const pruned: string[] = [];\n for (const abs of deletedAbsPaths) {\n let dir = dirname(abs);\n while (dir === root || dir.startsWith(root + sep)) {\n if (dir === root) {\n break;\n }\n if (!(await pathExists(dir))) {\n break;\n } // already pruned via another file's chain\n const entries = await readdir(dir);\n if (entries.length > 0) {\n break;\n } // first non-empty ancestor — stop climbing\n // `recursive` is required by fs.rm to remove a directory at all; safe here\n // since we just confirmed it's empty.\n await rmPath(dir, { recursive: true, force: true });\n pruned.push(toPosixRelative(root, dir));\n dir = dirname(dir);\n }\n }\n return pruned;\n}\n\n// A dangling alias would let a future `add` silently recreate a half-workspace;\n// dropping it makes `resolveTarget` fail loudly (\"Unknown alias\") instead.\nasync function dropDanglingAliases(\n root: string,\n config: SaasaloyConfig\n): Promise<string[]> {\n const dropped: string[] = [];\n for (const [alias, prefix] of Object.entries(config.aliases)) {\n const prefixAbs = join(root, ...prefix.split(\"/\"));\n if (!(await pathExists(prefixAbs))) {\n delete config.aliases[alias];\n dropped.push(alias);\n }\n }\n return dropped;\n}\n\n// Whether a planned file still looks the way it did when the plan was built:\n// unchanged — byte-identical to plan time → the plan's verdict still holds\n// changed — edited since → now user-owned content, must survive\n// gone — deleted since → nothing left to remove\ntype FileRecheck = \"unchanged\" | \"changed\" | \"gone\";\n\n// Arbitrary time passes between buildRemovePlan and here — the per-file drift\n// confirms are interactive and can sit open indefinitely. Re-reading immediately\n// before the delete is what keeps issue #27's \"drift is sacred\" true in the gap:\n// a file the user edited while a prompt was up is drift, whatever the plan said.\nasync function recheckFile(file: PlannedRemoveFile): Promise<FileRecheck> {\n if (!(await pathExists(file.targetAbs))) {\n return \"gone\";\n }\n if (file.oldContent === undefined) {\n return \"changed\";\n } // planned `missing`, something is there now\n const current = await readFile(file.targetAbs, \"utf-8\");\n return hashContent(current) === hashContent(file.oldContent)\n ? \"unchanged\"\n : \"changed\";\n}\n\nexport async function executeRemovePlan(\n plan: RemovePlan,\n args: ExecuteRemoveArgs\n): Promise<RemoveResult> {\n const { root, config, manifest, lock, deleteDrifted } = args;\n\n const deleted: PlannedRemoveFile[] = [];\n const driftSurvivors: PlannedRemoveFile[] = [];\n const missingUntracked: PlannedRemoveFile[] = [];\n\n for (const file of plan.files) {\n // Hash-clean, or drift the caller explicitly confirmed deleting — both are\n // \"delete this exact content\", so both have to prove the content is still that.\n const wantsDelete =\n file.action === \"delete\" ||\n (file.action === \"drift\" && !!deleteDrifted?.has(file.target));\n if (wantsDelete) {\n const state = await recheckFile(file);\n if (state === \"unchanged\") {\n await rmPath(file.targetAbs, { force: true });\n deleted.push(file);\n } else if (state === \"gone\") {\n missingUntracked.push(file);\n } else {\n // Edited between planning and now — the confirm the user gave was for\n // different bytes, so it doesn't authorize deleting these.\n driftSurvivors.push(file);\n }\n } else if (file.action === \"drift\") {\n driftSurvivors.push(file);\n } else {\n missingUntracked.push(file);\n }\n // Untrack in every case — a declined/survivor drift file becomes user-owned\n // (it classifies as a `conflict` on a future re-add, same as any untracked file).\n delete manifest.managed[file.target];\n }\n\n const linksRemoved: PlannedRemoveLink[] = [];\n const linkConflicts: PlannedRemoveLink[] = [];\n for (const link of plan.links) {\n if (link.action === \"remove\") {\n // Same staleness window as files: the link may have been replaced by a real\n // file or repointed since planning, and neither is ours to delete.\n const state = await classifyLink(link.pathAbs, link.targetAbs);\n if (state === \"correct\") {\n await rmPath(link.pathAbs, { force: true });\n linksRemoved.push(link);\n } else if (state === \"conflict\") {\n linkConflicts.push(link);\n }\n // \"missing\" — already gone, nothing to unlink.\n } else if (link.action === \"conflict\") {\n linkConflicts.push(link);\n }\n // Drop the links entry either way (ADR 0015 symmetry with `add`).\n delete manifest.links[link.target];\n }\n\n // Reverse what can be reversed, then untrack every entry either way. The four kinds\n // that edit a config file have an inverse (#83, #36); the two `package.json` kinds stay\n // report-only, and the command warns naming each file.\n //\n // Read fresh disk content rather than trusting the plan, mirroring what `executePlan`\n // does forward: the file may have been hand-edited since the plan was built. The\n // codemod is a no-op when the edit it recorded is already gone, so a hand-reverted\n // file is untracked and warned about instead of force-edited.\n const patchesReversed: ManifestPatch[] = [];\n const patchesDropped: ManifestPatch[] = [];\n const patchRefusals: RemovePatchRefusal[] = [];\n for (const planned of plan.patches) {\n const entry = planned.entry;\n const fileAbs = resolveWithinRoot(root, entry.file);\n let reversed = false;\n if (await pathExists(fileAbs)) {\n // `resolveWithinRoot` proves only that the recorded path is lexically inside the\n // project; the read and write below both follow symlinks. Refuse a linked component\n // rather than reverse a patch into a file outside the root.\n await assertNoSymlinkPath(root, fileAbs);\n const source = await readFile(fileAbs, \"utf-8\");\n const attempt = tryReversePatch(source, entry);\n if (!attempt.ok) {\n patchRefusals.push({ patch: entry, reason: attempt.reason });\n } else if (attempt.result?.changed) {\n await writeFile(fileAbs, attempt.result.content, \"utf-8\");\n reversed = true;\n } else if (attempt.result?.reason !== undefined) {\n patchRefusals.push({ patch: entry, reason: attempt.result.reason });\n }\n }\n (reversed ? patchesReversed : patchesDropped).push(entry);\n // Untrack this entry now, not after the loop. Reversing a patch is a disk write, and\n // `runRemove` saves the manifest from a `finally`: an entry still listed after its\n // file was already rewritten is a ledger that lies about disk. Anything this loop\n // never reached stays tracked, which is the point.\n manifest.patches = manifest.patches.filter(\n (p) => !samePatchEntry(p, entry)\n );\n }\n\n const prunedDirs = await pruneEmptyDirs(root, [\n ...deleted.map((f) => f.targetAbs),\n ...linksRemoved.map((l) => l.pathAbs),\n ]);\n const droppedAliases = await dropDanglingAliases(root, config);\n\n config.installed = config.installed.filter((m) => m !== plan.module);\n delete lock.modules[plan.module];\n delete manifest.removeWarnings[plan.module];\n\n return {\n deleted,\n driftSurvivors,\n missingUntracked,\n linksRemoved,\n linkConflicts,\n patchesReversed,\n patchesDropped,\n patchRefusals,\n prunedDirs,\n droppedAliases,\n };\n}\n","import {\n cancel,\n confirm,\n intro,\n isCancel,\n log,\n note,\n outro,\n select,\n} from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { lineDiff } from \"../lib/diff.js\";\nimport {\n EXIT_FAILURE,\n EXIT_OK,\n EXIT_REFUSED,\n exitCodeFor,\n formatFailure,\n} from \"../lib/exit.js\";\nimport { loadLock, saveLock } from \"../lib/lock.js\";\nimport { loadManifest, saveManifest } from \"../lib/manifest.js\";\nimport { isReversibleKind } from \"../lib/patch/index.js\";\nimport { findProjectRoot } from \"../lib/project.js\";\nimport { buildRemovePlan, executeRemovePlan } from \"../lib/remover.js\";\nimport type {\n FileRemoveAction,\n LinkRemoveAction,\n PatchRemoveAction,\n PlannedRemoveFile,\n RemovePlan,\n} from \"../lib/remover.js\";\nimport { loadConfig, saveConfig } from \"../lib/saasaloy-config.js\";\nimport { isInteractive, wrapForNote } from \"../lib/tui.js\";\nimport { uiBlockFiles } from \"../lib/ui-blocks.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\n\n// `saasaloy remove <module>` — the local undo, mirroring `add`'s clack UX and flag\n// surface. Fully offline: the plan derives entirely from manifest.json,\n// saasaloy.json and saasaloy-lock.json (issue #27). `--dry-run`/`--diff` preview\n// without mutating; a drifted managed file is never silently clobbered.\n\ninterface Options {\n name?: string;\n dryRun: boolean;\n diff: boolean;\n yes: boolean;\n force: boolean;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\n \"--dry-run\",\n \"--diff\",\n \"--yes\",\n \"-y\",\n \"--force\",\n \"--help\",\n \"-h\",\n]);\nconst USAGE =\n \"saasaloy remove [<module>] [--dry-run] [--diff] [--yes] [--force]\";\nconst HELP: CommandHelp = {\n name: \"remove\",\n describe: DESCRIPTIONS.remove,\n usage: USAGE,\n flags: {\n \"--dry-run\": \"show the plan and delete nothing\",\n \"--diff\": \"show what each deletion removes, and delete nothing\",\n \"-y, --yes\": \"skip the confirmation prompt (drifted files are kept)\",\n \"--force\": \"remove even while another module depends on it\",\n },\n};\n\nfunction parseArgs(argv: string[]): Options {\n const positional: string[] = [];\n const unknown: string[] = [];\n for (const arg of argv) {\n if (!arg.startsWith(\"-\")) {\n positional.push(arg);\n } else if (!KNOWN_FLAGS.has(arg)) {\n unknown.push(arg);\n }\n }\n unknown.push(...positional.slice(1));\n return {\n name: positional[0],\n dryRun: argv.includes(\"--dry-run\"),\n diff: argv.includes(\"--diff\"),\n yes: argv.includes(\"--yes\") || argv.includes(\"-y\"),\n force: argv.includes(\"--force\"),\n unknown,\n };\n}\n\nconst FILE_ACTION_LABEL: Record<FileRemoveAction, string> = {\n delete: pc.red(\"delete\"),\n drift: pc.yellow(\"drift → confirm\"),\n missing: pc.dim(\"missing → untrack\"),\n};\n\n// Under --yes the confirm loop never runs (see the `if (!opts.yes)` gate below), so a\n// drifted file always survives untracked rather than pausing for a prompt — label it\n// accordingly instead of implying a confirmation that won't happen.\nfunction fileActionLabel(action: FileRemoveAction, yes: boolean): string {\n if (action === \"drift\" && yes) {\n return pc.yellow(\"drift → kept (untracked)\");\n }\n return FILE_ACTION_LABEL[action];\n}\n\nconst LINK_ACTION_LABEL: Record<LinkRemoveAction, string> = {\n remove: pc.red(\"unlink\"),\n missing: pc.dim(\"missing\"),\n conflict: pc.yellow(\"conflict → left\"),\n};\n\nconst PATCH_ACTION_LABEL: Record<PatchRemoveAction, string> = {\n revert: pc.red(\"revert\"),\n refused: pc.yellow(\"drift → left\"),\n gone: pc.dim(\"already gone\"),\n drop: pc.dim(\"untrack\"),\n};\n\n// Cap a single file's deletion diff so a big generated file can't flood the terminal\n// (same cap as `add`'s renderDiff).\nconst MAX_DIFF_LINES = 60;\n\nfunction renderDeleteDiff(file: PlannedRemoveFile): string {\n const lines = lineDiff(file.oldContent ?? \"\", \"\");\n const shown = lines\n .slice(0, MAX_DIFF_LINES)\n .map((line) => pc.red(`- ${line.text}`));\n if (lines.length > MAX_DIFF_LINES) {\n shown.push(pc.dim(` … ${lines.length - MAX_DIFF_LINES} more lines`));\n }\n return shown.join(\"\\n\");\n}\n\nfunction summarizeRemovePlan(\n plan: RemovePlan,\n name: string,\n yes: boolean\n): void {\n const lines = [`will remove: ${pc.cyan(name)}`, \"\"];\n for (const file of plan.files) {\n lines.push(` ${fileActionLabel(file.action, yes)} ${file.target}`);\n }\n const toDelete = plan.files.filter((f) => f.action === \"delete\").length;\n const drifted = plan.files.filter((f) => f.action === \"drift\").length;\n const missing = plan.files.filter((f) => f.action === \"missing\").length;\n lines.push(\"\");\n const driftNote = yes ? \"kept untracked\" : \"need confirmation\";\n lines.push(\n pc.dim(\n `${toDelete} file(s) to delete, ${drifted} drifted (${driftNote}), ${missing} already missing`\n )\n );\n note(wrapForNote(lines.join(\"\\n\")), \"Plan\");\n\n const ownedLinks = plan.links.filter((l) => l.action !== \"missing\");\n if (ownedLinks.length > 0) {\n const linkLines = ownedLinks.map(\n (l) => `${LINK_ACTION_LABEL[l.action]} ${l.path}`\n );\n note(wrapForNote(linkLines.join(\"\\n\")), \"Skill links\");\n }\n // Conflicted skill links are warned about once, post-execute (see `result.linkConflicts`\n // below) — that reflects what actually happened, so we don't also warn here from the plan.\n\n // A dependents refusal (without --force) returns before this is ever called, so\n // reaching here with dependents means --force is overriding it.\n if (plan.dependents.length > 0) {\n log.warn(\n `${pc.cyan(name)} is still depended on by ${plan.dependents.join(\", \")} — proceeding anyway (--force).`\n );\n }\n for (const missingLock of plan.missingLockEntries) {\n log.warn(\n `Installed module ${pc.cyan(missingLock)} has no lock entry — dependent detection is incomplete for it.`\n );\n }\n\n // A kind with an inverse is listed as work rather than warned about; what actually\n // happened is reported post-execute (#83, #36).\n const reversible = plan.patches.filter((p) => p.action !== \"drop\");\n if (reversible.length > 0) {\n const patchLines = reversible.map(\n (p) =>\n ` ${PATCH_ACTION_LABEL[p.action]} ${p.entry.file} ${pc.dim(`(${p.entry.patch.kind})`)}${p.reason === undefined ? \"\" : `\\n ${pc.dim(p.reason)}`}`\n );\n note(wrapForNote(patchLines.join(\"\\n\")), \"Config patches\");\n }\n for (const p of plan.patches) {\n if (p.action !== \"drop\") {\n continue;\n }\n log.warn(\n `Config patch on ${pc.cyan(p.entry.file)} ${pc.dim(`(${p.entry.patch.kind})`)} is not reversed by \\`remove\\` — hand-revert it if needed.`\n );\n }\n}\n\nexport async function runRemove(argv: string[]): Promise<number> {\n // Parse before help answers, so a typo'd flag alongside `--help` still reports the typo.\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n intro(pc.bgCyan(pc.black(\" saasaloy remove \")));\n\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n\n let root: string;\n let config: Awaited<ReturnType<typeof loadConfig>>;\n try {\n root = await findProjectRoot();\n config = await loadConfig(root);\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n }\n\n try {\n let name = opts.name;\n if (!name) {\n if (config.installed.length === 0) {\n note(\"Nothing installed.\", \"Nothing to do\");\n outro(pc.dim(\"0 modules\"));\n return EXIT_OK;\n }\n // Same hazard as `add`: without a terminal this prompt can never be answered, so\n // it would hang rather than fail.\n if (!isInteractive()) {\n cancel(\n `No module named and no terminal to pick one in — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n const picked = await select({\n message: \"Pick a module to remove\",\n options: config.installed.map((n) => ({ value: n, label: n })),\n });\n if (isCancel(picked)) {\n cancel(\"remove cancelled\");\n return EXIT_FAILURE;\n }\n name = picked;\n }\n\n if (!config.installed.includes(name)) {\n cancel(`${pc.cyan(name)} isn't installed — nothing to remove.`);\n return EXIT_REFUSED;\n }\n\n const manifest = await loadManifest(root);\n const lock = await loadLock(root);\n const plan = await buildRemovePlan({ root, name, config, manifest, lock });\n\n if (plan.dependents.length > 0 && !opts.force) {\n cancel(\n `${pc.cyan(name)} is still depended on by ${plan.dependents.join(\", \")} — refusing ` +\n `${pc.dim(\"(use --force to remove it anyway)\")}.`\n );\n return EXIT_REFUSED;\n }\n\n summarizeRemovePlan(plan, name, opts.yes);\n for (const warning of plan.warnings) {\n log.warn(warning);\n }\n\n if (opts.diff) {\n for (const file of plan.files) {\n if (file.action === \"missing\") {\n continue;\n }\n note(\n renderDeleteDiff(file),\n `${fileActionLabel(file.action, opts.yes)} ${file.target}`\n );\n }\n // A reversal is a destructive edit to someone else's file, so it previews like one.\n // The diff comes from the plan's pure `reversePatch` run; nothing has been written.\n for (const p of plan.patches) {\n if (p.action !== \"revert\") {\n continue;\n }\n note(p.diff, `${pc.red(\"revert\")} ${p.entry.file}`);\n }\n }\n\n // --dry-run and --diff both preview only; nothing is written.\n if (opts.dryRun || opts.diff) {\n outro(\n pc.dim(\n opts.diff\n ? \"diff only — nothing removed\"\n : \"dry run — nothing removed\"\n )\n );\n return EXIT_OK;\n }\n\n // Drift is sacred: confirm per file before deleting hand-edited content.\n // Under --yes, no prompts run at all — drifted files always survive, untracked.\n const deleteDrifted = new Set<string>();\n if (!opts.yes) {\n for (const file of plan.files) {\n if (file.action !== \"drift\") {\n continue;\n }\n const proceed = await confirm({\n message: `${file.target} was hand-edited since it was applied — delete it anyway?`,\n initialValue: false,\n });\n if (isCancel(proceed)) {\n cancel(\"remove cancelled\");\n return EXIT_FAILURE;\n }\n if (proceed) {\n deleteDrifted.add(file.target);\n }\n }\n }\n\n if (!opts.yes) {\n const proceed = await confirm({ message: \"Proceed?\" });\n if (isCancel(proceed)) {\n cancel(\"remove cancelled\");\n return EXIT_FAILURE;\n }\n if (!proceed) {\n outro(pc.dim(\"aborted — nothing removed\"));\n return EXIT_OK;\n }\n }\n\n let result: Awaited<ReturnType<typeof executeRemovePlan>>;\n try {\n result = await executeRemovePlan(plan, {\n root,\n config,\n manifest,\n lock,\n deleteDrifted,\n });\n } finally {\n // Record whatever actually happened even if a mid-plan step failed — the\n // ledger must reflect the real on-disk state (same rationale as `add`).\n await saveManifest(root, manifest);\n await saveConfig(root, config);\n await saveLock(root, lock);\n }\n\n for (const file of result.deleted) {\n log.step(`${pc.red(\"delete\")} ${file.target}`);\n }\n for (const file of result.missingUntracked) {\n log.step(\n `${pc.dim(\"untrack\")} ${file.target} ${pc.dim(\"(already gone)\")}`\n );\n }\n for (const link of result.linksRemoved) {\n log.step(`${pc.red(\"unlink\")} ${link.path}`);\n }\n for (const link of result.linkConflicts) {\n log.warn(\n `Skill link ${pc.cyan(link.path)} left untouched — not ours to remove.`\n );\n }\n for (const p of result.patchesReversed) {\n log.step(`${pc.red(\"revert\")} ${p.file} ${pc.dim(`(${p.patch.kind})`)}`);\n }\n // A reversible entry that landed in `patchesDropped` either had nothing left to undo —\n // the link was hand-removed, or the file is gone — or the inverse refused it, which is\n // a different thing to say. Never claim a revert; the non-reversible kinds were already\n // warned about from the plan.\n const refusals = new Map(\n result.patchRefusals.map((r) => [r.patch, r.reason])\n );\n for (const p of result.patchesDropped) {\n if (!isReversibleKind(p.patch.kind)) {\n continue;\n }\n const reason = refusals.get(p);\n log.warn(\n reason === undefined\n ? `Config patch on ${pc.cyan(p.file)} ${pc.dim(`(${p.patch.kind})`)} was already gone — nothing to revert.`\n : `Config patch on ${pc.cyan(p.file)} ${pc.dim(`(${p.patch.kind})`)} left untouched — ${reason}.`\n );\n }\n for (const dir of result.prunedDirs) {\n log.step(`${pc.dim(\"prune\")} ${dir}`);\n }\n for (const alias of result.droppedAliases) {\n log.warn(\n `Alias ${pc.cyan(alias)} dropped — its target directory is gone.`\n );\n }\n\n // A block was placed by hand, so it is un-placed by hand. `add` wrote the file and\n // printed a pointer to the module's skill; nothing recorded which page the owner\n // imported it into, so the deletion here leaves a live import pointing at a file that\n // no longer exists. Say it plainly rather than let the next build say it (#62).\n const removedBlocks = uiBlockFiles(result.deleted, config.aliases);\n if (removedBlocks.length > 0) {\n const blockLines = removedBlocks\n .map((f) => ` ${pc.red(\"deleted\")} ${f.target}`)\n .join(\"\\n\");\n note(\n wrapForNote(\n `${blockLines}\\n\\n${pc.dim(\"The wire-up you added by hand is not reversed — nothing recorded which page you put it on. Take the import and its tag out yourself, or the build fails.\")}`\n ),\n \"Wire-up left behind\"\n );\n }\n\n // Drift survivors are the designed outcome, not a failure — exit 0 either way.\n if (result.driftSurvivors.length > 0) {\n const survivorLines = result.driftSurvivors\n .map((f) => ` ${pc.yellow(\"kept\")} ${f.target}`)\n .join(\"\\n\");\n note(\n wrapForNote(\n `${survivorLines}\\n\\n${pc.dim(\"Hand-edited since — left on disk, now untracked.\")}`\n ),\n \"Drift survivors\"\n );\n }\n\n outro(\n pc.green(\n `Removed ${pc.bold(name)} ${pc.dim(`(${result.deleted.length} files)`)}`\n )\n );\n return EXIT_OK;\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n }\n}\n","import { mkdir, realpath, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, isAbsolute, join, resolve } from \"node:path\";\nimport {\n cancel,\n confirm,\n intro,\n isCancel,\n log,\n note,\n outro,\n} from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport {\n detectCliMismatches,\n formatCliMismatches,\n UPGRADE_COMMAND,\n} from \"../lib/cli-requires.js\";\nimport {\n adoptBase,\n BASE_MODULE,\n baseUpdateInput,\n isBaseTracked,\n missingBaseTargets,\n templateHash,\n} from \"../lib/base.js\";\nimport { detectConflicts, formatConflicts } from \"../lib/conflicts.js\";\nimport { lineDiff } from \"../lib/diff.js\";\nimport {\n EXIT_FAILURE,\n EXIT_OK,\n EXIT_REFUSED,\n exitCodeFor,\n formatFailure,\n} from \"../lib/exit.js\";\nimport { LOCK_FILE, loadLock, saveLock } from \"../lib/lock.js\";\nimport { loadManifest, MANIFEST_FILE, saveManifest } from \"../lib/manifest.js\";\nimport { renderMergePlan } from \"../lib/merge-plan.js\";\nimport { readRootPackageJson } from \"../lib/pkg-json.js\";\nimport { findProjectRoot } from \"../lib/project.js\";\nimport {\n createRegistrySource,\n REGISTRY_ENV,\n RemoteRegistrySource,\n} from \"../lib/registry.js\";\nimport type { LoadedModule, RegistrySource } from \"../lib/registry.js\";\nimport type { Graph } from \"../lib/resolve.js\";\nimport { resolveGraph } from \"../lib/resolve.js\";\nimport { CONFIG_FILE, loadConfig, saveConfig } from \"../lib/saasaloy-config.js\";\nimport { baseTemplateDir } from \"../lib/scaffold.js\";\nimport { compareVersions, parseVersion } from \"../lib/semver.js\";\nimport { TUI_ON_STDERR, wrapForNote } from \"../lib/tui.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { readVersion } from \"../version.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\nimport {\n buildUpdatePlan,\n compareBase,\n compareInstalled,\n executeUpdatePlan,\n recordRefRewrites,\n} from \"../lib/updater.js\";\nimport type {\n ModuleComparison,\n ModuleUpdateInput,\n ModuleUpdatePlan,\n PlannedUpdateFile,\n UpdateFileAction,\n UpdatePlan,\n UpdateResult,\n} from \"../lib/updater.js\";\n\n// `saasaloy update [<module>]` — re-resolve each installed module's ref, apply what can\n// be applied deterministically, and emit an agent-consumable merge plan for everything\n// that can't (issue #48). The copy-in tool's answer to \"you own the code, so you can\n// never update it\": clean files are overwritten, hand-edited files are never touched,\n// and the difference between the two is written down in enough detail for an agent to\n// resolve it.\n//\n// stdout belongs to the merge plan and nothing else, so the whole clack TUI goes to\n// stderr (`TUI_ON_STDERR`) and `saasaloy update email | claude` works with no flag.\n//\n// The base template rides the same engine (#120, ADR 0032). A bare run considers the base\n// and every module; `update base` considers the base alone. The bundled template is\n// rendered for this project and classified like a module with no merge base. A project\n// with no base record is *adopted* first — its base files are recorded as they stand at\n// the running CLI — and the run stops there, so the next run is a real update. This is\n// the only command that writes that record; `outdated` and `doctor` report `untracked`.\n\ninterface Options {\n name?: string;\n /** `--ref <branch|tag|sha>`: the explicit unpin. Requires a named module. */\n ref?: string;\n /** `--out <path>`: write the merge plan here instead of stdout. */\n out?: string;\n dryRun: boolean;\n diff: boolean;\n yes: boolean;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\n \"--dry-run\",\n \"--diff\",\n \"--yes\",\n \"-y\",\n \"--help\",\n \"-h\",\n]);\nconst VALUE_FLAGS = new Set([\"--ref\", \"--out\"]);\nconst USAGE =\n \"saasaloy update [<module>|base] [--ref <ref>] [--out <path>] [--dry-run] [--diff] [--yes]\";\nconst HELP: CommandHelp = {\n name: \"update\",\n describe: DESCRIPTIONS.update,\n usage: USAGE,\n flags: {\n \"--ref <ref>\":\n \"update one module to this branch, tag, or SHA (not the base)\",\n \"--out <path>\": \"write the merge plan to a file instead of stdout\",\n \"--dry-run\": \"show the plan and write nothing\",\n \"--diff\": \"show a per-file diff and write nothing\",\n \"-y, --yes\": \"skip the confirmation prompt\",\n },\n};\n\nfunction parseArgs(argv: string[]): Options {\n const positional: string[] = [];\n const unknown: string[] = [];\n const values: Record<string, string> = {};\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!;\n if (!arg.startsWith(\"-\")) {\n positional.push(arg);\n continue;\n }\n // Accept both `--ref v2` and `--ref=v2`; a value flag with nothing after it is a\n // usage error, not a silently-empty ref. In the space-separated form the next token\n // must not itself be a flag, or `--ref --dry-run` pins the ref to \"--dry-run\" and\n // reaches the registry with it instead of reporting the usage error.\n const eq = arg.indexOf(\"=\");\n const flag = eq === -1 ? arg : arg.slice(0, eq);\n if (VALUE_FLAGS.has(flag)) {\n const next = eq === -1 ? argv[i + 1] : arg.slice(eq + 1);\n if (!next || (eq === -1 && next.startsWith(\"-\"))) {\n unknown.push(`${flag} (missing value)`);\n continue;\n }\n if (eq === -1) {\n i++;\n }\n values[flag] = next;\n continue;\n }\n if (!KNOWN_FLAGS.has(arg)) {\n unknown.push(arg);\n }\n }\n unknown.push(...positional.slice(1));\n\n return {\n name: positional[0],\n ref: values[\"--ref\"],\n out: values[\"--out\"],\n dryRun: argv.includes(\"--dry-run\"),\n diff: argv.includes(\"--diff\"),\n yes: argv.includes(\"--yes\") || argv.includes(\"-y\"),\n unknown,\n };\n}\n\nconst ACTION_LABEL: Record<UpdateFileAction, string> = {\n skip: pc.dim(\"unchanged upstream\"),\n create: pc.green(\"create\"),\n overwrite: pc.cyan(\"update\"),\n restore: pc.green(\"restore\"),\n unchanged: pc.dim(\"unchanged\"),\n drift: pc.yellow(\"drift → merge\"),\n conflict: pc.yellow(\"conflict → merge\"),\n delete: pc.red(\"delete\"),\n \"delete-drift\": pc.yellow(\"dropped → kept\"),\n \"delete-missing\": pc.dim(\"untrack\"),\n};\n\n// Actions worth a line in the summary — `skip`/`unchanged` are counted, not listed, so\n// a big module doesn't bury the handful of paths that actually move.\nconst QUIET: ReadonlySet<UpdateFileAction> = new Set<UpdateFileAction>([\n \"skip\",\n \"unchanged\",\n]);\n\n// Cap a single file's diff so a big generated file can't flood the terminal (same cap as `add`).\nconst MAX_DIFF_LINES = 60;\n\nfunction renderDiff(file: PlannedUpdateFile): string {\n const lines = lineDiff(file.mine ?? \"\", file.theirs ?? \"\");\n const shown = lines.slice(0, MAX_DIFF_LINES).map((line) => {\n switch (line.kind) {\n case \"add\": {\n return pc.green(`+ ${line.text}`);\n }\n case \"del\": {\n return pc.red(`- ${line.text}`);\n }\n default: {\n return pc.dim(` ${line.text}`);\n }\n }\n });\n if (lines.length > MAX_DIFF_LINES) {\n shown.push(pc.dim(` … ${lines.length - MAX_DIFF_LINES} more lines`));\n }\n return shown.join(\"\\n\");\n}\n\nfunction short(sha: string): string {\n return /^[0-9a-f]{40}$/i.test(sha) ? sha.slice(0, 7) : sha;\n}\n\nfunction summarizeModule(mod: ModuleUpdatePlan): string[] {\n const lines = [\n `${pc.cyan(mod.name)} ${pc.dim(`${short(mod.comparison.current)} → ${short(mod.comparison.latest)} (${mod.comparison.ref})`)}`,\n ];\n if (mod.noMergeBase) {\n lines.push(pc.dim(` no merge base — ${mod.noMergeBase}`));\n }\n for (const name of mod.prereqNames) {\n lines.push(\n ` ${pc.green(\"install\")} ${pc.cyan(name)} ${pc.dim(\"(new prerequisite)\")}`\n );\n }\n for (const file of [...mod.files, ...mod.removals]) {\n if (QUIET.has(file.action)) {\n continue;\n }\n lines.push(` ${ACTION_LABEL[file.action]} ${file.target}`);\n }\n const quiet = [...mod.files, ...mod.removals].filter((f) =>\n QUIET.has(f.action)\n ).length;\n if (quiet > 0) {\n lines.push(pc.dim(` ${quiet} file(s) already up to date`));\n }\n for (const bump of mod.depBumps) {\n lines.push(\n ` ${pc.cyan(\"bump\")} ${bump.name} ${pc.dim(`${bump.from} → ${bump.to}`)}`\n );\n }\n for (const dep of [...mod.depAdds, ...mod.devDepAdds]) {\n lines.push(` ${pc.green(\"dep\")} ${dep.name}@${dep.version}`);\n }\n return lines;\n}\n\nfunction summarizePlan(plan: UpdatePlan): void {\n const lines: string[] = [];\n for (const mod of plan.modules) {\n lines.push(...summarizeModule(mod), \"\");\n }\n const merging = plan.modules.reduce(\n (n, m) =>\n n +\n m.files.filter((f) => f.action === \"drift\" || f.action === \"conflict\")\n .length +\n m.removals.filter((f) => f.action === \"delete-drift\").length,\n 0\n );\n const writing = plan.modules.reduce(\n (n, m) =>\n n +\n m.files.filter(\n (f) =>\n !QUIET.has(f.action) &&\n f.action !== \"drift\" &&\n f.action !== \"conflict\"\n ).length,\n 0\n );\n lines.push(pc.dim(`${writing} file(s) to apply, ${merging} needing merge`));\n note(wrapForNote(lines.join(\"\\n\")), \"Plan\", TUI_ON_STDERR);\n\n // A module with no lock entry already arrives here as an `unresolvable` comparison\n // carrying that reason, so `plan.missingLockEntries` isn't reported a second time.\n for (const comparison of plan.skipped) {\n log.info(\n `${pc.cyan(comparison.name)} ${pc.dim(`— ${skipReason(comparison)}`)}`,\n TUI_ON_STDERR\n );\n }\n for (const mod of plan.modules) {\n for (const conflict of mod.depConflicts) {\n log.warn(`Dependency version conflict — ${conflict}.`, TUI_ON_STDERR);\n }\n for (const patch of mod.patches) {\n if (patch.action === \"missing\") {\n log.warn(\n `Config patch target ${pc.cyan(patch.file)} is missing — ${patch.patch.kind} not applied.`,\n TUI_ON_STDERR\n );\n }\n }\n }\n // Named before the confirmation, in their own box, because a secret the new version\n // started requiring is the one thing that breaks the project after a clean update and\n // the one thing the file list can't tell you (#98).\n const envVars = new Map<string, string>();\n for (const mod of plan.modules) {\n for (const [key, description] of Object.entries(mod.newEnvVars)) {\n envVars.set(key, description);\n }\n }\n if (envVars.size > 0) {\n const envLines = [...envVars].map(\n ([key, description]) => `${pc.cyan(key)} ${pc.dim(`— ${description}`)}`\n );\n note(wrapForNote(envLines.join(\"\\n\")), \"Env vars to set\", TUI_ON_STDERR);\n }\n if (plan.migrationCommand) {\n note(\n wrapForNote(\n `${pc.cyan(plan.migrationCommand)}\\n\\n${pc.dim(\"The update changed the database schema — run this to regenerate migrations.\")}`\n ),\n \"Migrations\",\n TUI_ON_STDERR\n );\n }\n note(\n wrapForNote(\n `${pc.cyan(plan.verifyCommand)}\\n\\n${pc.dim(\"Named, not run — an update usually precedes `pnpm install`.\")}`\n ),\n \"Verify with\",\n TUI_ON_STDERR\n );\n}\n\nfunction skipReason(comparison: ModuleComparison, preview = false): string {\n switch (comparison.status) {\n case \"current\": {\n const at = `already at ${short(comparison.current)}`;\n if (!comparison.refRewrite) {\n return at;\n }\n // The SHA didn't move but the ref did — say so, or \"already at <sha7>\" would read\n // as though `--ref` had been ignored.\n return preview\n ? `${at} — \\`--ref\\` would move the lock onto \\`${comparison.ref}\\``\n : `${at} — now tracking \\`${comparison.ref}\\``;\n }\n default: {\n return comparison.detail ?? comparison.status;\n }\n }\n}\n\n// The three files Saasaloy owns. `--out` writes after the ledger saves, so a path\n// pointing at one of these replaces real state with merge-plan prose and breaks the\n// next command. Refuse the path up front rather than at the write.\nconst STATE_FILES = [CONFIG_FILE, LOCK_FILE, MANIFEST_FILE];\n\n/** Why `--out <path>` is refused, or `undefined` when the path is safe to write. */\nasync function stateFileRefusal(\n root: string,\n out: string | undefined\n): Promise<undefined | string> {\n if (!out) {\n return undefined;\n }\n const target = await canonicalPath(resolvePlanOut(out));\n for (const file of STATE_FILES) {\n if ((await canonicalPath(resolve(root, file))) === target) {\n return `Refusing to write the merge plan to ${pc.cyan(out)} — that is Saasaloy's own ${pc.cyan(file)}. Pick another path.`;\n }\n }\n return undefined;\n}\n\n// Compare through realpath, not lexically: `/var/…` is a symlink to `/private/var/…` on\n// macOS, so two absolute paths naming the same file can differ character for character\n// and a lexical check would wave the state file through. A path that isn't on disk yet\n// still has a real parent, and one that has neither is already lexically resolved.\nasync function canonicalPath(path: string): Promise<string> {\n try {\n return await realpath(path);\n } catch {\n try {\n return join(await realpath(dirname(path)), basename(path));\n } catch {\n return path;\n }\n }\n}\n\n/** `--out <path>` resolved against the working directory, the one place that mapping lives. */\nfunction resolvePlanOut(out: string): string {\n return isAbsolute(out) ? out : resolve(process.cwd(), out);\n}\n\n/** Emit the merge plan: `--out <path>` when given, otherwise stdout — and nothing else ever. */\nasync function emitMergePlan(\n document: string,\n out: string | undefined\n): Promise<void> {\n if (!out) {\n process.stdout.write(document);\n return;\n }\n const target = resolvePlanOut(out);\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, document, \"utf-8\");\n log.success(`Merge plan written to ${pc.cyan(out)}`, TUI_ON_STDERR);\n}\n\n/** `owner/repo` → the two halves, or undefined when the lock's source isn't a remote slug. */\nfunction splitSlug(slug: string): [string, string] | undefined {\n const [owner, repo, ...rest] = slug.split(\"/\");\n if (!owner || !repo || rest.length > 0) {\n return undefined;\n }\n return [owner, repo];\n}\n\nexport async function runUpdate(argv: string[]): Promise<number> {\n // Parse before help answers, so a typo'd flag alongside `--help` still reports the typo.\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n intro(pc.bgCyan(pc.black(\" saasaloy update \")), TUI_ON_STDERR);\n\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`,\n TUI_ON_STDERR\n );\n return EXIT_REFUSED;\n }\n // A bare `update --ref v2` would silently move every module onto one ref, which is\n // never what someone means — the unpin is per module by construction (decision 10).\n if (opts.ref && !opts.name) {\n cancel(\n `\\`--ref\\` needs an explicit module — usage: \\`${USAGE}\\`.`,\n TUI_ON_STDERR\n );\n return EXIT_REFUSED;\n }\n\n let root: string;\n let config: Awaited<ReturnType<typeof loadConfig>>;\n try {\n root = await findProjectRoot();\n config = await loadConfig(root);\n } catch (error) {\n cancel(formatFailure(error), TUI_ON_STDERR);\n return exitCodeFor(error);\n }\n\n const outRefusal = await stateFileRefusal(root, opts.out);\n if (outRefusal) {\n cancel(outRefusal, TUI_ON_STDERR);\n return EXIT_REFUSED;\n }\n\n // The merge plan goes to stdout, so `saasaloy update email | claude` is the designed\n // pipeline and a redirected stdout says nothing about who is watching. This command\n // used to read it as \"a script is driving me\" and set `--yes` for itself, which made\n // `saasaloy update | tee log` overwrite every clean file unconfirmed (#98). stdin is\n // the stream the confirmation actually reads: no terminal there means the prompt can\n // never be answered, so refuse and name the two ways forward. A preview writes nothing,\n // so it is exempt.\n const preview = opts.dryRun || opts.diff;\n if (!opts.yes && !preview && !process.stdin.isTTY) {\n cancel(\n \"No terminal to confirm in — re-run with `--yes` to apply, or `--dry-run` to preview.\",\n TUI_ON_STDERR\n );\n // Refused by design, like every other bad-usage exit here (#98's 0/1/2 scheme).\n return EXIT_REFUSED;\n }\n\n const sources: RegistrySource[] = [];\n // Temp renders of the bundled template, dropped with the sources.\n const cleanups: (() => Promise<void>)[] = [];\n try {\n // `update base` names the template, which ships with the CLI and has no ref to move.\n const wantsBase = !opts.name || opts.name === BASE_MODULE;\n const wantsModules = opts.name !== BASE_MODULE;\n if (!wantsModules && opts.ref) {\n cancel(\n `\\`--ref\\` moves a module's ref; the base template ships with the CLI — usage: \\`${USAGE}\\`.`,\n TUI_ON_STDERR\n );\n return EXIT_REFUSED;\n }\n if (wantsModules && opts.name && !config.installed.includes(opts.name)) {\n cancel(\n `${pc.cyan(opts.name)} isn't installed — nothing to update.`,\n TUI_ON_STDERR\n );\n return EXIT_REFUSED;\n }\n const targets = wantsModules\n ? opts.name\n ? [opts.name]\n : config.installed\n : [];\n\n const manifest = await loadManifest(root);\n const lock = await loadLock(root);\n const registryOverride = !!process.env[REGISTRY_ENV];\n const cliVersion = await readVersion();\n\n // The base first (#120): adopt an unrecorded project and stop, refuse a downgrade,\n // then compare the recorded template hash against the one this CLI ships.\n let baseComparison: ModuleComparison | undefined;\n let templateDir: string | undefined;\n if (wantsBase) {\n templateDir = await baseTemplateDir();\n if (!isBaseTracked(lock, manifest)) {\n const adoption = await adoptBase({\n root,\n cliVersion,\n manifest,\n lock,\n templateDir,\n dryRun: preview,\n });\n const count = adoption.adopted.length;\n const lines = [\n `${preview ? \"Would adopt\" : \"Adopted\"} ${count} base file${count === 1 ? \"\" : \"s\"} at CLI ${pc.cyan(cliVersion)}, ` +\n `each at the hash it has on disk — your edits are the baseline, not drift.`,\n ];\n if (adoption.absent.length > 0) {\n lines.push(\n \"\",\n `${adoption.absent.length} template file${adoption.absent.length === 1 ? \" is\" : \"s are\"} not on disk and ${preview ? \"would be\" : \"were\"} recorded as missing, so the next update restores ${adoption.absent.length === 1 ? \"it\" : \"them\"}:`,\n ...adoption.absent.map((target) => ` ${pc.dim(target)}`)\n );\n }\n lines.push(\n \"\",\n pc.dim(\n \"Nothing else was applied. Run `saasaloy update` again to update the base.\"\n )\n );\n note(wrapForNote(lines.join(\"\\n\")), \"Base adopted\", TUI_ON_STDERR);\n outro(\n preview\n ? pc.dim(\"dry run — nothing recorded\")\n : pc.green(`Recorded the base at CLI ${cliVersion}.`),\n TUI_ON_STDERR\n );\n return EXIT_OK;\n }\n\n const recorded = lock.base!.cliVersion;\n const recordedVersion = parseVersion(recorded);\n const runningVersion = parseVersion(cliVersion);\n if (\n recordedVersion &&\n runningVersion &&\n compareVersions(recordedVersion, runningVersion) > 0\n ) {\n cancel(\n `This project's base was recorded by saasaloy ${recorded}, and ${cliVersion} is running — ` +\n `updating would move the base backwards. Upgrade with \\`${UPGRADE_COMMAND}\\`. Nothing was written.`,\n TUI_ON_STDERR\n );\n return EXIT_REFUSED;\n }\n if (recorded !== cliVersion) {\n log.info(\n `Base recorded at CLI ${pc.cyan(recorded)}; ${pc.cyan(cliVersion)} is running.`,\n TUI_ON_STDERR\n );\n }\n baseComparison = compareBase({\n lock,\n manifest,\n runningHash: await templateHash(templateDir),\n runningVersion: cliVersion,\n missingTargets: await missingBaseTargets(root, manifest, templateDir),\n });\n }\n\n if (targets.length === 0 && !baseComparison) {\n note(\"Nothing installed.\", \"Nothing to do\", TUI_ON_STDERR);\n outro(pc.dim(\"0 modules\"), TUI_ON_STDERR);\n return EXIT_OK;\n }\n\n // Cache one source per (repo, ref) so a bare `update` resolves each ref once.\n const resolvers = new Map<string, RemoteRegistrySource>();\n const remote = (slug: string, ref: string): RemoteRegistrySource => {\n const key = `${slug}@${ref}`;\n const cached = resolvers.get(key);\n if (cached) {\n return cached;\n }\n const parts = splitSlug(slug);\n if (!parts) {\n throw new Error(\n `Lock entry source \"${slug}\" isn't an owner/repo coordinate.`\n );\n }\n const source = new RemoteRegistrySource(parts[0], parts[1], ref);\n resolvers.set(key, source);\n sources.push(source);\n return source;\n };\n\n if (registryOverride) {\n const fromRemote = targets.filter((name) => {\n const entry = lock.modules[name];\n return entry && entry.source !== \"local\";\n });\n if (fromRemote.length > 0) {\n log.warn(\n `${REGISTRY_ENV} is set, so ${fromRemote.join(\", \")} will be updated from that working ` +\n `copy rather than ${pc.cyan(lock.modules[fromRemote[0]!]?.source ?? \"the registry\")} — ` +\n `${pc.dim(\"there is no merge base for a working copy\")}.`,\n TUI_ON_STDERR\n );\n }\n }\n\n const moduleComparisons =\n targets.length > 0\n ? await compareInstalled({\n installed: targets,\n lock,\n overrideRef: opts.ref,\n registryOverride,\n resolveRef: (_name, entry, ref) =>\n remote(entry.source, ref).resolveSha(),\n })\n : [];\n const comparisons = baseComparison\n ? [baseComparison, ...moduleComparisons]\n : moduleComparisons;\n\n const outdated = comparisons.filter((c) => c.status === \"outdated\");\n const skipped = comparisons.filter((c) => c.status !== \"outdated\");\n if (outdated.length === 0) {\n // `--ref` onto a tag that already points at the SHA the lock records moves no\n // files, but it is still the explicit unpin — record it here or the module stays\n // pinned forever. `--dry-run`/`--diff` preview only, so they write nothing.\n if (!preview && recordRefRewrites(lock, comparisons).length > 0) {\n await saveLock(root, lock);\n }\n for (const comparison of skipped) {\n log.info(\n `${pc.cyan(comparison.name)} ${pc.dim(`— ${skipReason(comparison, preview)}`)}`,\n TUI_ON_STDERR\n );\n }\n outro(pc.green(\"Everything is up to date.\"), TUI_ON_STDERR);\n return EXIT_OK;\n }\n\n // Fetch each outdated module twice — at the new SHA (theirs) and at the SHA the lock\n // records (base). Refetching base is what makes a real three-way merge possible; a\n // base we can't reach degrades the document rather than failing the update.\n const inputs: ModuleUpdateInput[] = [];\n // Mutually exclusive modules, checked the way `add` checks them (#98). A new version\n // can introduce a `dependsOn` on a second driver, and `update` installs a new\n // prerequisite as part of the same plan — so without this the command lands the exact\n // pair `add` refuses. Collected across every module and reported once, before\n // anything is written: `executeUpdatePlan` applies the whole plan under one\n // confirmation, so a refusal here has to stop the run, not one module of it.\n const conflictRefusals: string[] = [];\n // The same shape for `requires.saasaloy` (#50): a module's *new* version can declare\n // a CLI floor its installed version never had, and that is exactly the case the field\n // exists for. Collected across the run and refused before any plan is built.\n const cliRefusals: string[] = [];\n const missingLockEntries = new Set<string>();\n // Every graph that resolved, kept for the combined check below.\n const resolvedGraphs: Graph[] = [];\n if (baseComparison?.status === \"outdated\" && templateDir) {\n const handle = await baseUpdateInput({\n root,\n manifest,\n lock,\n templateDir,\n cliVersion,\n comparison: baseComparison,\n });\n cleanups.push(handle.cleanup);\n inputs.push(handle.input);\n }\n for (const comparison of outdated) {\n if (comparison.name === BASE_MODULE) {\n continue;\n }\n // One module's fetch failing is that module's problem: a dead tarball, a renamed\n // dependency, a network blip. It is reported and skipped, never fatal, so a bare\n // `update` still lands every other module (criterion 17).\n try {\n const theirsSource = registryOverride\n ? createRegistrySource({})\n : remote(comparison.source, comparison.latest);\n if (registryOverride) {\n sources.push(theirsSource);\n }\n\n const graph = await resolveGraph(theirsSource, comparison.name);\n const theirs = graph.modules.get(comparison.name);\n if (!theirs) {\n throw new Error(\n `${comparison.name} isn't in the registry at ${short(comparison.latest)}.`\n );\n }\n\n resolvedGraphs.push(graph);\n const cliMismatches = detectCliMismatches({ cliVersion, graph });\n if (cliMismatches.length > 0) {\n cliRefusals.push(\n formatCliMismatches(cliMismatches, comparison.name, cliVersion)\n );\n continue;\n }\n const report = detectConflicts({ graph, config, lock });\n for (const name of report.missingLockEntries) {\n missingLockEntries.add(name);\n }\n if (report.conflicts.length > 0) {\n conflictRefusals.push(\n formatConflicts(report.conflicts, comparison.name, \"update\")\n );\n continue;\n }\n\n let base: LoadedModule | undefined;\n let noMergeBase: string | undefined;\n if (registryOverride || comparison.current === \"local\") {\n noMergeBase = \"local install\";\n } else {\n try {\n base = await remote(\n comparison.source,\n comparison.current\n ).readModule(comparison.name);\n } catch (error) {\n // A force-pushed branch, a deleted tag, a repo gone private — the clean path\n // has already been decided, so refusing here would leave the user worse off.\n noMergeBase =\n error instanceof Error ? error.message : String(error);\n }\n }\n\n const intent =\n base && !registryOverride\n ? await theirsSource.commitSubjects(\n `modules/${comparison.name}`,\n comparison.current,\n comparison.latest\n )\n : [];\n\n inputs.push({\n comparison,\n theirs,\n ...(base ? { base } : {}),\n ...(noMergeBase ? { noMergeBase } : {}),\n intent,\n prereqs: {\n order: graph.order.filter((n) => n !== comparison.name),\n modules: graph.modules,\n },\n });\n } catch (error) {\n skipped.push({\n ...comparison,\n status: \"unresolvable\",\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // The per-module pass above cannot see a pair that arrives across two graphs: one\n // outdated module's new version pulls in one driver, another's pulls in the other,\n // and neither graph alone holds both. Union every resolved graph and check once more,\n // before any plan is built or written (#98).\n if (conflictRefusals.length === 0 && resolvedGraphs.length > 1) {\n const merged: Graph = { order: [], modules: new Map() };\n for (const graph of resolvedGraphs) {\n for (const [name, mod] of graph.modules) {\n if (!merged.modules.has(name)) {\n merged.modules.set(name, mod);\n merged.order.push(name);\n }\n }\n }\n const report = detectConflicts({ graph: merged, config, lock });\n for (const name of report.missingLockEntries) {\n missingLockEntries.add(name);\n }\n if (report.conflicts.length > 0) {\n conflictRefusals.push(\n formatConflicts(\n report.conflicts,\n outdated.map((c) => c.name).join(\", \"),\n \"update\"\n )\n );\n }\n }\n\n if (missingLockEntries.size > 0) {\n log.warn(\n `No lock entry for ${[...missingLockEntries].map((m) => pc.cyan(m)).join(\", \")} — ` +\n `any conflict they declare can't be checked ${pc.dim(\"(re-add them to record it)\")}.`,\n TUI_ON_STDERR\n );\n }\n // A CLI too old for one of the incoming descriptors stops the whole run, ahead of the\n // conflict report: there is no point resolving a driver choice with an applier that\n // cannot read the module it would resolve it for.\n if (cliRefusals.length > 0) {\n cancel(cliRefusals.join(\"\\n\"), TUI_ON_STDERR);\n return EXIT_REFUSED;\n }\n if (conflictRefusals.length > 0) {\n cancel(conflictRefusals.join(\"\\n\"), TUI_ON_STDERR);\n return EXIT_REFUSED;\n }\n\n // Everything outdated failed to fetch — there is no plan to summarize or confirm.\n if (inputs.length === 0) {\n for (const comparison of skipped) {\n // Only a fetch failure is a warning here — a `current` module is just news.\n const line = `${pc.cyan(comparison.name)} ${pc.dim(`— ${skipReason(comparison)}`)}`;\n if (comparison.status === \"unresolvable\") {\n log.warn(line, TUI_ON_STDERR);\n } else {\n log.info(line, TUI_ON_STDERR);\n }\n }\n // Every candidate failed to fetch — a failure, not a refusal: a retry may work.\n outro(pc.yellow(\"Nothing could be updated.\"), TUI_ON_STDERR);\n return EXIT_FAILURE;\n }\n\n const pkg = await readRootPackageJson(root);\n if (!pkg) {\n log.warn(\n \"No package.json at the project root — dependency pins won't be updated.\",\n TUI_ON_STDERR\n );\n }\n const plan = await buildUpdatePlan({\n root,\n config,\n manifest,\n lock,\n inputs,\n considered: targets,\n skipped,\n pkg,\n });\n\n summarizePlan(plan);\n\n if (opts.diff) {\n for (const file of [\n ...plan.modules.flatMap((m) => m.files),\n ...plan.modules.flatMap((m) => m.removals),\n ]) {\n if (QUIET.has(file.action)) {\n continue;\n }\n note(\n renderDiff(file),\n `${ACTION_LABEL[file.action]} ${file.target}`,\n TUI_ON_STDERR\n );\n }\n }\n\n // --dry-run and --diff both preview only: nothing is written anywhere, including\n // `--out`. The merge plan still renders to stdout so the preview is complete.\n if (preview) {\n const document = renderMergePlan(plan);\n if (document) {\n process.stdout.write(document);\n }\n outro(\n pc.dim(\n opts.diff\n ? \"diff only — nothing applied\"\n : \"dry run — nothing applied\"\n ),\n TUI_ON_STDERR\n );\n return EXIT_OK;\n }\n\n if (!opts.yes) {\n const proceed = await confirm({ message: \"Proceed?\", ...TUI_ON_STDERR });\n if (isCancel(proceed)) {\n cancel(\"update cancelled\", TUI_ON_STDERR);\n return EXIT_FAILURE;\n }\n if (!proceed) {\n outro(pc.dim(\"aborted — nothing applied\"), TUI_ON_STDERR);\n return EXIT_OK;\n }\n }\n\n // Capture the outcome rather than letting it throw, so the ledger writes below\n // still run on the failure path and the original error still wins afterwards.\n const outcome = await executeUpdatePlan(plan, {\n root,\n config,\n manifest,\n lock,\n pkg,\n }).then(\n (value) => ({ ok: true as const, value }),\n (error: unknown) => ({ ok: false as const, error })\n );\n\n // Record whatever actually landed even if a mid-plan write failed — the ledger must\n // describe the real on-disk state (`add`'s invariant; #49 owns real transactionality\n // for both commands). All three are attempted together: awaiting them in sequence\n // would let a failed manifest save skip the config and lock, which is the same\n // partial ledger this step exists to prevent.\n const saves = await Promise.allSettled([\n saveManifest(root, manifest),\n saveConfig(root, config),\n saveLock(root, lock),\n ]);\n if (!outcome.ok) {\n throw outcome.error;\n }\n const failedSave = saves.find((save) => save.status === \"rejected\");\n if (failedSave) {\n throw failedSave.reason;\n }\n const result: UpdateResult = outcome.value;\n\n reportResult(result);\n\n // Phase 4: the merge plan is emitted only after the clean path has run, so it\n // describes what is actually left to do rather than what might have been.\n const document = renderMergePlan(plan);\n if (document) {\n await emitMergePlan(document, opts.out);\n note(\n wrapForNote(\n pc.dim(\n opts.out\n ? \"Hand that file to an agent — your edits were left untouched.\"\n : \"That merge plan is on stdout — pipe it to an agent. Your edits were left untouched.\"\n )\n ),\n \"Needs merge\",\n TUI_ON_STDERR\n );\n }\n\n // Drift routing to a merge plan is the designed outcome, not a failure — exit 0\n // whether or not anything was left for the agent to reconcile.\n outro(summarizeOutcome(plan, result), TUI_ON_STDERR);\n return EXIT_OK;\n } catch (error) {\n cancel(formatFailure(error), TUI_ON_STDERR);\n return exitCodeFor(error);\n } finally {\n // Each remote source extracted its modules to a temp dir; drop them all.\n for (const source of sources) {\n await source.cleanup?.();\n }\n for (const cleanup of cleanups) {\n await cleanup();\n }\n }\n}\n\n/** The closing line: what landed, and what is still waiting on a merge. */\nfunction summarizeOutcome(plan: UpdatePlan, result: UpdateResult): string {\n const changed = result.written.length + result.deleted.length;\n const merging = plan.modules.filter((m) => m.needsMerge).map((m) => m.name);\n const applied =\n changed > 0\n ? `Updated ${[\n ...new Set(\n [...result.written, ...result.deleted].map((f) => f.module)\n ),\n ]\n .map((n) => pc.bold(n))\n .join(\n \", \"\n )} ${pc.dim(`(${result.written.length} written, ${result.deleted.length} removed)`)}`\n : \"\";\n\n if (merging.length === 0) {\n return pc.green(applied || \"Already up to date.\");\n }\n const names = merging.map((n) => pc.bold(n)).join(\", \");\n const verb = merging.length === 1 ? \"needs\" : \"need\";\n const merge = pc.yellow(\n applied\n ? `${names} still ${verb} a merge — see the plan above.`\n : `${names} ${verb} a merge — see the plan above.`\n );\n return applied ? `${pc.green(applied)} ${merge}` : merge;\n}\n\nfunction reportResult(result: UpdateResult): void {\n for (const name of result.prereqsInstalled) {\n log.step(\n `${pc.green(\"install\")} ${pc.cyan(name)} ${pc.dim(\"(new prerequisite)\")}`,\n TUI_ON_STDERR\n );\n }\n for (const file of result.written) {\n log.step(`${ACTION_LABEL[file.action]} ${file.target}`, TUI_ON_STDERR);\n }\n for (const file of result.deleted) {\n log.step(`${pc.red(\"delete\")} ${file.target}`, TUI_ON_STDERR);\n }\n for (const file of result.untracked) {\n log.step(\n `${pc.dim(\"untrack\")} ${file.target} ${pc.dim(\"(already gone)\")}`,\n TUI_ON_STDERR\n );\n }\n for (const patch of result.patched) {\n log.step(\n `${pc.green(\"patch\")} ${patch.file} ${pc.dim(`— ${patch.patch.kind}`)}`,\n TUI_ON_STDERR\n );\n }\n for (const link of result.linkConflicts) {\n log.warn(\n `Skill link ${pc.cyan(link.path)} left untouched — not ours to replace.`,\n TUI_ON_STDERR\n );\n }\n for (const dep of result.depsWritten) {\n log.step(`${pc.cyan(\"dep\")} ${dep.name}@${dep.version}`, TUI_ON_STDERR);\n }\n if (result.lateDrift.length > 0) {\n const lines = result.lateDrift\n .map((f) => ` ${pc.yellow(\"kept\")} ${f.target}`)\n .join(\"\\n\");\n note(\n wrapForNote(\n `${lines}\\n\\n${pc.dim(\"Edited while the plan was open — left alone rather than overwritten.\")}`\n ),\n \"Changed under us\",\n TUI_ON_STDERR\n );\n }\n if (result.driftSurvivors.length > 0) {\n const lines = result.driftSurvivors\n .map((f) => ` ${pc.yellow(\"kept\")} ${f.target}`)\n .join(\"\\n\");\n note(\n wrapForNote(\n `${lines}\\n\\n${pc.dim(\"Hand-edited — left on disk, now untracked.\")}`\n ),\n \"Drift survivors\",\n TUI_ON_STDERR\n );\n }\n}\n","import { BASE_INTENT, BASE_MODULE } from \"./base.js\";\nimport { toDiff } from \"./patch/diff.js\";\nimport type {\n ModuleUpdatePlan,\n PlannedUpdateFile,\n PlannedUpdatePatch,\n UpdatePlan,\n} from \"./updater.js\";\n\n// The artifact the whole feature exists for (build spec §2.9, CONTEXT.md): for every\n// file `update` refused to overwrite, a structured, agent-consumable merge plan —\n// natural-language intent + target files + old/new context — that can be handed\n// straight to an agent CLI with no editing. Markdown, because it is the one format\n// both a person and an agent read without a renderer (issue #48).\n//\n// Pure string building: it writes nothing and reads nothing. The command decides\n// whether the result goes to stdout or to `--out`.\n\n/** Render the merge plan for everything that drifted, or `\"\"` when nothing did. */\nexport function renderMergePlan(plan: UpdatePlan): string {\n const modules = plan.modules.filter((m) => m.needsMerge);\n if (modules.length === 0) {\n return \"\";\n }\n\n const fileCount = modules.reduce(\n (total, m) =>\n total +\n m.files.filter(isMergeFile).length +\n m.removals.filter(isMergeFile).length,\n 0\n );\n\n const out: string[] = [\n \"# Saasaloy merge plan\",\n \"\",\n `Generated by \\`saasaloy update\\`. ${countLabel(fileCount, \"file\")} across ` +\n `${countLabel(modules.length, \"module\")} changed on both sides — upstream and here — so ` +\n \"the update stopped short of writing them. Saasaloy never overwrites work you did by \" +\n \"hand; below is everything needed to reconcile the two sets of changes.\",\n \"\",\n ];\n\n for (const mod of modules) {\n out.push(...renderModule(mod));\n }\n\n out.push(...renderInstructions(plan));\n return `${out.join(\"\\n\").trimEnd()}\\n`;\n}\n\n/** Only the states that actually need a human or an agent — the merge plan's whole subject. */\nfunction isMergeFile(file: PlannedUpdateFile): boolean {\n return (\n file.action === \"drift\" ||\n file.action === \"conflict\" ||\n file.action === \"delete-drift\"\n );\n}\n\nfunction countLabel(n: number, noun: string): string {\n return `${n} ${noun}${n === 1 ? \"\" : \"s\"}`;\n}\n\nfunction renderModule(mod: ModuleUpdatePlan): string[] {\n const out: string[] = [`## ${mod.name}`, \"\", \"### Intent\", \"\"];\n\n if (mod.baseRecord) {\n // The base has no commit range to read subjects from: the template ships inside the\n // CLI. One line says what the agent needs to know (#120).\n out.push(\n `The base template shipped with a newer CLI. For every file below: ${BASE_INTENT}.`\n );\n } else if (mod.intent.length > 0) {\n out.push(\n \"What changed upstream, from the commits touching this module:\",\n \"\"\n );\n for (const subject of mod.intent) {\n out.push(`- ${subject}`);\n }\n } else {\n out.push(\n `No commit subjects could be read for this range${\n mod.noMergeBase ? ` (${mod.noMergeBase})` : \"\"\n } — read the diffs below to infer what the module intended.`\n );\n }\n out.push(\"\", \"### Provenance\", \"\");\n if (mod.baseRecord) {\n out.push(\n `- Module: \\`${mod.name}\\` (the template \\`saasaloy init\\` rendered)`,\n `- Source: \\`${mod.comparison.source}\\``,\n `- Recorded (your version): \\`${mod.comparison.current}\\` — CLI version, then template hash`,\n `- Running (new version): \\`${mod.comparison.latest}\\``\n );\n } else {\n out.push(\n `- Module: \\`${mod.name}\\``,\n `- Source: \\`${mod.comparison.source}\\``,\n `- Ref: \\`${mod.comparison.ref}\\``,\n `- Base (your version): \\`${mod.comparison.current}\\``,\n `- Theirs (new version): \\`${mod.comparison.latest}\\``\n );\n }\n if (mod.noMergeBase) {\n out.push(\n `- **no merge base — ${mod.noMergeBase}**: the original file can't be refetched, so the ` +\n \"diffs below are two-way (theirs vs. yours) and you must judge which side each \" +\n \"difference came from.\"\n );\n }\n out.push(\"\");\n\n const mergeFiles = mod.files.filter(isMergeFile);\n const droppedFiles = mod.removals.filter(isMergeFile);\n if (mergeFiles.length > 0 || droppedFiles.length > 0) {\n out.push(\"### Files to merge\", \"\");\n for (const file of mergeFiles) {\n out.push(...renderFile(file));\n }\n for (const file of droppedFiles) {\n out.push(...renderDroppedFile(file));\n }\n }\n\n const movedPatches = mod.patches.filter((p) => p.matched !== undefined);\n if (movedPatches.length > 0) {\n out.push(\n \"### Config patches that moved\",\n \"\",\n \"These config edits were skipped: the key the module identifies its entry by is already \" +\n \"present, holding a value that isn't the one the new version declares. That is almost \" +\n \"always a setting you changed on purpose, so nothing was written.\",\n \"\"\n );\n for (const patch of movedPatches) {\n out.push(...renderPatch(patch));\n }\n }\n\n return out;\n}\n\nconst ACTION_HEADING: Record<string, string> = {\n drift: \"you edited this file after it was applied\",\n conflict: \"new file collides with yours\",\n \"delete-drift\": \"the module dropped this file\",\n};\n\nfunction renderFile(file: PlannedUpdateFile): string[] {\n const out: string[] = [\n `#### \\`${file.target}\\``,\n \"\",\n `Source in the ${file.module === BASE_MODULE ? \"template\" : \"module\"}: \\`${file.from}\\` — ${ACTION_HEADING[file.action] ?? file.action}.`,\n \"\",\n ];\n\n if (file.action === \"conflict\") {\n out.push(\n file.base === undefined\n ? \"The new version of the module ships this file, but a file already sits at that path \" +\n \"that Saasaloy never wrote. There is no base for it — the module didn't have it at \" +\n \"your current version — so this is a two-way comparison.\"\n : \"The new version of the module ships this file, but a file already sits at that path \" +\n \"that Saasaloy never wrote, so nothing records what it looked like when it was \" +\n \"applied. The module's own older version of the file is available and is used as \" +\n \"the base below — but the on-disk file was never tracked against it, so treat it as \" +\n \"independent work rather than as edits on top of that base.\",\n \"\"\n );\n }\n\n if (file.patchedBy && file.patchedBy.length > 0) {\n out.push(\n `Note: \\`${file.patchedBy.join(\"`, `\")}\\` also applied a config patch to this file. Part of ` +\n \"the difference below is that patch rather than an edit anyone made by hand — keep those \" +\n \"lines when you merge.\",\n \"\"\n );\n }\n\n const base = file.base;\n const theirs = file.theirs ?? \"\";\n const mine = file.mine ?? \"\";\n\n if (base === undefined) {\n out.push(\n \"**theirs → mine** (no base available — two-way):\",\n \"\",\n ...fenced(toDiff(theirs, mine, file.target), \"diff\")\n );\n } else {\n out.push(\n \"**base → theirs** (what the module changed):\",\n \"\",\n ...fenced(toDiff(base, theirs, file.target), \"diff\"),\n \"**base → mine** (what you changed):\",\n \"\",\n ...fenced(toDiff(base, mine, file.target), \"diff\")\n );\n }\n return out;\n}\n\nfunction renderDroppedFile(file: PlannedUpdateFile): string[] {\n const out: string[] = [\n `#### \\`${file.target}\\``,\n \"\",\n `Source in the module: \\`${file.from}\\`. The new version of the module dropped this file, ` +\n \"but you had edited it — so it was left exactly as it is and is now yours alone \" +\n \"(Saasaloy no longer tracks it). Your edits are still here; decide whether the work \" +\n \"they represent still belongs in the project now the module no longer ships it.\",\n \"\",\n ];\n if (file.base !== undefined && file.mine !== undefined) {\n out.push(\n \"**base → mine** (what you changed before it was dropped):\",\n \"\",\n ...fenced(toDiff(file.base, file.mine, file.target), \"diff\")\n );\n }\n return out;\n}\n\nfunction renderPatch(patch: PlannedUpdatePatch): string[] {\n const matched = patch.matched;\n if (!matched) {\n return [];\n }\n return [\n `#### \\`${patch.file}\\` — \\`${patch.patch.kind}\\``,\n \"\",\n `- Identity: \\`${matched.key}\\``,\n `- On disk: \\`${JSON.stringify(matched.current)}\\``,\n `- The new version declares: \\`${JSON.stringify(matched.wanted)}\\``,\n \"\",\n \"Reconcile these by hand if the new value matters; keep yours if it was deliberate.\",\n \"\",\n ];\n}\n\n/**\n * Where the lock is left, said concretely when every merging module shares one base SHA\n * and generically when a bare `update` collected several.\n */\nfunction lockStaysOn(plan: UpdatePlan): string {\n const bases = [\n ...new Set(\n plan.modules.filter((m) => m.needsMerge).map((m) => m.comparison.current)\n ),\n ];\n return bases.length === 1\n ? `the lock stays on \\`${bases[0]}\\``\n : \"each module's lock entry stays on the base SHA named in its Provenance section\";\n}\n\nfunction renderInstructions(plan: UpdatePlan): string[] {\n const out: string[] = [\n \"## Instructions\",\n \"\",\n \"You are reconciling two sets of changes to the same files: the module's (base → theirs) \" +\n \"and the user's (base → mine). Work file by file.\",\n \"\",\n \"1. Read the intent above to understand what the module was trying to fix or add.\",\n \"2. For each file, apply the module's change to the file **as it currently stands on disk** \" +\n \"— the on-disk copy is `mine` and it is the version to edit.\",\n \"3. **The user's edits must not be lost.** Where the two changes touch the same lines, keep \" +\n \"the user's intent and re-express the module's fix on top of it. If the two genuinely \" +\n \"conflict and you cannot preserve both, leave the user's version in place and say so \" +\n \"rather than choosing for them.\",\n \"4. Don't touch files that aren't listed here — everything else was already updated cleanly.\",\n \"5. Don't edit `.saasaloy/manifest.json` or `saasaloy-lock.json` — Saasaloy owns both.\",\n \"\",\n \"### What Saasaloy records afterwards\",\n \"\",\n \"Nothing, and that is deliberate. Saasaloy has no way to tell a finished merge from an \" +\n \"unfinished one, so it does not mark this plan resolved: the files above stay tracked at \" +\n `the module's old version and ${lockStaysOn(plan)}. A later ` +\n \"`saasaloy update` will offer the same files again, against the same base, until a file \" +\n \"either matches the new version byte for byte or the module stops shipping it. The old \" +\n \"SHA is the only merge base that exists — advancing it would leave these files with \" +\n \"nothing to diff against, which is why the merge is yours to keep, not Saasaloy's to \" +\n \"record.\",\n \"\",\n \"## Verification\",\n \"\",\n \"Run these after merging — `saasaloy update` deliberately runs nothing itself:\",\n \"\",\n `- \\`${plan.verifyCommand}\\``,\n ];\n if (plan.migrationCommand) {\n out.push(\n `- \\`${plan.migrationCommand}\\` — the update changed the database schema`\n );\n }\n out.push(\"\");\n return out;\n}\n\n/**\n * Wrap content in a fence long enough to survive whatever it contains — a merge plan\n * routinely carries markdown files that hold their own code fences, and a three-backtick\n * fence would end the block early and corrupt the document an agent reads.\n */\nfunction fenced(content: string, language: string): string[] {\n let longest = 0;\n for (const match of content.matchAll(/`{3,}/g)) {\n longest = Math.max(longest, match[0].length);\n }\n const fence = \"`\".repeat(Math.max(3, longest + 1));\n return [`${fence}${language}`, content.replace(/\\n$/, \"\"), fence, \"\"];\n}\n","import { runAdd } from \"./add.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\nimport { runDoctor } from \"./doctor.js\";\nimport { runEnv } from \"./env.js\";\nimport { runInit } from \"./init.js\";\nimport { runList } from \"./list.js\";\nimport { runNew } from \"./new.js\";\nimport { runOutdated } from \"./outdated.js\";\nimport { runRemove } from \"./remove.js\";\nimport { runUpdate } from \"./update.js\";\n\n// The command registry. It is the single source both consumers in `../cli.ts` read —\n// `printHelp()` and the bare-invocation picker — so a new command is one entry here and\n// shows up in help and in the picker with no second list to keep in sync.\n\nexport interface Command {\n describe: string;\n run: (argv: string[]) => Promise<number> | number;\n}\n\nexport type CommandRegistry = Record<string, Command>;\n\n// Insertion order is deliberate: it is the lifecycle a user walks (scaffold → compose →\n// configure → inspect → refresh → undo → browse), then the author-facing `new` and\n// `doctor` — the pair a registry author uses and a consumer never does — and it\n// is the order both help and the picker render. `env` sits directly after `add` because\n// it is the step `add`'s own next-steps note sends you to, and `outdated` sits directly\n// before `update` because it is the question `update` is the answer to. Append with that\n// in mind. No explicit `order`/`group` field until the list outgrows a single screen.\nexport const COMMANDS: CommandRegistry = {\n init: { describe: DESCRIPTIONS.init, run: runInit },\n add: { describe: DESCRIPTIONS.add, run: runAdd },\n env: { describe: DESCRIPTIONS.env, run: runEnv },\n outdated: { describe: DESCRIPTIONS.outdated, run: runOutdated },\n update: { describe: DESCRIPTIONS.update, run: runUpdate },\n remove: { describe: DESCRIPTIONS.remove, run: runRemove },\n list: { describe: DESCRIPTIONS.list, run: runList },\n new: { describe: DESCRIPTIONS.new, run: runNew },\n doctor: { describe: DESCRIPTIONS.doctor, run: runDoctor },\n};\n","#!/usr/bin/env node\n// saasaloy CLI entrypoint. Bootstrap only: it resolves an exit code and hands it to\n// process.exit. Argument dispatch lives in cli.ts and the command registry in\n// commands/index.ts, so both can be imported by a test without running the CLI.\n\nimport { main } from \"./cli.js\";\nimport { exitCodeFor, formatFailure } from \"./lib/exit.js\";\n\nmain(process.argv.slice(2)).then(\n (code) => process.exit(code),\n (error: unknown) => {\n // formatFailure prints the `cause` chain when SAASALOY_DEBUG is set; the chain\n // registry.ts attaches used to stop here and never reach a bug report (#98).\n console.error(formatFailure(error));\n process.exit(exitCodeFor(error));\n }\n);\n"],"mappings":"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","names":["cancel","pc","posix","pc","mkdir","readFile","writeFile","dirname","posix","describe","resolved","mkdir","readFile","dirname","join","readFile","join","join","readFile","join","readFile","mkdir","dirname","parseModule","edits","applyEdits","findNodeAtLocation","getNodeValue","modify","parseTree","parseTree","findNodeAtLocation","getNodeValue","edits","modify","applyEdits","applyEdits","findNodeAtLocation","getNodeValue","modify","parseTree","parseTree","findNodeAtLocation","getNodeValue","edits","modify","applyEdits","builders","generateCode","parseModule","parseModule","builders","removeBindingConflict","generateCode","readFile","writeFile","join","join","readFile","writeFile","posix","readFile","planned","mkdir","dirname","writeFile","partial","describe","describe","mkdir","readFile","writeFile","dirname","posix","posix","text","readFile","mkdir","dirname","writeFile","text","i","j","readFile","writeFile","join","join","readFile","writeFile","readFile","writeFile","join","version","join","readFile","writeFile","dirname","join","join","dirname","readFile","isAbsolute","join","resolve","isAbsolute","resolve","join","readFile","describe","text","describe","readFile","fileURLToPath","UNKNOWN_VERSION","pc","text","posix","cancel","intro","log","note","outro","join","relative","pc","createHash","mkdtemp","readdir","readFile","rm","tmpdir","join","posix","mkdir","readdir","readFile","writeFile","join","posix","fileURLToPath","fileURLToPath","join","mkdir","readdir","posix","readFile","writeFile","join","readdir","posix","createHash","readFile","mkdtemp","tmpdir","rm","readFile","basename","dirname","join","posix","resolve","join","readFile","basename","posix","resolve","dirname","KNOWN_FLAGS","USAGE","HELP","parseArgs","pc","relative","note","outro","cancel","intro","join","log","mkdir","readFile","writeFile","dirname","isAbsolute","resolve","cancel","intro","isCancel","log","note","outro","select","pc","posix","posix","readFile","dirname","join","relative","resolve","sep","text","resolve","join","dirname","relative","sep","readFile","KNOWN_FLAGS","USAGE","HELP","parseArgs","pc","isAbsolute","resolve","mkdir","dirname","writeFile","readFile","intro","cancel","log","outro","note","select","isCancel","readdir","basename","join","resolve","cancel","intro","isCancel","note","outro","select","text","pc","log","pc","KNOWN_FLAGS","USAGE","HELP","parseArgs","join","intro","pc","cancel","text","basename","resolve","isCancel","readdir","note","select","outro","cancel","intro","note","outro","join","pc","KNOWN_FLAGS","USAGE","HELP","parseArgs","join","intro","pc","cancel","note","outro","join","relative","resolve","cancel","intro","isCancel","note","outro","select","text","pc","mkdir","writeFile","dirname","join","NAME_PATTERN","SKILL_PREFIX","join","mkdir","dirname","writeFile","KNOWN_FLAGS","USAGE","HELP","parseArgs","pc","intro","cancel","join","text","isCancel","resolve","select","relative","note","outro","cancel","intro","log","note","outro","pc","mkdir","readFile","rmPath","writeFile","dirname","posix","readdir","readFile","writeFile","dirname","join","relative","sep","readFile","relative","sep","dirname","readdir","join","writeFile","version","WRITABLE","readFile","posix","stillMatches","mkdir","dirname","writeFile","rmPath","KNOWN_FLAGS","USAGE","HELP","parseArgs","SHA_PATTERN","short","pc","text","intro","cancel","log","note","outro","cancel","confirm","intro","isCancel","log","note","outro","select","pc","KNOWN_FLAGS","USAGE","HELP","parseArgs","pc","MAX_DIFF_LINES","note","log","intro","cancel","outro","select","isCancel","confirm","mkdir","writeFile","basename","dirname","isAbsolute","join","resolve","cancel","confirm","intro","isCancel","log","note","outro","pc","KNOWN_FLAGS","VALUE_FLAGS","USAGE","HELP","parseArgs","ACTION_LABEL","pc","MAX_DIFF_LINES","renderDiff","short","summarizePlan","note","log","resolve","join","dirname","basename","isAbsolute","mkdir","writeFile","splitSlug","intro","cancel","outro","document","confirm","isCancel","pc","select","isCancel","command","cancel"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/add.ts","../src/lib/applier.ts","../src/lib/exit.ts","../src/lib/collisions.ts","../src/lib/fs-utils.ts","../src/lib/manifest.ts","../src/lib/schema.ts","../src/lib/patch/chained-route.ts","../src/lib/patch/ts-ast.ts","../src/lib/patch/const-array.ts","../src/lib/patch/diff.ts","../src/lib/patch/jsonc.ts","../src/lib/patch/pkg-json.ts","../src/lib/patch/pkg-json-script.ts","../src/lib/patch/ts-module.ts","../src/lib/patch/index.ts","../src/lib/saasaloy-config.ts","../src/lib/semver.ts","../src/lib/cli-requires.ts","../src/lib/conflicts.ts","../src/lib/design.ts","../src/lib/dev-vars.ts","../src/lib/diff.ts","../src/lib/lock.ts","../src/lib/pkg-json.ts","../src/lib/project.ts","../src/lib/registry.ts","../src/lib/requires.ts","../src/lib/resolve.ts","../src/lib/tui.ts","../src/lib/ui-blocks.ts","../src/lib/usage.ts","../src/version.ts","../src/commands/descriptions.ts","../src/commands/doctor.ts","../src/lib/base.ts","../src/lib/scaffold.ts","../package.json","../src/lib/doctor.ts","../src/commands/env.ts","../src/lib/env-vars.ts","../src/lib/gitignore.ts","../src/commands/init.ts","../src/lib/logger.ts","../src/commands/list.ts","../src/commands/new.ts","../src/lib/new-module.ts","../src/commands/outdated.ts","../src/lib/updater.ts","../src/lib/remover.ts","../src/commands/remove.ts","../src/commands/update.ts","../src/lib/backup.ts","../src/lib/git-state.ts","../src/lib/project-name.ts","../src/lib/merge-plan.ts","../src/commands/index.ts","../src/index.ts"],"sourcesContent":["import {\n cancel,\n isCancel as clackIsCancel,\n select as clackSelect,\n} from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport type { CommandRegistry } from \"./commands/index.js\";\nimport { COMMANDS } from \"./commands/index.js\";\nimport { EXIT_FAILURE, EXIT_OK, EXIT_REFUSED } from \"./lib/exit.js\";\nimport { isInteractive } from \"./lib/tui.js\";\nimport { readVersion } from \"./version.js\";\n\n// Re-exported so `saasaloy --version` and this module's tests keep one import site; the\n// reader itself lives in version.ts, which commands/add.ts also imports (#50).\nexport { readVersion } from \"./version.js\";\n\n// Argument parsing and dispatch. Kept out of index.ts — which only bootstraps this and\n// maps the resolved code onto process.exit — so a test can import it without running the\n// CLI. Everything the picker needs from the outside world (the registry, the prompt) is a\n// defaulted parameter, which is the test seam: this package mocks no modules anywhere.\n\n/** The slice of clack's `select` the picker uses, narrowed to string-valued options. */\nexport type SelectPrompt = (opts: {\n message: string;\n options: { value: string; label: string; hint?: string }[];\n}) => Promise<string | symbol>;\n\nexport interface CliDeps {\n registry: CommandRegistry;\n select: SelectPrompt;\n /** clack's cancel sentinel is module-private, so recognising it is injectable too. */\n isCancel: (value: unknown) => value is symbol;\n}\n\nexport function printHelp(registry: CommandRegistry): void {\n console.log(\n `${pc.bold(\"saasaloy\")} ${pc.dim(\"— composable SaaS accelerator for Cloudflare\")}\\n`\n );\n console.log(\n `${pc.bold(\"Usage:\")} saasaloy ${pc.cyan(\"<command>\")} [options]\\n`\n );\n console.log(pc.bold(\"Commands:\"));\n for (const [name, command] of Object.entries(registry)) {\n console.log(` ${pc.cyan(name.padEnd(8))} ${pc.dim(command.describe)}`);\n }\n console.log(`\\n${pc.bold(\"Options:\")}`);\n console.log(\n ` ${pc.cyan(\"-h, --help\".padEnd(14))} ${pc.dim(\"show this help, or a command's own usage\")}`\n );\n console.log(\n ` ${pc.cyan(\"-v, --version\".padEnd(14))} ${pc.dim(\"print the installed saasaloy version\")}`\n );\n console.log(`\\n${pc.bold(\"Exit codes:\")}`);\n console.log(\n ` ${pc.cyan(\"0\".padEnd(14))} ${pc.dim(\"done, or you answered no to the confirm\")}`\n );\n console.log(\n ` ${pc.cyan(\"1\".padEnd(14))} ${pc.dim(\"something failed, or you cancelled\")}`\n );\n console.log(\n ` ${pc.cyan(\"2\".padEnd(14))} ${pc.dim(\"saasaloy refused: bad usage, a conflict, an unmet requirement\")}`\n );\n console.log(\n `\\n${pc.dim(\"Set SAASALOY_DEBUG=1 to print the full cause chain behind a failure.\")}`\n );\n}\n\n// Bare invocation on a terminal: show what the tool can do and run the choice, rather\n// than making a newcomer read a list, quit, and retype. Options are mapped from the\n// registry, never listed by hand, and each carries its `describe` as a hint — which is\n// why no help is printed above the picker; that would render the same list twice.\n// No intro()/outro() here either: the chosen command opens its own, so the handoff reads\n// as one continuous clack rail instead of two stacked boxes.\nasync function pickCommand(deps: CliDeps): Promise<number> {\n const { registry, select, isCancel } = deps;\n const picked = await select({\n message: \"What would you like to do?\",\n options: Object.entries(registry).map(([name, command]) => ({\n value: name,\n label: name,\n hint: command.describe,\n })),\n });\n if (isCancel(picked)) {\n cancel(\"cancelled\");\n return EXIT_FAILURE;\n }\n // The options were mapped from this registry's own keys, so a miss can't happen; the\n // guard is what lets TypeScript narrow the lookup.\n const command = registry[picked];\n if (!command) {\n return EXIT_FAILURE;\n }\n // Hand off with an empty argv: every command already asks for what it needs (`init`\n // prompts for a name, `add`/`remove` open their module pickers), so the picker\n // deliberately teaches itself nothing about any command's argument shape.\n return command.run([]);\n}\n\nexport async function main(\n argv: string[],\n deps: Partial<CliDeps> = {}\n): Promise<number> {\n const {\n registry = COMMANDS,\n select = clackSelect,\n isCancel = clackIsCancel,\n } = deps;\n const [name, ...rest] = argv;\n\n // Explicit help is never the picker, on a TTY as much as anywhere.\n if (name === \"--help\" || name === \"-h\" || name === \"help\") {\n printHelp(registry);\n return EXIT_OK;\n }\n\n // `--version` before dispatch, so it answers even when the project is unscaffolded.\n if (name === \"--version\" || name === \"-v\" || name === \"version\") {\n console.log(await readVersion());\n return EXIT_OK;\n }\n\n if (!name) {\n // No TTY (CI, piped stdin, `saasaloy | cat`) → the exact help and exit code this\n // printed before the picker existed. A prompt nobody can answer would hang.\n if (!isInteractive()) {\n printHelp(registry);\n return EXIT_OK;\n }\n return pickCommand({ registry, select, isCancel });\n }\n\n // Own keys only: a plain object inherits `toString`, `constructor`, `valueOf` and the\n // rest from Object.prototype, and every one of them is truthy — so a bare `registry[name]`\n // lets `saasaloy toString` past this guard and dies on `.run` instead of saying what it\n // should, that the command is unknown.\n const command = Object.hasOwn(registry, name) ? registry[name] : undefined;\n if (!command) {\n console.error(`${pc.red(\"Unknown command:\")} ${name}\\n`);\n printHelp(registry);\n // Bad usage is a refusal, not a failure — nothing broke, the input was wrong.\n return EXIT_REFUSED;\n }\n\n return command.run(rest);\n}\n","import {\n cancel,\n confirm,\n intro,\n isCancel,\n log,\n note,\n outro,\n select,\n} from \"@clack/prompts\";\nimport { posix } from \"node:path\";\nimport pc from \"picocolors\";\nimport { buildPlan, executePlan } from \"../lib/applier.js\";\nimport type {\n ApplyResult,\n FileAction,\n Plan,\n PlannedFile,\n} from \"../lib/applier.js\";\nimport {\n detectCliMismatches,\n formatCliMismatches,\n} from \"../lib/cli-requires.js\";\nimport { describeStaleOwner } from \"../lib/collisions.js\";\nimport { detectConflicts, formatConflicts } from \"../lib/conflicts.js\";\nimport {\n EXIT_FAILURE,\n EXIT_OK,\n EXIT_REFUSED,\n exitCodeFor,\n formatFailure,\n} from \"../lib/exit.js\";\nimport { planWritesUi } from \"../lib/design.js\";\nimport { DEV_VARS_EXAMPLE, writeDevVarsExample } from \"../lib/dev-vars.js\";\nimport { lineDiff } from \"../lib/diff.js\";\nimport type { DiffLine } from \"../lib/diff.js\";\nimport { loadLock, LOCK_FILE, saveLock, upsertLock } from \"../lib/lock.js\";\nimport type { Lockfile } from \"../lib/lock.js\";\nimport { loadManifest, MANIFEST_FILE, saveManifest } from \"../lib/manifest.js\";\nimport type { Manifest } from \"../lib/manifest.js\";\nimport { planDeps, readRootPackageJson, writeDeps } from \"../lib/pkg-json.js\";\nimport { findProjectRoot } from \"../lib/project.js\";\nimport {\n createRegistrySource,\n DEFAULT_OWNER,\n DEFAULT_REPO,\n parseCoordinate,\n REGISTRY_ENV,\n} from \"../lib/registry.js\";\nimport type { Coordinate, RegistrySource } from \"../lib/registry.js\";\nimport {\n detectMissingRequirements,\n formatMissingRequirements,\n} from \"../lib/requires.js\";\nimport { mergeGraph, resolveGraph } from \"../lib/resolve.js\";\nimport type { Graph } from \"../lib/resolve.js\";\nimport { CONFIG_FILE, loadConfig, saveConfig } from \"../lib/saasaloy-config.js\";\nimport type { LoadedConfig } from \"../lib/saasaloy-config.js\";\nimport { isInteractive, wrapForNote } from \"../lib/tui.js\";\nimport { uiBlockFiles } from \"../lib/ui-blocks.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { readVersion } from \"../version.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\n\n// `saasaloy add <module>` — the local applier. Resolve the dependsOn graph, show the\n// plan behind a confirmation prompt, then drop files into their convention-based\n// targets, record them in .saasaloy/manifest.json with content hashes, and merge npm\n// deps (build spec §2.4/§2.7/§2.9). `--dry-run`/`--diff` preview without mutating.\n\nexport interface AddOptions {\n name?: string;\n dryRun: boolean;\n diff: boolean;\n yes: boolean;\n force: boolean;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\n \"--dry-run\",\n \"--diff\",\n \"--yes\",\n \"-y\",\n \"--force\",\n \"--help\",\n \"-h\",\n]);\nconst USAGE =\n \"saasaloy add [<module>|<owner/repo[@ref]/module>|<owner/repo>] [--dry-run] [--diff] [--yes] [--force]\";\nconst HELP: CommandHelp = {\n name: \"add\",\n describe: DESCRIPTIONS.add,\n usage: USAGE,\n flags: {\n \"--dry-run\": \"show the plan and write nothing\",\n \"--diff\": \"show a per-file diff and write nothing\",\n \"-y, --yes\": \"skip the confirmation prompt\",\n \"--force\": \"re-apply a module that is already installed\",\n },\n};\n\n/**\n * Split `add`'s argv into options. Exported for its own tests: the rejection rules here\n * (an unknown flag, a second positional) are the difference between `--forse` doing\n * nothing and `--forse` being reported, and they are worth pinning without driving a\n * whole apply to reach them.\n */\nexport function parseArgs(argv: string[]): AddOptions {\n const positional: string[] = [];\n const unknown: string[] = [];\n for (const arg of argv) {\n if (!arg.startsWith(\"-\")) {\n positional.push(arg);\n } else if (!KNOWN_FLAGS.has(arg)) {\n // A typo'd flag (`--forse`) silently running without force is worse than an error.\n unknown.push(arg);\n }\n }\n unknown.push(...positional.slice(1));\n return {\n diff: argv.includes(\"--diff\"),\n dryRun: argv.includes(\"--dry-run\"),\n force: argv.includes(\"--force\"),\n name: positional[0],\n unknown,\n yes: argv.includes(\"--yes\") || argv.includes(\"-y\"),\n };\n}\n\n/**\n * Pin a coordinate to the SHA the lock recorded for it, so a re-install reproduces\n * identical bytes (ADR 0012). Three conditions hold it back, and each one means the user\n * asked for something else: `SAASALOY_REGISTRY_DIR` points at a working copy, an explicit\n * `@ref` names the ref to move to, or the entry was installed from a local checkout and\n * carries no SHA to pin to. Returns `coord` unchanged when none applies.\n *\n * A separate function from `runAdd` because reaching this branch through the command\n * means resolving a remote source, and the rule is worth testing on its own.\n */\nexport function pinToLock(coord: Coordinate, lock: Lockfile): Coordinate {\n if (process.env[REGISTRY_ENV] || !coord.module || coord.ref) {\n return coord;\n }\n const slug = `${coord.owner ?? DEFAULT_OWNER}/${coord.repo ?? DEFAULT_REPO}`;\n const pinned = lock.modules[coord.module];\n if (!pinned || pinned.source !== slug || pinned.resolved === \"local\") {\n return coord;\n }\n return { ...coord, ref: pinned.resolved };\n}\n\nconst ACTION_LABEL: Record<FileAction, string> = {\n conflict: pc.yellow(\"conflict → merge\"),\n create: pc.green(\"create\"),\n drift: pc.yellow(\"drift → merge\"),\n overwrite: pc.cyan(\"overwrite\"),\n unchanged: pc.dim(\"unchanged\"),\n};\n\n// Cap a single file's diff so a big generated file can't flood the terminal.\nconst MAX_DIFF_LINES = 60;\n\n// How many `requiresOneOf` prompts one `add` may open. Each pick settles one requirement,\n// so this is only reached by a chain of modules that each require the next.\nconst MAX_REQUIREMENT_PROMPTS = 8;\n\n// Keyed by DiffLine[\"kind\"], so adding a kind to lib/diff.ts is a type error here\n// rather than a silently unstyled line.\nconst DIFF_LINE_STYLE: Record<DiffLine[\"kind\"], (text: string) => string> = {\n add: (text) => pc.green(`+ ${text}`),\n context: (text) => pc.dim(` ${text}`),\n del: (text) => pc.red(`- ${text}`),\n};\n\nfunction renderDiff(file: PlannedFile): string {\n const lines = lineDiff(file.oldContent ?? \"\", file.content);\n const shown = lines\n .slice(0, MAX_DIFF_LINES)\n .map((line) => DIFF_LINE_STYLE[line.kind](line.text));\n if (lines.length > MAX_DIFF_LINES) {\n shown.push(pc.dim(` … ${lines.length - MAX_DIFF_LINES} more lines`));\n }\n return shown.join(\"\\n\");\n}\n\n/**\n * How the plan names one file. A target alone identifies it, until `onlyWith` lets two\n * descriptor entries share one target (#99) — then the plan has to say which variant it\n * picked, because that is the whole decision the user would want to check. An\n * unconditional file's line is unchanged, so the common plan reads as it always did.\n */\nfunction fileLabel(file: PlannedFile): string {\n return file.onlyWith === undefined\n ? file.target\n : `${file.target} ${pc.dim(`(${file.from})`)}`;\n}\n\nfunction summarizePlan(plan: Plan, requested: string, prereqs: string[]): void {\n if (prereqs.length > 0) {\n note(\n wrapForNote(\n `${pc.bold(requested)} requires: ${prereqs.map((p) => pc.cyan(p)).join(\", \")}`\n ),\n \"Dependencies\"\n );\n }\n const willInstall = plan.install.map((m) => pc.cyan(m)).join(pc.dim(\" → \"));\n const lines = [`will install: ${willInstall}`];\n if (plan.alreadyInstalled.length > 0) {\n lines.push(\n pc.dim(`already installed (skipped): ${plan.alreadyInstalled.join(\", \")}`)\n );\n }\n\n const writable = plan.files.filter(\n (f) => f.action !== \"drift\" && f.action !== \"conflict\"\n );\n const held = plan.files.filter(\n (f) => f.action === \"drift\" || f.action === \"conflict\"\n );\n lines.push(\"\");\n for (const file of plan.files) {\n lines.push(` ${ACTION_LABEL[file.action]} ${fileLabel(file)}`);\n }\n lines.push(\n \"\",\n pc.dim(`${writable.length} file(s) to apply, ${held.length} needing merge`)\n );\n if (plan.dependencies.length > 0) {\n lines.push(pc.dim(`deps: ${plan.dependencies.join(\", \")}`));\n }\n if (plan.devDependencies.length > 0) {\n lines.push(pc.dim(`devDeps: ${plan.devDependencies.join(\", \")}`));\n }\n note(wrapForNote(lines.join(\"\\n\")), \"Plan\");\n\n if (Object.keys(plan.envVars).length > 0) {\n const envLines = Object.entries(plan.envVars).map(\n ([k, v]) => `${pc.cyan(k)} ${pc.dim(`— ${v}`)}`\n );\n note(wrapForNote(envLines.join(\"\\n\")), \"Env vars to set\");\n }\n if (Object.keys(plan.aliases).length > 0) {\n const aliasLines = Object.entries(plan.aliases).map(\n ([a, p]) => `${pc.cyan(a)} ${pc.dim(`→ ${p}`)}`\n );\n note(\n wrapForNote(\n `${aliasLines.join(\"\\n\")}\\n\\n${pc.dim(\"New workspace(s) — run `pnpm install` to link them.\")}`\n ),\n \"Aliases registered\"\n );\n }\n for (const conflict of plan.aliasConflicts) {\n log.warn(\n `Alias redefinition: ${conflict} ${pc.dim(\"(scaffold overrides the existing alias)\")}.`\n );\n }\n // The sentence lives in collisions.ts beside the ownership refusal it mirrors, so the\n // two ownership messages cannot drift apart; the path is accented here the way every\n // other `add` warning accents one (#107).\n for (const stale of plan.staleOwners) {\n log.warn(describeStaleOwner({ ...stale, target: pc.cyan(stale.target) }));\n }\n const newLinks = plan.links.filter((l) => l.action !== \"conflict\");\n if (newLinks.length > 0) {\n const linkLines = newLinks.map(\n (l) => `${pc.cyan(l.path)} ${pc.dim(`→ ${l.target}`)}`\n );\n note(\n wrapForNote(\n `${linkLines.join(\"\\n\")}\\n\\n${pc.dim(\"Symlinked for Claude Code — the skill files live in `.agents/skills/`.\")}`\n ),\n \"Skill links\"\n );\n }\n for (const link of plan.links) {\n if (link.action === \"conflict\") {\n log.warn(\n `Skill link ${pc.cyan(link.path)} already exists and isn't ours — ` +\n `left untouched ${pc.dim(\"(remove it to let `add` link the skill)\")}.`\n );\n }\n }\n const applyPatches = plan.patches.filter((p) => p.action === \"apply\");\n if (applyPatches.length > 0) {\n const patchLines = applyPatches.map(\n (p) => `${pc.cyan(p.file)} ${pc.dim(`— ${p.patch.kind}`)}`\n );\n note(wrapForNote(patchLines.join(\"\\n\")), \"Config patches\");\n }\n for (const p of plan.patches) {\n if (p.action === \"missing\") {\n log.warn(\n `Config patch target ${pc.cyan(p.file)} is missing — ${p.patch.kind} not applied ` +\n `${pc.dim(`(is ${p.module}'s prerequisite installed?)`)}.`\n );\n }\n }\n}\n\n/**\n * A block the applier wrote into the ui package renders nothing until the owner imports\n * it, because `add` never edits a page — where a block belongs is the owner's decision,\n * not filename sort's (ADR 0030). One line per module: which file arrived, and the skill\n * command whose Wire-up section carries the import and the tag.\n *\n * The `@ui/blocks/` target is the whole signal, so a module that ships one without a\n * skill has nowhere to keep those steps. That is the module author's bug, and it warns\n * rather than refuses — the file itself is fine and the user asked for it.\n */\nfunction wireUpSteps(\n result: ApplyResult,\n aliases: Record<string, string>\n): string[] {\n const blocks = uiBlockFiles(\n [...result.written, ...result.refreshed],\n aliases\n );\n if (blocks.length === 0) {\n return [];\n }\n\n const skillByModule = new Map(\n result.links.map((link) => [link.module, posix.basename(link.path)])\n );\n const byModule = new Map<string, string[]>();\n for (const block of blocks) {\n byModule.set(block.module, [\n ...(byModule.get(block.module) ?? []),\n block.target,\n ]);\n }\n\n const lines: string[] = [];\n for (const module of [...byModule.keys()].toSorted()) {\n const moduleTargets = (byModule.get(module) ?? []).toSorted();\n const targets = moduleTargets.map((target) => pc.cyan(target)).join(\", \");\n const verb = moduleTargets.length === 1 ? \"is\" : \"are\";\n const skill = skillByModule.get(module);\n if (skill === undefined) {\n log.warn(\n `${pc.cyan(module)} wrote ${targets} but ships no skill — its wire-up steps are ` +\n `missing. Import the block yourself ${pc.dim(\"(a module that ships a block should ship a skill with a Wire-up section)\")}.`\n );\n continue;\n }\n lines.push(\n `Manual wire-up needed — ${targets} ${verb} on disk but on no page yet. ` +\n `In Claude Code, run ${pc.cyan(`/${skill}`)} and follow its Wire-up section.`\n );\n }\n return lines;\n}\n\n/**\n * The environment half of the next-steps box.\n *\n * The variable names still print — they are what a reader scans for. What changed in #50\n * is the instruction under them. `add` used to say \"copy `.dev.vars.example` to\n * `.dev.vars` and fill it in\", which was the whole procedure and did nothing for a\n * `PUBLIC_*` value, whose home is a frontend `.env` the example file never described.\n * `saasaloy env` does both, so this points at it and leaves the example file as what it\n * is: the checked-in record of what each variable means.\n *\n * Exported so the wording is pinned by a test — it is the one line telling a new user\n * the next command exists.\n */\nexport function envSteps(\n names: string[],\n devVarsPath: string | undefined\n): string[] {\n if (names.length === 0) {\n return [];\n }\n const keys = names.toSorted();\n const where = devVarsPath\n ? ` ${pc.dim(`${devVarsPath} keeps the checked-in description of each.`)}`\n : \"\";\n return [\n `Set ${keys.map((key) => pc.cyan(key)).join(\", \")}.`,\n `Run ${pc.cyan(\"saasaloy env\")} — it prompts for each unset one and writes it to the ${pc.cyan(\".dev.vars\")} or ${pc.cyan(\".env\")} that reads it.${where}`,\n ];\n}\n\n/**\n * The line an incomplete apply ends on (#49). A module whose file was edited between the\n * plan and the write is not installed, and staying quiet about it is the bug: the state\n * files say one thing and the closing \"Applied\" would say another. Name the modules, and\n * name the command that finishes the job — a plain re-run, because they are absent from\n * `config.installed` and so get planned again, drift and all, for a fresh approval.\n *\n * `requested` is the module the user typed, not each incomplete name: re-running it\n * re-plans the whole graph, including an incomplete dependency.\n */\nexport function formatIncomplete(\n incomplete: string[],\n requested: string\n): string {\n const names = incomplete.map((name) => pc.cyan(name)).join(\", \");\n const verb = incomplete.length === 1 ? \"is\" : \"are\";\n const object = incomplete.length === 1 ? \"it\" : \"them\";\n return (\n `${names} ${verb} not installed — files changed while the plan was open. ` +\n `Re-run ${pc.cyan(`saasaloy add ${requested}`)} to complete ${object}.`\n );\n}\n\n/**\n * The line a thrown apply ends on (#49). `add` has no rollback by design: whatever\n * landed before the throw stays, and the manifest, config and lock are written to\n * describe it. That is only honest if the user is told, so state the model and name the\n * command that finishes the job — the same plain re-run an incomplete run asks for,\n * since the module never reached `config.installed`.\n */\nexport function formatRecovery(requested: string): string {\n return (\n \"Partial apply — the manifest, config and lock describe what landed. \" +\n `Re-run ${pc.cyan(`saasaloy add ${requested}`)} to complete it.`\n );\n}\n\n/** What `persistState` needs to write the three state files. */\nexport interface PersistInput {\n config: LoadedConfig;\n /** The modules `executePlan` reported complete; `[]` when the apply threw. */\n completed: string[];\n graph: Graph;\n lock: Lockfile;\n manifest: Manifest;\n root: string;\n source: Pick<RegistrySource, \"provenance\">;\n}\n\n/**\n * Write the manifest, the config and the lock, and never throw (#49).\n *\n * This runs on the way out of an apply, successful or not, so one broken step here must\n * not take the others with it — nor replace the apply error that sent us this way. Each\n * step is attempted on its own, a failure is reported, and the failures are returned for\n * the caller to decide about.\n *\n * The lock is skipped when the source has no commit SHA to pin to. `provenance()` throws\n * in that case, and it used to throw first, before any save ran: an unresolvable remote\n * cost the user the manifest as well, and reported itself instead of the real error. A\n * lock entry is a pin, not a record of disk, so having none is the correct answer.\n *\n * A separate function from `runAdd` because these rules are what the bug was, and they\n * need injectable failures to test.\n */\nexport async function persistState(input: PersistInput): Promise<unknown[]> {\n const failures: unknown[] = [];\n\n // Pin the source + ref + commit SHA per module (ADR 0012). Only the freshly installed\n // ones: an already-installed dependency keeps the SHA it was fetched at, so the lock\n // never misstates on-disk provenance.\n try {\n upsertLock(\n input.lock,\n input.source.provenance(),\n input.completed,\n input.graph\n );\n } catch (error) {\n failures.push(error);\n log.warn(`Couldn't pin ${LOCK_FILE} — ${formatFailure(error)}.`);\n }\n\n // Record whatever actually landed even if a mid-plan write failed — a written file the\n // manifest doesn't know about would classify as a conflict next run.\n const saves: [string, () => Promise<void>][] = [\n [MANIFEST_FILE, () => saveManifest(input.root, input.manifest)],\n [CONFIG_FILE, () => saveConfig(input.root, input.config)],\n [LOCK_FILE, () => saveLock(input.root, input.lock)],\n ];\n for (const [file, save] of saves) {\n try {\n await save();\n } catch (error) {\n failures.push(error);\n log.warn(`Couldn't write ${file} — ${formatFailure(error)}.`);\n }\n }\n\n return failures;\n}\n\n/** What `applyAndPersist` needs beyond the state files `persistState` writes. */\nexport interface ApplyInput extends Omit<PersistInput, \"completed\"> {\n /**\n * Called once the apply itself is over, before any state file is written (#49). This\n * is the edge of the window where a failure can leave files on disk, and the caller\n * needs it to decide whether the run owes the user a recovery line.\n */\n onApplied?: () => void;\n plan: Plan;\n}\n\n/**\n * Run the plan and record what it did, whichever way it goes (#98, #49).\n *\n * All three state files leave `add` through this one path, so no exit skips one of them.\n * They pin what the apply reported complete rather than what the plan intended, so a run\n * that threw pins nothing and `config.installed` and the lock never disagree, not even\n * under `--force`, where the module was already installed before the run.\n *\n * A failed apply propagates its own error; a failed save on an otherwise clean apply\n * propagates that instead, because a run whose state files didn't land is not a success.\n * The two are not the same failure, though, so `onApplied` marks the boundary between\n * them for the caller.\n */\nexport async function applyAndPersist(input: ApplyInput): Promise<ApplyResult> {\n let result: ApplyResult;\n try {\n result = await executePlan(\n input.plan,\n input.root,\n input.config,\n input.manifest\n );\n } catch (error) {\n await persistState({ ...input, completed: [] });\n throw error;\n }\n input.onApplied?.();\n\n const failures = await persistState({\n ...input,\n completed: result.completed,\n });\n if (failures.length > 0) {\n throw failures[0];\n }\n return result;\n}\n\n/**\n * The closing box: where the module's procedure is written down, and what the project\n * still needs from the operator. `add waitlist` used to end at \"Applied\" while the\n * project 500s until `db:generate` and `db:migrate:local` run, and the env vars it\n * printed at plan time had scrolled away behind the confirmation (#98).\n *\n * There is deliberately no `nextSteps` field on the descriptor. The skill is the single\n * source of a module's procedure; a second copy in the descriptor would drift from it,\n * and this box points at the skill rather than paraphrasing it.\n */\nfunction printNextSteps(\n plan: Plan,\n result: ApplyResult,\n devVarsPath: string | undefined,\n aliases: Record<string, string>\n): void {\n const lines: string[] = [...wireUpSteps(result, aliases)];\n\n const skills = result.links\n .map((link) => posix.basename(link.path))\n .toSorted();\n if (skills.length > 0) {\n lines.push(\n `Read the module's own procedure — in Claude Code, run ${skills\n .map((name) => pc.cyan(`/${name}`))\n .join(\" or \")}.`\n );\n }\n\n lines.push(...envSteps(Object.keys(plan.envVars), devVarsPath));\n\n if (lines.length === 0) {\n return;\n }\n note(wrapForNote(lines.join(\"\\n\\n\")), \"Next steps\");\n}\n\nexport async function runAdd(argv: string[]): Promise<number> {\n // Parse before anything answers, so `--help --forse` reports the typo instead of\n // printing help over it. Help is then answered before the intro rail opens, so it reads\n // as plain output a person can pipe into a pager rather than as a clack box.\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n intro(pc.bgCyan(pc.black(\" saasaloy add \")));\n\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n\n let coord: ReturnType<typeof parseCoordinate>;\n try {\n coord = parseCoordinate(opts.name);\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n }\n\n // The \"Proceed?\" confirm below reads stdin. With no terminal there it can never be\n // answered, so `saasaloy add x </dev/null` printed the whole plan, took the closed\n // stream as \"no\", and exited 0 having written nothing — a silent success. `update`\n // already refuses in this situation; both commands now answer it the same way (#98).\n // A preview writes nothing and needs no confirmation, so it is exempt. With no module\n // named the picker's own non-interactive refusal below is the more specific message,\n // so leave that case to it.\n if (\n coord.module &&\n !opts.yes &&\n !opts.dryRun &&\n !opts.diff &&\n !process.stdin.isTTY\n ) {\n cancel(\n \"No terminal to confirm in — re-run with `--yes` to apply, or `--dry-run` to preview.\"\n );\n return EXIT_REFUSED;\n }\n\n let root: string;\n let config: Awaited<ReturnType<typeof loadConfig>>;\n try {\n root = await findProjectRoot();\n config = await loadConfig(root);\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n }\n\n let plan: Plan;\n let prereqs: string[];\n let source: RegistrySource | undefined;\n // Set to the requested module once the apply starts. It tells the catch below whether\n // a failure could have left files on disk, so only those failures carry the re-run\n // instruction — a bad coordinate or a missing descriptor writes nothing (#49).\n let partial: string | undefined;\n try {\n // Load the lock up front so a named remote add can pin to the SHA it recorded — a\n // re-install then reproduces identical bytes (ADR 0012). Explicit `@ref` or the\n // `update` flow (#17) are the sanctioned ways to move off the lock.\n const lock = await loadLock(root);\n coord = pinToLock(coord, lock);\n\n source = createRegistrySource(coord);\n if (process.env[REGISTRY_ENV] && (coord.owner || coord.repo)) {\n log.warn(\n `Ignoring source \"${coord.owner}/${coord.repo}\" — ${REGISTRY_ENV} override is set.`\n );\n }\n\n // No module named (bare `add`, or `owner/repo` with no module) → pick from the source.\n let requested = coord.module;\n if (!requested) {\n // No terminal to pick in (CI, piped stdin, `saasaloy add | cat`) means the prompt\n // below could never be answered — it would hang the pipeline. Say what was missing\n // instead, before the registry is even fetched.\n if (!isInteractive()) {\n cancel(\n `No module named and no terminal to pick one in — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n const offered = await source.listModules();\n if (offered.length === 0) {\n cancel(`No modules found in ${source.label}.`);\n return EXIT_REFUSED;\n }\n // Picking something already installed took the user through a plan preview to\n // \"Nothing to do\", which reads as a bug in the picker rather than an answer.\n // `--force` re-applies one, and that path names the module explicitly (#98).\n const installed = new Set(config.installed);\n const available = offered.filter((n) => !installed.has(n));\n if (available.length === 0) {\n note(\n `Every module in ${source.label} is already installed.`,\n \"Nothing to add\"\n );\n outro(pc.dim(\"use `saasaloy add <module> --force` to re-apply one\"));\n return EXIT_OK;\n }\n const picked = await select({\n message: `Pick a module to add ${pc.dim(`(from ${source.label}, ${installed.size} already installed)`)}`,\n options: available.map((n) => ({ label: n, value: n })),\n });\n if (isCancel(picked)) {\n cancel(\"add cancelled\");\n return EXIT_FAILURE;\n }\n requested = picked;\n }\n\n let graph = await resolveGraph(source, requested);\n\n // `requires.saasaloy` — the descriptor's floor on the CLI reading it (#50). Checked\n // before the driver prompt and before a single file is planned, because a CLI too old\n // to understand the descriptor cannot be trusted to resolve the rest of it either.\n // The graph holds every transitive prerequisite, so a range declared three levels down\n // refuses here and the message names that module, not the one the user typed.\n const cliVersion = await readVersion();\n const cliCheckFails = (): boolean => {\n const mismatches = detectCliMismatches({ cliVersion, graph });\n if (mismatches.length === 0) {\n return false;\n }\n cancel(formatCliMismatches(mismatches, requested, cliVersion));\n return true;\n };\n if (cliCheckFails()) {\n return EXIT_REFUSED;\n }\n\n // `requiresOneOf` — a capability naming its mutually exclusive drivers. Settle it\n // before the conflict check below, so that check reads the graph a picked driver is\n // already part of (#98).\n let unmet = detectMissingRequirements({ config, graph });\n if (unmet.length > 0 && (!isInteractive() || opts.yes)) {\n // `--yes` means \"don't ask me\", not \"choose a driver for my project\", and a\n // pipeline has no terminal to answer in. Both refuse and name the options.\n cancel(formatMissingRequirements(unmet, requested));\n return EXIT_REFUSED;\n }\n // One prompt per unmet requirement, re-checked after each pick: a single driver can\n // satisfy two capabilities, and a picked module may declare a requirement of its own.\n // The round cap keeps a descriptor that requires its way in a circle from prompting\n // forever; whatever is still unmet when it runs out is refused below.\n for (\n let round = 0;\n unmet.length > 0 && round < MAX_REQUIREMENT_PROMPTS;\n round++\n ) {\n const requirement = unmet[0];\n if (!requirement) {\n break;\n }\n const picked = await select({\n message: `${pc.cyan(requirement.declaredBy)} needs one of these — pick one`,\n options: requirement.options.map((n) => ({ label: n, value: n })),\n });\n if (isCancel(picked)) {\n cancel(\"add cancelled\");\n return EXIT_FAILURE;\n }\n graph = mergeGraph(graph, await resolveGraph(source, picked));\n unmet = detectMissingRequirements({ config, graph });\n }\n if (unmet.length > 0) {\n cancel(formatMissingRequirements(unmet, requested));\n return EXIT_REFUSED;\n }\n\n // A driver the user just picked is a descriptor this run never saw above, so its own\n // `requires` is checked here too — still before anything is planned.\n if (cliCheckFails()) {\n return EXIT_REFUSED;\n }\n\n prereqs = graph.order.filter((n) => n !== requested);\n\n // Mutually exclusive modules are refused before anything is written, and `--force`\n // doesn't bypass it — force means \"re-apply this module\", not \"install it anyway\".\n const manifest = await loadManifest(root);\n const conflicts = detectConflicts({ graph, config, lock });\n if (conflicts.missingLockEntries.length > 0) {\n log.warn(\n `No lock entry for ${conflicts.missingLockEntries.map((m) => pc.cyan(m)).join(\", \")} — ` +\n `any conflict they declare can't be checked ${pc.dim(\"(re-add them to record it)\")}.`\n );\n }\n if (conflicts.conflicts.length > 0) {\n cancel(formatConflicts(conflicts.conflicts, requested));\n return EXIT_REFUSED;\n }\n\n const install = graph.order.filter(\n (n) => !config.installed.includes(n) || (opts.force && n === requested)\n );\n // Installed and not being (re-)applied — a forced module belongs to `install`, not here.\n const alreadyInstalled = graph.order.filter(\n (n) => config.installed.includes(n) && !install.includes(n)\n );\n\n if (install.length === 0) {\n note(\n `${pc.cyan(requested)} and its dependencies are already installed.`,\n \"Nothing to do\"\n );\n outro(pc.dim(\"use --force to re-apply\"));\n return EXIT_OK;\n }\n\n plan = await buildPlan({\n alreadyInstalled,\n config,\n install,\n manifest,\n modules: graph.modules,\n requested,\n root,\n });\n\n summarizePlan(plan, requested, prereqs);\n\n if (planWritesUi(plan)) {\n log.info(\n `This module writes ${pc.cyan(\"packages/ui/\")}. Run ${pc.cyan(\"/saasaloy-design update\")} after it applies.`\n );\n }\n\n if (opts.diff) {\n for (const file of plan.files) {\n if (file.action === \"unchanged\") {\n continue;\n }\n note(\n renderDiff(file),\n `${ACTION_LABEL[file.action]} ${fileLabel(file)}`\n );\n }\n for (const p of plan.patches) {\n if (p.action !== \"apply\") {\n continue;\n }\n note(p.diff, `${pc.green(\"patch\")} ${p.file}`);\n }\n }\n\n // --dry-run and --diff both preview only; nothing is written.\n if (opts.dryRun || opts.diff) {\n outro(\n pc.dim(\n opts.diff\n ? \"diff only — nothing applied\"\n : \"dry run — nothing applied\"\n )\n );\n return EXIT_OK;\n }\n\n if (!opts.yes) {\n const proceed = await confirm({ message: \"Proceed?\" });\n if (isCancel(proceed)) {\n cancel(\"add cancelled\");\n return EXIT_FAILURE;\n }\n if (!proceed) {\n outro(pc.dim(\"aborted — nothing applied\"));\n return EXIT_OK;\n }\n }\n\n // For the length of the apply a failure can leave files on disk, so the catch below\n // owes the user the re-run that completes them (#49). `onApplied` closes that window\n // the moment the apply is over: a save that fails afterwards is its own story, and\n // the re-run the recovery line advises would have nothing left to complete.\n partial = requested;\n const result = await applyAndPersist({\n config,\n graph,\n lock,\n manifest,\n onApplied: () => {\n partial = undefined;\n },\n plan,\n root,\n source,\n });\n\n // Merge npm deps into the project root package.json (best-effort — never blocks the\n // apply). This trails `executePlan` on purpose (#98): a mid-plan failure throws past\n // here, so a run that wrote no files leaves no package.json advertising packages\n // nothing installed. Rolling back what `executePlan` itself wrote is #49.\n const pkg = await readRootPackageJson(root);\n let depsAdded: string[] = [];\n const allDeps = [...plan.dependencies, ...plan.devDependencies];\n if (allDeps.length > 0) {\n if (pkg) {\n const {\n added,\n devAdded,\n conflicts: depConflicts,\n } = planDeps(pkg, plan.dependencies, plan.devDependencies);\n await writeDeps(root, pkg, added, devAdded);\n depsAdded = [...added, ...devAdded].map((d) => d.name);\n for (const conflict of depConflicts) {\n log.warn(`Dependency version conflict — ${conflict}.`);\n }\n } else {\n // Best-effort means \"don't block\", not \"fail silently\".\n log.warn(\n `No package.json at the project root — add ${allDeps.join(\", \")} yourself.`\n );\n }\n }\n\n for (const file of result.written) {\n log.step(`${ACTION_LABEL[file.action]} ${file.target}`);\n }\n for (const file of result.refreshed) {\n log.step(`${ACTION_LABEL[file.action]} ${file.target}`);\n }\n for (const link of result.links) {\n const label =\n link.action === \"create\" ? pc.green(\"link\") : pc.dim(\"link\");\n log.step(`${label} ${link.path} ${pc.dim(`→ ${link.target}`)}`);\n }\n for (const link of result.linkConflicts) {\n log.warn(\n `Skill link ${pc.cyan(link.path)} left untouched — a non-saasaloy path already occupies it.`\n );\n }\n for (const p of result.patched) {\n log.step(\n `${pc.green(\"patch\")} ${p.file} ${pc.dim(`— ${p.patch.kind}`)}`\n );\n }\n for (const p of result.patchConflicts) {\n log.warn(\n `Config patch target ${pc.cyan(p.file)} missing — ${p.patch.kind} skipped.`\n );\n }\n // A refusal is not an idempotent no-op: the patch would have written something wrong,\n // so nothing was written and nothing was tracked. Name the file and the reason.\n for (const r of result.patchRefusals) {\n log.warn(\n `Config patch on ${pc.cyan(r.patch.file)} skipped — ${r.reason}. Wire it by hand.`\n );\n }\n if (result.lateDrift.length > 0) {\n const lines = result.lateDrift\n .map((f) => ` ${pc.yellow(\"kept\")} ${f.target}`)\n .join(\"\\n\");\n note(\n wrapForNote(\n `${lines}\\n\\n${pc.dim(\"Changed while the plan was open — left alone rather than overwritten.\")}`\n ),\n \"Changed under us\"\n );\n }\n // Which of those kept files cost their module its install, stated plainly. The run\n // still exits 0: nothing failed, the user's own edit simply outranked the plan.\n const incomplete = plan.install.filter(\n (name) => !result.completed.includes(name)\n );\n if (incomplete.length > 0) {\n log.warn(formatIncomplete(incomplete, requested));\n }\n if (result.heldBack.length > 0) {\n const merges = result.heldBack\n .map((f) => ` ${ACTION_LABEL[f.action]} ${f.target}`)\n .join(\"\\n\");\n note(\n wrapForNote(\n `${merges}\\n\\n${pc.dim(\"These were left untouched. Hand them to an agent with `--diff` to merge.\")}`\n ),\n \"Needs merge\"\n );\n }\n if (depsAdded.length > 0) {\n note(\n wrapForNote(\n `${depsAdded.map((d) => pc.cyan(d)).join(\", \")}\\n\\n${pc.dim(\"Run `pnpm install` to fetch them.\")}`\n ),\n \"Dependencies added\"\n );\n }\n\n // The env vars printed at plan time, several boxes and a confirmation ago. Write\n // them where the project can keep them, and say where that is (#98).\n let devVarsPath: string | undefined;\n try {\n devVarsPath = await writeDevVarsExample({\n aliases: config.aliases,\n devVars: plan.devVars,\n envVars: plan.envVars,\n root,\n });\n } catch (error) {\n // Best-effort, like the dependency merge: an unwritable example file must not\n // undo an apply that already landed.\n log.warn(`Couldn't write ${DEV_VARS_EXAMPLE} — ${formatFailure(error)}.`);\n }\n\n printNextSteps(plan, result, devVarsPath, config.aliases);\n\n // The closing line names what was installed, not what was planned (#49). When\n // everything landed the two lists are the same and this reads as it always did.\n outro(\n result.completed.length > 0\n ? pc.green(\n `Applied ${result.completed.map((m) => pc.bold(m)).join(\", \")} ${pc.dim(`(${result.written.length} files)`)}`\n )\n : pc.yellow(\n `Nothing installed ${pc.dim(`(${result.written.length} files written)`)}`\n )\n );\n return EXIT_OK;\n } catch (error) {\n // A failure after the apply started leaves part of the module on disk. Nothing is\n // rolled back, so say what the state files now describe and how to finish the job\n // (#49) before the error itself closes the run.\n if (partial) {\n log.warn(formatRecovery(partial));\n }\n cancel(formatFailure(error));\n return exitCodeFor(error);\n } finally {\n // A remote source extracts each module to a temp dir; drop them once applied.\n await source?.cleanup?.();\n }\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, posix } from \"node:path\";\nimport { RefusalError } from \"./exit.js\";\nimport {\n detectCollisions,\n detectOwnedCollisions,\n formatCollisions,\n formatOwnedCollisions,\n mayShareTarget,\n} from \"./collisions.js\";\nimport type { OwnedCollision, StaleOwner } from \"./collisions.js\";\nimport {\n assertNoSymlinkPath,\n classifyLink,\n createDirLink,\n hashContent,\n joinModulePath,\n listFilesRelative,\n pathExists,\n readIfPresent,\n resolveWithinRoot,\n} from \"./fs-utils.js\";\nimport { samePatchEntry } from \"./manifest.js\";\nimport type { Manifest, ManifestPatch } from \"./manifest.js\";\nimport { applyPatch } from \"./patch/index.js\";\nimport type { PatchMatch } from \"./patch/index.js\";\nimport type { LoadedModule } from \"./registry.js\";\nimport { resolveTarget } from \"./saasaloy-config.js\";\nimport type { RegistryPatch, SaasaloyConfig } from \"./schema.js\";\n\n// The deterministic core of `saasaloy add`: turn the modules-to-install into a plan of\n// concrete file writes, classify each against the manifest's content hashes, then\n// execute the safe ones (build spec §2.9, §3.2). Convention-based file-drop is the\n// whole spine here; structural AST patches are the separate 10% (issue #7).\n\n// How a planned file relates to what's already on disk:\n// create — target doesn't exist yet\n// overwrite — tracked by us and untouched since (hash matches manifest) → safe update\n// unchanged — on-disk content already equals what we'd write\n// drift — tracked by us but hand-edited (hash ≠ manifest) → route to AI-merge, don't clobber\n// conflict — exists but we never wrote it (untracked) → don't clobber\nexport type FileAction =\n \"create\" | \"overwrite\" | \"unchanged\" | \"drift\" | \"conflict\";\n\n// Actions safe to apply without a human in the loop; drift/conflict are held back\n// for a merge. This is also the set whose content the patch preview reads, since each\n// of these leaves the planned bytes on disk.\nconst SAFE: ReadonlySet<FileAction> = new Set<FileAction>([\n \"create\",\n \"overwrite\",\n \"unchanged\",\n]);\n\n// Of the safe actions, the ones that need an actual write. `unchanged` means disk\n// already holds these exact bytes, so rewriting it would churn the file's mtime for\n// nothing — its manifest entry is still refreshed (#98, matching `executeUpdatePlan`).\nconst WRITABLE: ReadonlySet<FileAction> = new Set<FileAction>([\n \"create\",\n \"overwrite\",\n]);\n\nexport interface PlannedFile {\n module: string;\n /** Absolute path of the source file inside the module folder. */\n source: string;\n /** Module-relative POSIX source path (`files/lib/x.ts`) — recorded as the manifest's `from`. */\n from: string;\n /** Project-relative POSIX path (manifest key + display). */\n target: string;\n /** Absolute destination path. */\n targetAbs: string;\n content: string;\n newHash: string;\n action: FileAction;\n /** Present when the target already exists — used by `--diff`. */\n oldContent?: string;\n /** True for files copied from an `agent.skills` folder (vs. `files[]`). */\n isSkill: boolean;\n /** The `onlyWith` condition that chose this variant — the plan names it (#99). */\n onlyWith?: string;\n}\n\n// How a skill's `.claude/skills/<name>` symlink relates to what's already on disk:\n// create — nothing there, we'll make the link\n// exists — the correct link is already present (idempotent re-add)\n// conflict — a real dir/file or a link elsewhere sits there → don't clobber\nexport type LinkAction = \"create\" | \"exists\" | \"conflict\";\n\n// How a planned config patch relates to its target file:\n// apply — target resolvable and the patch changes it → write the result\n// unchanged — the patch is already present (idempotent no-op) → skip the write\n// missing — no target file (not scaffolded this run, not on disk) → can't patch\nexport type PatchAction = \"apply\" | \"unchanged\" | \"missing\";\n\n// A structural config patch bound to a concrete target file (ADR 0019). Unlike a\n// PlannedFile it is never manifest-tracked as a managed file — a patch mutates a\n// file another module owns, so it isn't a clean managed copy. `executePlan` records\n// each applied patch in `manifest.patches`, which is what `remove` reads back: it\n// reverses the four kinds that edit a config file (`chained-route`, `wrangler-binding`,\n// `plugin-array`, `const-array`) and warns about the two `package.json` kinds, which stay\n// the user's to undo (#36).\nexport interface PlannedPatch {\n module: string;\n /** Project-relative POSIX path of the file being patched. */\n file: string;\n /** Absolute path of the target file. */\n fileAbs: string;\n /** The op as authored (kind + payload + file) — re-applied at execute time. */\n patch: RegistryPatch;\n action: PatchAction;\n /** Unified diff of the would-be change; `\"\"` when unchanged or missing. */\n diff: string;\n /** The would-be content after the patch; undefined when the target is missing. */\n content?: string;\n /** Set when the patch's identity already exists at a different value — the user edited\n * what we would have written. `update` reports these into its merge plan; `add`\n * ignores it (issue #48, decision 1). */\n matched?: PatchMatch;\n}\n\n// A `.claude/skills/<name>` symlink pointing at the real, committed `.agents/skills/<name>`\n// folder, so Claude Code discovers the skill while every other agent reads the files directly.\nexport interface PlannedLink {\n module: string;\n /** Project-relative POSIX path of the symlink (under `.claude/skills`). */\n path: string;\n /** Absolute path of the symlink. */\n pathAbs: string;\n /** Project-relative POSIX path the link points at (under `.agents/skills`). */\n target: string;\n /** Absolute path of the link target. */\n targetAbs: string;\n action: LinkAction;\n}\n\nexport interface Plan {\n /** Modules being applied, in topological order. */\n install: string[];\n /** Requested modules already installed (skipped). */\n alreadyInstalled: string[];\n files: PlannedFile[];\n /** `.claude/skills/<name>` symlinks the installed skills register (created by executePlan). */\n links: PlannedLink[];\n /** Union of npm `dependencies[]` declared across the installed modules. */\n dependencies: string[];\n /** Union of npm `devDependencies[]` declared across the installed modules. */\n devDependencies: string[];\n /** Union of env vars declared (name → description) — reported, not written. */\n envVars: Record<string, string>;\n /** Union of the local-dev values declared for those vars — written into `.dev.vars.example`. */\n devVars: Record<string, string>;\n /** Aliases the installed scaffolds register into saasaloy.json (applied by executePlan). */\n aliases: Record<string, string>;\n /** Human-readable notes where a scaffold alias would redefine an existing one to a new path. */\n aliasConflicts: string[];\n /** Files a module reclaims whose manifest owner is a different installed module that is now stale (#107). */\n staleOwners: StaleOwner[];\n /** Structural config patches to apply (previewed against the would-be on-disk state). */\n patches: PlannedPatch[];\n /** Removal warnings keyed by the module that supplied them. */\n removeWarnings: Record<string, string[]>;\n}\n\n/** A file one revision of a module ships: where it comes from and where it lands. */\nexport interface ModuleFileRef {\n /** Module-relative POSIX source path (`files/lib/x.ts`) — the manifest's `from`. */\n from: string;\n /** Project-relative POSIX target path. */\n target: string;\n /** Absolute path of the source file inside the module folder. */\n abs: string;\n /** The `onlyWith` condition that selected this variant; absent on an unconditional file. */\n onlyWith?: string;\n}\n\n/** One `onlyWith` entry that could have filled a target, as the refusal names it. */\nexport interface FileCandidate {\n from: string;\n onlyWith: string;\n}\n\n/** A target every one of whose entries is conditional, none of them matching this project. */\nexport interface UnmatchedTarget {\n module: string;\n target: string;\n /** Every entry that declared this target, in descriptor order. */\n candidates: FileCandidate[];\n}\n\n/** What a module ships once its conditions are applied: the files, and the targets left empty. */\nexport interface ModuleFileSelection {\n files: Map<string, ModuleFileRef>;\n unmatched: UnmatchedTarget[];\n}\n\n/**\n * Every file a module ships, keyed by its project-relative target — `files[]`, each\n * scaffold's workspace files, and each `agent.skills` folder expanded — with each entry's\n * `onlyWith` condition applied against the resolved install set.\n *\n * The one place those four rules live. `buildPlan` walks it to plan an `add`, and\n * `buildUpdatePlan` walks it at two SHAs to diff a module against itself, so `update`\n * always sees exactly the set `add` would have written. Each engine carried its own copy\n * of these rules until #98, which is how the write-path guards drifted apart.\n *\n * `onlyWith` (#99) lets one module ship a dialect-bound file twice, once per database\n * driver, both entries naming the same `target`. Selection has to sit here rather than in\n * `buildPlan`: `auth`'s per-driver file lives in a scaffold rather than in `files[]`, and\n * an `update` that filtered differently would diff against a set `add` never wrote and\n * reinstate the variant this project does not hold.\n *\n * `installed` is this run's resolved graph unioned with what the project already records.\n * A target whose entries are *all* conditional and none matches is not an error here —\n * this stays a pure lister, and a project that predates a variant must not make `update`\n * throw. It comes back in `unmatched`, and `buildPlan` alone refuses on it.\n */\nexport async function selectModuleFiles(\n mod: LoadedModule,\n aliases: Record<string, string>,\n installed: ReadonlySet<string>\n): Promise<ModuleFileSelection> {\n // Gather every entry per target first, then resolve: a target is only unmatched once\n // all of its candidates are known, and the last eligible entry still wins, which is the\n // precedence `record` had before conditions existed.\n const byTarget = new Map<\n string,\n { chosen?: ModuleFileRef; candidates: FileCandidate[] }\n >();\n const record = (from: string, target: string, onlyWith?: string): void => {\n let slot = byTarget.get(target);\n if (!slot) {\n slot = { candidates: [] };\n byTarget.set(target, slot);\n }\n if (onlyWith !== undefined) {\n slot.candidates.push({ from, onlyWith });\n if (!installed.has(onlyWith)) {\n return;\n }\n }\n slot.chosen = {\n from,\n target,\n abs: joinModulePath(mod.dir, from),\n ...(onlyWith === undefined ? {} : { onlyWith }),\n };\n };\n\n for (const file of mod.item.files ?? []) {\n record(file.path, resolveTarget(aliases, file.target), file.onlyWith);\n }\n for (const scaffold of mod.item.scaffolds ?? []) {\n for (const file of scaffold.files) {\n record(\n file.path,\n posix.join(scaffold.workspace, file.target),\n file.onlyWith\n );\n }\n }\n // A skill folder is expanded from disk, not declared file by file, so there is no entry\n // to hang a condition on. Its files are unconditional by construction (#99).\n for (const skillRel of mod.item.agent?.skills ?? []) {\n const folderName = posix.basename(skillRel);\n for (const rel of await listFilesRelative(\n joinModulePath(mod.dir, skillRel)\n )) {\n record(\n posix.join(skillRel, rel),\n posix.join(\".agents/skills\", folderName, rel)\n );\n }\n }\n\n const files = new Map<string, ModuleFileRef>();\n const unmatched: UnmatchedTarget[] = [];\n for (const [target, slot] of byTarget) {\n if (slot.chosen) {\n files.set(target, slot.chosen);\n } else {\n unmatched.push({\n module: mod.item.name,\n target,\n candidates: slot.candidates,\n });\n }\n }\n return { files, unmatched };\n}\n\n/** {@link selectModuleFiles} without the unmatched report — what `update` needs. */\nexport async function listModuleFiles(\n mod: LoadedModule,\n aliases: Record<string, string>,\n installed: ReadonlySet<string>\n): Promise<Map<string, ModuleFileRef>> {\n return (await selectModuleFiles(mod, aliases, installed)).files;\n}\n\n/**\n * The refusal `add` prints when a target's every variant was filtered out. Same shape as\n * `formatConflicts`/`formatMissingRequirements`: a heading, then one line per target\n * naming each candidate and the module that would have selected it. `requiresOneOf`\n * should make this unreachable, so it reads as a descriptor bug rather than user error.\n */\nexport function formatUnmatchedTargets(unmatched: UnmatchedTarget[]): string {\n const heading = `Cannot apply — no file variant matches this project for ${unmatched.length} target${unmatched.length > 1 ? \"s\" : \"\"}:`;\n const lines = unmatched.map((u) => {\n const candidates = u.candidates\n .map((c) => `${c.from} (onlyWith ${c.onlyWith})`)\n .join(\", \");\n return ` ${u.target} — ${u.module} ships ${candidates}, and none of those modules is installed.`;\n });\n return [heading, ...lines].join(\"\\n\");\n}\n\n/** True for a target under the `.agents/skills/` namespace an `agent.skills` folder fills. */\nfunction isSkillTarget(target: string): boolean {\n return target.startsWith(\".agents/skills/\");\n}\n\n/**\n * A file this run plans to lay down, as the patch preview needs to see it: the content,\n * and whether that content actually reaches disk. A held-back file keeps whatever is\n * there, so its planned content is only a last resort.\n */\nexport interface PatchSourceFile {\n /** The content this run would write; `undefined` when the module dropped the file. */\n content?: string;\n /** True when this run leaves that content on disk. */\n landsOnDisk: boolean;\n}\n\nexport interface PreviewPatchesArgs {\n root: string;\n /** Module that authored these ops — recorded on each preview. */\n module: string;\n ops: RegistryPatch[];\n /** Files this run plans, keyed by project-relative target. */\n planned: ReadonlyMap<string, PatchSourceFile>;\n}\n\n/**\n * Preview a module's config patches against the state this run will leave behind.\n *\n * Shared by `buildPlan` and `buildUpdatePlan` (#98): both resolve a patch's source the\n * same three ways — a file this run writes, else what is on disk, else a held-back\n * file's planned content — and both need the preview to be pure, because `--dry-run`\n * and `--diff` render it without writing. Only genuinely-absent targets are `missing`.\n * Both engines re-apply against fresh disk state at execute time; this is the preview,\n * never the source of truth for the write.\n */\nexport async function previewPatches(\n args: PreviewPatchesArgs\n): Promise<PlannedPatch[]> {\n const { root, module, ops, planned } = args;\n const out: PlannedPatch[] = [];\n for (const op of ops) {\n const fileAbs = resolveWithinRoot(root, op.file);\n const plannedFile = planned.get(op.file);\n const source = plannedFile?.landsOnDisk\n ? plannedFile.content\n : ((await readIfPresent(fileAbs)) ?? plannedFile?.content);\n if (source === undefined) {\n out.push({\n module,\n file: op.file,\n fileAbs,\n patch: op,\n action: \"missing\",\n diff: \"\",\n });\n continue;\n }\n const { content, changed, diff, matched } = applyPatch(source, op, op.file);\n out.push({\n module,\n file: op.file,\n fileAbs,\n patch: op,\n action: changed ? \"apply\" : \"unchanged\",\n diff,\n content,\n ...(matched ? { matched } : {}),\n });\n }\n return out;\n}\n\nasync function classify(\n targetAbs: string,\n target: string,\n newHash: string,\n manifest: Manifest,\n /** The module planning this write, compared against the manifest's recorded owner. */\n module: string\n): Promise<{\n action: FileAction;\n oldContent?: string;\n /**\n * The installed module that owns this file, when it is not the one writing now. The\n * cross-module fact only; `buildPlan` decides whether the claim is legal, because the\n * answer needs the run's `dependsOn` map and a per-file fatal action would leave a plan\n * that must not execute (plan decision Q6).\n */\n ownedBy?: string;\n /**\n * The module the manifest still records as this target's owner, when the file itself is\n * gone and the owner is not the module writing now. Reported, never refused (#107).\n */\n staleOwner?: string;\n}> {\n if (!(await pathExists(targetAbs))) {\n // Nothing on disk to lose, so a stale `managed` entry pointing at another module is\n // not a collision — the file this run writes is the only copy there will be. The\n // entry is still a fact worth saying out loud: the recorded owner keeps its\n // `installed` slot and its applied patches while owning none of the bytes. #107\n // settles that as warn-and-instruct — name the owner, tell the user to run\n // `saasaloy remove <owner>`, and never edit the bookkeeping on their behalf.\n const stale = manifest.managed[target];\n return stale && stale.module !== module\n ? { action: \"create\", staleOwner: stale.module }\n : { action: \"create\" };\n }\n const oldContent = await readFile(targetAbs, \"utf-8\");\n const oldHash = hashContent(oldContent);\n const managed = manifest.managed[target];\n // Reported before the hash branches, because identical bytes are no defence: the\n // manifest entry is refreshed on `unchanged` too, so ownership would still flip (#91).\n const ownedBy =\n managed && managed.module !== module ? { ownedBy: managed.module } : {};\n if (oldHash === newHash) {\n return { action: \"unchanged\", oldContent, ...ownedBy };\n }\n if (managed) {\n return {\n action: managed.hash === oldHash ? \"overwrite\" : \"drift\",\n oldContent,\n ...ownedBy,\n };\n }\n return { action: \"conflict\", oldContent };\n}\n\nasync function planModuleFile(\n module: LoadedModule,\n ref: ModuleFileRef,\n root: string,\n manifest: Manifest\n): Promise<{ file: PlannedFile; ownedBy?: string; staleOwner?: string }> {\n const content = await readFile(ref.abs, \"utf-8\");\n const newHash = hashContent(content);\n // `add` is the one engine whose input is an untrusted remote descriptor, so the target\n // is resolved under the same guard `remover` and `updater` use on their state files\n // (#98). `join()` normalizes a `..` away silently; this refuses it instead.\n const targetAbs = resolveWithinRoot(root, ref.target);\n const { action, oldContent, ownedBy, staleOwner } = await classify(\n targetAbs,\n ref.target,\n newHash,\n manifest,\n module.item.name\n );\n return {\n file: {\n module: module.item.name,\n source: ref.abs,\n from: ref.from,\n target: ref.target,\n targetAbs,\n content,\n newHash,\n action,\n oldContent,\n isSkill: isSkillTarget(ref.target),\n ...(ref.onlyWith === undefined ? {} : { onlyWith: ref.onlyWith }),\n },\n ...(ownedBy === undefined ? {} : { ownedBy }),\n ...(staleOwner === undefined ? {} : { staleOwner }),\n };\n}\n\nexport interface BuildPlanArgs {\n root: string;\n install: string[];\n alreadyInstalled: string[];\n modules: Map<string, LoadedModule>;\n config: SaasaloyConfig;\n manifest: Manifest;\n /**\n * What the user asked for, named in a refusal so a collision raised by a transitive\n * prerequisite still says which command hit it. Omitted by callers that plan a set with\n * no single requested module (`update`'s prerequisite plan).\n */\n requested?: string;\n}\n\nexport async function buildPlan(args: BuildPlanArgs): Promise<Plan> {\n const {\n root,\n install,\n alreadyInstalled,\n modules,\n config,\n manifest,\n requested,\n } = args;\n // Keyed by project-relative target, because two modules in one run can ship the same\n // file: `database` and its driver `database-d1`/`database-postgres` both scaffold\n // `packages/db/tsconfig.json`. Planned as two entries they both classify `create` off\n // an empty disk, the core's copy lands first, and `stillMatches` then reads this run's\n // own bytes and drops the driver's copy as late drift — so the driver's tsconfig never\n // arrived and `packages/db` lost the `types` the driver needs (#98). `install` is\n // topological, so the last planner is the most specific one: let it win, and plan one\n // entry per target.\n const byTarget = new Map<string, PlannedFile>();\n // Every file this run would write that an already-installed *other* module owns, as\n // `classify` reports them. Collected across the whole loop and judged once below, so a\n // module contesting three of another's files raises one refusal naming all three (#91).\n const claims: OwnedCollision[] = [];\n // The same fact about a file that is no longer on disk. Nothing is at risk, so these\n // never refuse: they are collected here and reported by `add` so the user knows a\n // module is left installed owning nothing (#107).\n const staleClaims: OwnedCollision[] = [];\n const links: PlannedLink[] = [];\n const dependencies: string[] = [];\n const devDependencies: string[] = [];\n const envVars: Record<string, string> = {};\n const devVars: Record<string, string> = {};\n const removeWarnings: Record<string, string[]> = {};\n\n // Collect the aliases every scaffold in this run registers up front, so a feature's\n // files[] can resolve against a capability's brand-new alias even when both install in\n // the same run. Topo order already lands the capability first; this makes resolution\n // order-independent and keeps the target-resolving view (below) complete (ADR 0013).\n const aliases: Record<string, string> = {};\n for (const name of install) {\n for (const scaffold of modules.get(name)?.item.scaffolds ?? []) {\n for (const [alias, prefix] of Object.entries(scaffold.aliases ?? {})) {\n aliases[alias] = prefix;\n }\n }\n }\n const aliasConflicts: string[] = [];\n for (const [alias, prefix] of Object.entries(aliases)) {\n const existing = config.aliases[alias];\n if (existing !== undefined && existing !== prefix) {\n aliasConflicts.push(`${alias} → ${existing} redefined as ${prefix}`);\n }\n }\n // Scaffold aliases win over the on-disk map when resolving this run's file targets.\n const aliasView = { ...config.aliases, ...aliases };\n\n // What an `onlyWith` condition is tested against (#99): everything this run installs,\n // plus everything the project already holds. A feature added long after its driver has\n // only the second half; a feature that drags its driver in has only the first.\n const installedSet = new Set([\n ...install,\n ...alreadyInstalled,\n ...config.installed,\n ]);\n const unmatched: UnmatchedTarget[] = [];\n\n // Enumerate every module's files first, so the collision check sees the whole run\n // before a single file is classified. One enumeration for all three rules — `files[]`,\n // each scaffold's workspace files, and each `agent.skills` folder — shared with\n // `update` (#98), so `files[]` and `scaffolds[].files[]` are covered alike here.\n const refsByModule = new Map<string, ModuleFileRef[]>();\n for (const name of install) {\n const mod = modules.get(name);\n if (!mod) {\n continue;\n }\n const selection = await selectModuleFiles(mod, aliasView, installedSet);\n unmatched.push(...selection.unmatched);\n refsByModule.set(name, [...selection.files.values()]);\n }\n\n // Two modules writing the same target is legal only when one reaches the other through\n // `dependsOn` — core-plus-driver keeps last-planner-wins below, an unrelated pair is\n // refused here, before anything is planned (#91).\n const collisions = detectCollisions({\n planned: [...refsByModule].map(([module, refs]) => ({\n module,\n targets: refs.map((ref) => ref.target),\n })),\n modules,\n });\n if (collisions.length > 0) {\n throw new RefusalError(formatCollisions(collisions, requested));\n }\n\n for (const name of install) {\n const mod = modules.get(name);\n if (!mod) {\n continue;\n }\n const { item } = mod;\n\n // A scaffold file's target is workspace-relative and a skill file's lands under\n // `.agents/skills/`; from here every one of them is an ordinary managed file,\n // classified and recorded alike, so create/drift/conflict and `remove` all come\n // for free.\n for (const ref of refsByModule.get(name) ?? []) {\n const {\n file: planned,\n ownedBy,\n staleOwner,\n } = await planModuleFile(mod, ref, root, manifest);\n if (ownedBy !== undefined) {\n claims.push({\n target: planned.target,\n owner: ownedBy,\n claimant: name,\n });\n }\n if (staleOwner !== undefined) {\n staleClaims.push({\n target: planned.target,\n owner: staleOwner,\n claimant: name,\n });\n }\n // Re-key rather than overwrite in place, so the surviving entry is listed under\n // the module that actually writes it.\n byTarget.delete(planned.target);\n byTarget.set(planned.target, planned);\n }\n\n // The skill files themselves are real, committed files under `.agents/skills/<folder>/…`,\n // readable by every AI agent rather than only Claude Code. A `.claude/skills/<folder>`\n // symlink points back at them so Claude Code still discovers the skill (ADR 0015).\n for (const skillRel of item.agent?.skills ?? []) {\n const folderName = posix.basename(skillRel);\n const linkPath = posix.join(\".claude/skills\", folderName);\n const linkTarget = posix.join(\".agents/skills\", folderName);\n // Both sides are engine-built from a descriptor-supplied folder name, so both go\n // through the guard — the name is the untrusted part (#98).\n const pathAbs = resolveWithinRoot(root, linkPath);\n const targetAbs = resolveWithinRoot(root, linkTarget);\n const state = await classifyLink(pathAbs, targetAbs);\n links.push({\n module: name,\n path: linkPath,\n pathAbs,\n target: linkTarget,\n targetAbs,\n action:\n state === \"missing\"\n ? \"create\"\n : state === \"correct\"\n ? \"exists\"\n : \"conflict\",\n });\n }\n\n for (const dep of item.dependencies ?? []) {\n dependencies.push(dep);\n }\n for (const dep of item.devDependencies ?? []) {\n devDependencies.push(dep);\n }\n for (const [key, value] of Object.entries(item.devVars ?? {})) {\n devVars[key] = value;\n }\n for (const [key, value] of Object.entries(item.envVars ?? {})) {\n envVars[key] = value;\n }\n if (item.removeWarnings && item.removeWarnings.length > 0) {\n removeWarnings[name] = [...item.removeWarnings];\n }\n }\n\n // A module may take a file another one owns only when it reaches that owner through\n // `dependsOn`. Judged after every file is classified so the refusal names every\n // contested path at once, and before the plan is returned so `add` never executes it.\n // `--force` cannot reach this: it changes what goes into `install`, not this rule\n // (plan decision Q3).\n const owned = detectOwnedCollisions(claims, modules);\n if (owned.length > 0) {\n throw new RefusalError(formatOwnedCollisions(owned, requested));\n }\n\n // The missing-file half of the same rule, reported rather than refused (#107). Two\n // filters keep the note honest. `mayShareTarget` drops the driver reclaiming a file its\n // `dependsOn` capability applied, because telling the user to remove a module the new\n // one depends on would be wrong. The owner also has to still be installed: an owner\n // absent from `installed` is the mirror state `doctor` reports (#49), not this one.\n const priorInstalled = new Set([...alreadyInstalled, ...config.installed]);\n // Every target this plan writes changes hands, so an owner is left stale only when its\n // remaining `managed` entries are all in that set. That is the same arithmetic `doctor`\n // does, which is what keeps the two messages from contradicting each other: an owner\n // that keeps a file is not flagged there and is not told to `remove` here (#107).\n const changingHands = new Set(byTarget.keys());\n const keepsFiles = (owner: string): boolean =>\n Object.entries(manifest.managed).some(\n ([target, entry]) => entry.module === owner && !changingHands.has(target)\n );\n const staleOwners: StaleOwner[] = staleClaims\n .filter(\n (claim) =>\n priorInstalled.has(claim.owner) &&\n !mayShareTarget(claim.claimant, claim.owner, modules)\n )\n .map((claim) => ({ ...claim, ownerKeepsFiles: keepsFiles(claim.owner) }));\n\n // A target every one of whose variants was filtered out would otherwise be a file that\n // silently never arrived — an app missing its schema, discovered at typecheck. Refuse\n // the whole plan instead, before `executePlan` writes anything, and name what would\n // have satisfied it. A refusal, not a failure: `add` exits 2 (#98's code scheme).\n if (unmatched.length > 0) {\n throw new RefusalError(formatUnmatchedTargets(unmatched));\n }\n\n const files = [...byTarget.values()];\n\n // Plan patches after every file is collected, so an op targeting a file another module\n // scaffolds this same run previews against that file's *would-be* content, not disk.\n const planned = new Map<string, PatchSourceFile>(\n files.map((f) => [\n f.target,\n { content: f.content, landsOnDisk: SAFE.has(f.action) },\n ])\n );\n const patches: PlannedPatch[] = [];\n for (const name of install) {\n patches.push(\n ...(await previewPatches({\n root,\n module: name,\n ops: modules.get(name)?.item.patches ?? [],\n planned,\n }))\n );\n }\n\n return {\n install,\n alreadyInstalled,\n files,\n links,\n dependencies,\n devDependencies,\n devVars,\n envVars,\n aliases,\n aliasConflicts,\n staleOwners,\n patches,\n removeWarnings,\n };\n}\n\nexport interface ApplyResult {\n /** Modules of `plan.install` whose planned file writes all landed — the list\n * `config.installed` and the lock's freshly-installed entries derive from (#49). */\n completed: string[];\n written: PlannedFile[];\n /** Files already byte-identical to what we'd write — manifest refreshed, disk untouched. */\n refreshed: PlannedFile[];\n /** Planned-writable files edited between planning and the write — left alone. */\n lateDrift: PlannedFile[];\n /** drift + conflict files, held back for the merge path. */\n heldBack: PlannedFile[];\n /** `.claude/skills` symlinks created or already correct, recorded in the manifest. */\n links: PlannedLink[];\n /** Symlinks left untouched because something else already occupies their path. */\n linkConflicts: PlannedLink[];\n /** Config patches that actually changed their target file. */\n patched: PlannedPatch[];\n /** Patches whose target file was absent — reported, not applied. */\n patchConflicts: PlannedPatch[];\n /** Patches the codemod declined to apply, each with the reason it gave. Distinct from\n * an idempotent no-op, which changes nothing and has nothing to report. */\n patchRefusals: PatchRefusal[];\n}\n\n/** A patch left unapplied on purpose, and the codemod's own account of why. */\nexport interface PatchRefusal {\n patch: PlannedPatch;\n reason: string;\n}\n\n/**\n * Whether a planned file still looks the way it did when the plan was built. The\n * confirmation prompt sits between the two and can stay open indefinitely, so the\n * verdict is re-earned immediately before the write — the same reason `remover.ts`\n * re-checks before deleting and `updater.ts` before overwriting (#98). A planned\n * `create` matches only while its path is still empty.\n */\nasync function stillMatches(file: PlannedFile): Promise<boolean> {\n return (await readIfPresent(file.targetAbs)) === file.oldContent;\n}\n\n// Write the safe files, record each in the manifest with its content hash, and mark\n// the modules installed. Drift/conflict files are left on disk untouched and returned\n// so the caller can emit an AI-merge plan (the non-deterministic seam, build spec §2.9).\nexport async function executePlan(\n plan: Plan,\n root: string,\n config: SaasaloyConfig,\n manifest: Manifest\n): Promise<ApplyResult> {\n const written: PlannedFile[] = [];\n const refreshed: PlannedFile[] = [];\n const lateDrift: PlannedFile[] = [];\n const heldBack: PlannedFile[] = [];\n const links: PlannedLink[] = [];\n const linkConflicts: PlannedLink[] = [];\n const patched: PlannedPatch[] = [];\n const patchConflicts: PlannedPatch[] = [];\n const patchRefusals: PatchRefusal[] = [];\n\n for (const file of plan.files) {\n if (!SAFE.has(file.action)) {\n heldBack.push(file);\n continue;\n }\n if (!(await stillMatches(file))) {\n // Edited while the confirmation was up — the plan the user approved described\n // different bytes, so it doesn't authorize overwriting these.\n lateDrift.push(file);\n continue;\n }\n if (WRITABLE.has(file.action)) {\n // `resolveWithinRoot` proved the path is lexically inside the project; `writeFile`\n // still follows links, so a planted symlink on any component would carry the write\n // outside it. Refuse before creating the parent dirs (#98), matching `remover`.\n await assertNoSymlinkPath(root, file.targetAbs);\n await mkdir(dirname(file.targetAbs), { recursive: true });\n await writeFile(file.targetAbs, file.content, \"utf-8\");\n written.push(file);\n } else {\n // `unchanged`: disk already holds these exact bytes. Its manifest entry is still\n // refreshed — that is how a file installed before `from` existed acquires one.\n refreshed.push(file);\n }\n manifest.managed[file.target] = {\n module: file.module,\n hash: file.newHash,\n from: file.from,\n };\n }\n\n // Apply structural patches after the file writes, so an op targeting a freshly-scaffolded\n // file (e.g. `database`'s D1 binding into api's just-written wrangler.jsonc) reads the real\n // content. Re-read disk here rather than trusting the plan's preview — the file may have been\n // held back as drift/conflict. The engine is idempotent, so a re-apply is a clean no-op, and a\n // patched file is deliberately *not* re-tracked in the manifest (it's another module's copy).\n for (const p of plan.patches) {\n if (!(await pathExists(p.fileAbs))) {\n patchConflicts.push(p);\n continue;\n }\n // The read below follows links just as the write does, so guard before either (#98).\n await assertNoSymlinkPath(root, p.fileAbs);\n const source = await readFile(p.fileAbs, \"utf-8\");\n const { content, changed, reason } = applyPatch(source, p.patch, p.file);\n if (changed) {\n await writeFile(p.fileAbs, content, \"utf-8\");\n patched.push(p);\n // Record for `remove` (which reverses the four config kinds and warns for the two\n // `package.json` kinds) — deduped so a `--force` re-apply that lands the same op\n // again doesn't duplicate the entry.\n const entry: ManifestPatch = {\n module: p.module,\n file: p.file,\n patch: p.patch,\n };\n if (\n !manifest.patches.some((existing) => samePatchEntry(existing, entry))\n ) {\n manifest.patches.push(entry);\n }\n } else if (reason !== undefined) {\n // The codemod refused rather than no-op'd: nothing is written and nothing is\n // tracked, so the manifest keeps saying `remove` has no business in this file.\n patchRefusals.push({ patch: p, reason });\n }\n }\n\n // Point `.claude/skills/<name>` at the real `.agents/skills/<name>` folder written above so\n // Claude Code discovers the skill. The native link (junction on Windows, symlink elsewhere) is\n // regenerated per-machine and git-ignored; the manifest tracks source→link for a clean `remove`.\n for (const link of plan.links) {\n if (link.action === \"conflict\") {\n linkConflicts.push(link);\n continue;\n }\n if (link.action === \"create\") {\n // Only the `create` leg writes. An `exists` link is our own symlink at the final\n // component, which the guard would (correctly) refuse, so it is not asked (#98).\n await assertNoSymlinkPath(root, link.pathAbs);\n await createDirLink(link.pathAbs, link.targetAbs);\n }\n manifest.links[link.target] = link.path;\n links.push(link);\n }\n\n // Register the aliases the scaffolds declared so the first feature targeting them\n // resolves against a real path (ADR 0013). Merge is idempotent on re-apply; a conflicting\n // redefinition was surfaced at plan time (plan.aliasConflicts) — last write wins here.\n for (const [alias, prefix] of Object.entries(plan.aliases)) {\n config.aliases[alias] = prefix;\n }\n\n // A module counts as installed once its clean files have landed — completeness comes\n // from what this run did, never from what it intended (#49). The gate is authorization,\n // not bytes: a `lateDrift` file was edited after the user approved the plan, so that\n // approval doesn't describe the module now on disk and the module stays uninstalled,\n // which is what lets a plain re-run re-plan it and ask again. A plan-time `heldBack`\n // file was shown as held *before* the approval, so it never blocks, and patch failures\n // are reported on their own — file writes alone decide this. A module that plans no\n // file writes has nothing that could fail, so it completes.\n const drifted = new Set(lateDrift.map((file) => file.module));\n const completed = plan.install.filter((name) => !drifted.has(name));\n\n // Preserve insertion order and dedupe against what's already there.\n for (const name of completed) {\n if (!config.installed.includes(name)) {\n config.installed.push(name);\n }\n }\n // Warnings track the files that did land, so they follow `plan.install`: an incomplete\n // module still wrote some of its files, and `remove` must warn about those.\n for (const name of plan.install) {\n const warnings = plan.removeWarnings[name];\n if (warnings) {\n manifest.removeWarnings[name] = [...warnings];\n } else {\n delete manifest.removeWarnings[name];\n }\n }\n\n return {\n completed,\n written,\n refreshed,\n lateDrift,\n heldBack,\n links,\n linkConflicts,\n patched,\n patchConflicts,\n patchRefusals,\n };\n}\n","// The CLI's exit-code vocabulary, plus the error formatter every command's catch block\n// shares. Before #98 every failure exited 1, so a script could not tell \"saasaloy\n// refused because the driver conflicts\" from \"the network broke mid-fetch\". Three codes\n// now carry that difference:\n//\n// 0 — the command did what it was asked, or the user answered \"no\" to a confirm.\n// 1 — something failed or the user cancelled: a fetch died, a write threw, Ctrl-C.\n// 2 — saasaloy refused by design: bad usage, a module conflict, an unmet requirement,\n// an invalid manifest, or a prompt with no terminal to answer it in.\n// 3 — `update` applied what it could and left files for a human or an agent to merge.\n// Everything it wrote is on disk; the run is unfinished, not failed.\n//\n// A wrapper script reads 2 as \"the input is wrong, do not retry\" and 1 as \"transient,\n// a retry may work\". Nothing else may take on a fourth meaning; add a code here first.\n\nexport const EXIT_OK = 0;\nexport const EXIT_FAILURE = 1;\nexport const EXIT_REFUSED = 2;\nexport const EXIT_MERGE_PENDING = 3;\n\n/**\n * A failure saasaloy chose, thrown from a library that has no exit code to return.\n * `loadConfig` rejecting an invalid `saasaloy.json` is the shape: the command above it\n * catches, sees this type, and exits 2 instead of 1. A plain `Error` stays a 1.\n */\nexport class RefusalError extends Error {\n override readonly name = \"RefusalError\";\n}\n\n/** True when this error is a refusal saasaloy made, rather than a failure it hit. */\nexport function isRefusal(error: unknown): error is RefusalError {\n return error instanceof RefusalError;\n}\n\n/** `EXIT_REFUSED` for a refusal, `EXIT_FAILURE` for anything else. */\nexport function exitCodeFor(error: unknown): number {\n return isRefusal(error) ? EXIT_REFUSED : EXIT_FAILURE;\n}\n\n/**\n * Set this to any non-empty value to print the whole `cause` chain under the message.\n * `registry.ts` wraps a fetch failure with `{ cause: error }` and the top level printed\n * only `error.message`, so the underlying `ECONNREFUSED` never reached a bug report.\n */\nexport const DEBUG_ENV = \"SAASALOY_DEBUG\";\n\nfunction isDebug(): boolean {\n return (process.env[DEBUG_ENV] ?? \"\") !== \"\";\n}\n\n// Guard against a cause cycle (`a.cause = b; b.cause = a`), which a hand-built error\n// can produce and which would otherwise loop forever.\nconst MAX_CAUSE_DEPTH = 10;\n\n/** Every `cause` under `error`, outermost first. Empty when nothing wrapped anything. */\nexport function causeChain(error: unknown): unknown[] {\n const chain: unknown[] = [];\n const seen = new Set<unknown>();\n let current: unknown = error;\n while (chain.length < MAX_CAUSE_DEPTH) {\n if (!(current instanceof Error) || current.cause === undefined) {\n break;\n }\n if (seen.has(current)) {\n break;\n }\n seen.add(current);\n current = current.cause;\n chain.push(current);\n }\n return chain;\n}\n\nfunction describe(value: unknown): string {\n if (value instanceof Error) {\n return value.stack ?? `${value.name}: ${value.message}`;\n }\n return String(value);\n}\n\n/**\n * The one-line message a command shows, plus the cause chain and stack when\n * `SAASALOY_DEBUG` is set. Every `catch` in the commands formats through this, so the\n * debug switch reaches all of them rather than the one that remembered it.\n */\nexport function formatFailure(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n if (!isDebug()) {\n return message;\n }\n const lines = [describe(error)];\n for (const [index, cause] of causeChain(error).entries()) {\n lines.push(`${\" \".repeat(index + 1)}caused by: ${describe(cause)}`);\n }\n return lines.join(\"\\n\");\n}\n","import type { LoadedModule } from \"./registry.js\";\n\n// Cross-module file collisions for `saasaloy add` — the deterministic core, kept beside\n// conflicts.ts and out of applier.ts's planning loop so it can be unit-tested.\n//\n// Two modules in one run may legitimately write the same target: `database` scaffolds\n// `packages/db/tsconfig.json` and its driver `database-d1` scaffolds a more specific one\n// over it, which is why buildPlan plans one entry per target and lets the last planner win\n// (#98). Two unrelated modules picking the same target is the opposite case: nothing says\n// which copy the user wanted, and the silent overwrite is a data-loss bug (#91).\n//\n// The rule that separates them is `dependsOn`. An overlap is legal when one of the two\n// modules reaches the other through `dependsOn`, because that edge is the author saying\n// \"I am built on top of that module and I know what it ships\". Everything else is refused.\n// No new descriptor field: `dependsOn` is the declaration (plan decision Q4).\n//\n// A collision is reported, never resolved. `buildPlan` raises one RefusalError naming\n// every contested path, before it plans a single write.\n\nexport interface FileCollision {\n /** Project-relative POSIX path both modules plan to write. */\n target: string;\n /** The module that claimed the target first — earlier in the run's install order. */\n module: string;\n /** The later module claiming the same target. */\n other: string;\n}\n\n/**\n * One file already on disk that a module in this run wants to write, and the installed\n * module `.saasaloy/manifest.json` says owns it. `classify` reports the pair; the rule\n * below decides whether the claimant may take it (#91 phase 2).\n */\nexport interface OwnedCollision {\n /** Project-relative POSIX path of the contested file. */\n target: string;\n /** The installed module that applied the file, per `manifest.managed[target].module`. */\n owner: string;\n /** The module this run would have overwrite it. */\n claimant: string;\n}\n\n/** One module's planned targets, as `listModuleFiles` enumerates them. */\nexport interface ModuleTargets {\n module: string;\n /** Project-relative POSIX targets — `files[]`, `scaffolds[].files[]` and skills alike. */\n targets: readonly string[];\n}\n\nexport interface DetectCollisionsArgs {\n /** Every module in this run with its planned targets, in install (topological) order. */\n planned: readonly ModuleTargets[];\n /**\n * Every descriptor this run resolved, keyed by name — the map `buildPlan` already\n * receives. Reachability is read off `dependsOn` here rather than off a `Graph`,\n * because `BuildPlanArgs` carries no graph.\n */\n modules: ReadonlyMap<string, LoadedModule>;\n}\n\n/** True when `from` reaches `to` by following `dependsOn`, at any depth. */\nfunction reaches(\n from: string,\n to: string,\n modules: ReadonlyMap<string, LoadedModule>\n): boolean {\n const seen = new Set<string>([from]);\n const stack: string[] = [from];\n while (stack.length > 0) {\n const name = stack.pop() ?? \"\";\n for (const dep of modules.get(name)?.item.dependsOn ?? []) {\n if (dep === to) {\n return true;\n }\n // A name with no descriptor in this map ends the walk there rather than throwing:\n // resolveGraph visits every `dependsOn` recursively, so a gap means a caller built\n // a partial map, and a missing edge can only make the check stricter.\n if (!seen.has(dep)) {\n seen.add(dep);\n stack.push(dep);\n }\n }\n }\n return false;\n}\n\n/**\n * True when these two modules may share a target: one of them declares the other in\n * `dependsOn`, directly or transitively. The relation is checked both ways — the driver\n * usually depends on the capability, but either side may be the one that declares it.\n */\nexport function mayShareTarget(\n a: string,\n b: string,\n modules: ReadonlyMap<string, LoadedModule>\n): boolean {\n return a === b || reaches(a, b, modules) || reaches(b, a, modules);\n}\n\n/**\n * Every illegal same-run overlap, in install order and then target order, so the refusal\n * reads the same on every run. Each claimant of a target reports one pair per earlier\n * claimant, so three unrelated modules on one target report three pairs, because each\n * pair is its own decision to fix.\n */\nexport function detectCollisions(args: DetectCollisionsArgs): FileCollision[] {\n const { planned, modules } = args;\n const claimants = new Map<string, string[]>();\n const collisions: FileCollision[] = [];\n\n for (const { module, targets } of planned) {\n for (const target of targets) {\n const prior = claimants.get(target);\n if (!prior) {\n claimants.set(target, [module]);\n continue;\n }\n for (const other of prior) {\n if (!mayShareTarget(module, other, modules)) {\n collisions.push({ target, module: other, other: module });\n }\n }\n prior.push(module);\n }\n }\n\n return collisions;\n}\n\n// One collision as a sentence: both module names, the contested path, and the two ways\n// out. `conflictsWith` is named because a deliberate either-or pair is the case this\n// refusal must not stand in the way of.\nfunction describe(collision: FileCollision): string {\n const { target, module, other } = collision;\n return (\n `${module} and ${other} both write ${target}, and neither declares the other in \\`dependsOn\\`. ` +\n `Give one of them a different target, or declare \\`conflictsWith\\` between them if they are deliberately exclusive.`\n );\n}\n\n/** The refusal `add` prints. One line per contested path, under a heading. */\nexport function formatCollisions(\n collisions: FileCollision[],\n /** What the user asked for; a caller planning a set with no single request omits it. */\n requested = \"these modules\"\n): string {\n const heading = `Cannot add ${requested} — file collision${collisions.length > 1 ? \"s\" : \"\"}:`;\n return [heading, ...collisions.map((c) => ` ${describe(c)}`)].join(\"\\n\");\n}\n\n/**\n * Every claim on an installed file that crosses module ownership illegally, in the order\n * `buildPlan` classified them. The rule is `mayShareTarget`, the same one the same-run\n * check uses: a module may take a file owned by a module it reaches through `dependsOn`,\n * and nothing else (plan decision Q4).\n *\n * The relation stays symmetric here, as it is in the same-run case. `add database --force`\n * on a project where `database-d1` took over `packages/db/tsconfig.json` is the capability\n * rewriting its own driver's file, and refusing it would tell the user to remove a driver\n * that depends on the very module being installed.\n *\n * An owner with no descriptor in `modules` reads as unrelated, which refuses the claim.\n * That is the safe answer: the run resolved every module it could reach, so an owner it\n * never saw is one nothing in this run depends on.\n */\nexport function detectOwnedCollisions(\n claims: readonly OwnedCollision[],\n modules: ReadonlyMap<string, LoadedModule>\n): OwnedCollision[] {\n return claims.filter(\n (claim) => !mayShareTarget(claim.claimant, claim.owner, modules)\n );\n}\n\n// One owned-file claim as a sentence. `--force` is named because it is the flag a user\n// reaches for next, and it deliberately does not cross ownership — `remove` does, because\n// it is the one path that leaves the manifest consistent (plan decision Q3).\nfunction describeOwned(collision: OwnedCollision): string {\n const { target, owner, claimant } = collision;\n return (\n `${owner} owns ${target}, and ${claimant} does not declare it in \\`dependsOn\\`. ` +\n `\\`--force\\` does not cross module file ownership: run \\`saasaloy remove ${owner}\\` first.`\n );\n}\n\n/**\n * A stale-ownership note: the manifest owner of a file that is gone, plus whether that\n * owner still owns manifest entries this run does not take over. `remove` deletes every\n * file a module owns, so the advice to run it is only safe when the owner is left owning\n * nothing (#107).\n */\nexport interface StaleOwner extends OwnedCollision {\n /** True when the owner keeps at least one managed entry after this run writes. */\n ownerKeepsFiles: boolean;\n}\n\n/**\n * One stale-ownership note as a sentence. The file is gone, so nothing is refused: the\n * claim is legal and the run continues. What is left behind is an installed module that\n * no longer owns the file it applied, and only `remove` clears that (#107).\n *\n * An owner that keeps other files is not stale once the run ends — `doctor` would not\n * flag it — so that sentence states the transfer and stops. Naming `remove` there would\n * tell the user to delete files they still use.\n */\nexport function describeStaleOwner(collision: StaleOwner): string {\n const { target, owner, claimant, ownerKeepsFiles } = collision;\n const head = `${owner} still owns ${target} in the manifest, but the file is gone — ${claimant} now writes it.`;\n return ownerKeepsFiles\n ? `${head} ${owner} keeps its other files, so leave it installed.`\n : `${head} Run \\`saasaloy remove ${owner}\\` to drop the stale module.`;\n}\n\n/** The refusal `add` prints for files another installed module owns. */\nexport function formatOwnedCollisions(\n collisions: OwnedCollision[],\n /** What the user asked for; a caller planning a set with no single request omits it. */\n requested = \"these modules\"\n): string {\n const noun = collisions.length > 1 ? \"files\" : \"file\";\n const heading = `Cannot add ${requested} — ${noun} owned by another module:`;\n return [heading, ...collisions.map((c) => ` ${describeOwned(c)}`)].join(\n \"\\n\"\n );\n}\n","import { createHash } from \"node:crypto\";\nimport {\n access,\n lstat,\n mkdir,\n readdir,\n readFile,\n readlink,\n symlink,\n} from \"node:fs/promises\";\nimport {\n dirname,\n isAbsolute,\n join,\n posix,\n relative,\n resolve,\n sep,\n} from \"node:path\";\nimport { RefusalError } from \"./exit.js\";\n\n/** sha256 hex digest of a string — used to fingerprint managed files in the manifest. */\nexport function hashContent(content: string): string {\n return createHash(\"sha256\").update(content, \"utf-8\").digest(\"hex\");\n}\n\n/**\n * Resolve a project-relative POSIX path (a manifest key, a manifest link value, a\n * descriptor target) to an absolute path that is guaranteed to sit inside `root`.\n *\n * The state files this resolves from are persisted JSON, so their keys are only as\n * trustworthy as the last process that wrote them. `join()` silently normalizes a\n * `..` segment, which is how a corrupt or hand-edited manifest turns into a delete\n * outside the project. Reject the shape up front rather than trusting the result.\n *\n * @param hint appended to the refusal, naming where the bad path came from.\n * @throws {Error} if the path is absolute, contains a `..`/`.`/empty segment, carries a\n * platform-specific separator, or otherwise escapes `root`.\n */\nexport function resolveWithinRoot(\n root: string,\n relPosixPath: string,\n hint = \"This usually means the state file that recorded it is corrupt or was hand-edited.\"\n): string {\n const reject = (why: string): never => {\n throw new RefusalError(\n `Refusing to resolve ${JSON.stringify(relPosixPath)}: ${why}. ${hint}`\n );\n };\n\n // A backslash also covers `C:\\…`, so drive-letter paths never reach the resolve below.\n if (relPosixPath.includes(\"\\\\\")) {\n reject(\"paths must use '/' separators\");\n }\n if (isAbsolute(relPosixPath) || /^[a-zA-Z]:/.test(relPosixPath)) {\n reject(\"path must be project-relative\");\n }\n\n const segments = relPosixPath.split(\"/\");\n if (segments.some((s) => s === \"\" || s === \".\" || s === \"..\")) {\n reject(\"path must not contain empty, '.' or '..' segments\");\n }\n\n const rootAbs = resolve(root);\n const abs = resolve(rootAbs, ...segments);\n // Belt and braces: catches anything the segment checks above missed (symlinked\n // roots, exotic normalization) before a caller deletes what comes back.\n if (abs !== rootAbs && !abs.startsWith(rootAbs + sep)) {\n reject(\"path escapes the project root\");\n }\n\n return abs;\n}\n\n/**\n * Reject a path that reaches its target through a symlink, at any component below `root`.\n *\n * `resolveWithinRoot` proves only that the *lexical* path stays inside the project. Every\n * `readFile`/`writeFile` that follows resolves links, so a symlink planted anywhere along\n * the path turns an in-root state-file entry into a read or write outside the project.\n * Walk the components from `root` down and refuse the first link found.\n *\n * A component that doesn't exist yet ends the walk: there is no link to follow, and\n * whether the caller may create it is its own question.\n *\n * @throws {Error} if any component of `abs` below `root` is a symlink.\n */\nexport async function assertNoSymlinkPath(\n root: string,\n abs: string\n): Promise<void> {\n const rootAbs = resolve(root);\n const rel = relative(rootAbs, resolve(abs));\n if (rel === \"\") {\n return;\n }\n\n let current = rootAbs;\n for (const segment of rel.split(sep)) {\n current = join(current, segment);\n let stat: Awaited<ReturnType<typeof lstat>>;\n try {\n stat = await lstat(current);\n } catch {\n return; // not on disk yet — nothing here can be followed\n }\n if (stat.isSymbolicLink()) {\n throw new RefusalError(\n `Refusing to touch ${JSON.stringify(rel.split(sep).join(\"/\"))}: ${JSON.stringify(segment)} is a symlink, and following it would leave the project root. This usually means the state file that recorded the path is corrupt or was hand-edited.`\n );\n }\n }\n}\n\n/**\n * Recursively list the files under `dir` as POSIX paths relative to it. Used to expand\n * an `agent.skills` folder into the individual files a module ships, by both the\n * applier (at add time) and the updater (comparing two SHAs of the same folder).\n */\nexport async function listFilesRelative(\n dir: string,\n prefix = \"\"\n): Promise<string[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const out: string[] = [];\n for (const entry of entries) {\n const rel = prefix ? posix.join(prefix, entry.name) : entry.name;\n if (entry.isDirectory()) {\n out.push(...(await listFilesRelative(join(dir, entry.name), rel)));\n } else if (entry.isFile()) {\n out.push(rel);\n }\n }\n return out;\n}\n\n/**\n * Names of the immediate subdirectories of `dir` (skips files); `[]` if `dir` is missing\n * or isn't a directory at all.\n *\n * Anything else — a permission error, an I/O failure — is rethrown rather than folded\n * into that empty array. Both callers loop over the result, so a swallowed error reads\n * as \"there was nothing here\", and the caller reports neither the work nor the reason\n * it didn't happen.\n *\n * @throws {Error} if `dir` exists but cannot be read.\n */\nexport async function readDirNames(dir: string): Promise<string[]> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.filter((e) => e.isDirectory()).map((e) => e.name);\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\" || code === \"ENOTDIR\") {\n return [];\n }\n throw error;\n }\n}\n\n/**\n * Resolve a descriptor-authored POSIX source path under a module folder.\n *\n * A module folder is a temp dir or a local checkout, so it is the root here rather than\n * the project. The path itself comes from an untrusted `registry-item.json`: Phase 1\n * guarded every write *target* and left the read side on a bare `join`, which normalizes\n * a `..` away silently, so `\"path\": \"../../../etc/passwd\"` with an in-root target copied\n * a host file into the project. Same guard, module folder as the root (#98).\n *\n * @throws {RefusalError} if the source path escapes the module folder.\n */\nexport function joinModulePath(dir: string, relPosix: string): string {\n return resolveWithinRoot(\n dir,\n relPosix,\n \"A module's source paths must stay inside the module folder; this descriptor is malformed or hostile.\"\n );\n}\n\n/** The file's content, or `undefined` when nothing is at that path. */\nexport async function readIfPresent(abs: string): Promise<string | undefined> {\n return (await pathExists(abs)) ? readFile(abs, \"utf-8\") : undefined;\n}\n\n/** True if the path exists (file, dir, or symlink). */\nexport async function pathExists(target: string): Promise<boolean> {\n try {\n await access(target);\n return true;\n } catch {\n return false;\n }\n}\n\n// How a would-be directory symlink relates to what's already at its path:\n// missing — nothing there, safe to create\n// correct — already a symlink/junction resolving to the intended target (idempotent no-op)\n// conflict — a real file/dir, or a symlink pointing elsewhere (don't clobber)\nexport type LinkState = \"missing\" | \"correct\" | \"conflict\";\n\n/** Classify a would-be directory symlink at `linkAbs` that should point to `targetAbs`. */\nexport async function classifyLink(\n linkAbs: string,\n targetAbs: string\n): Promise<LinkState> {\n let stat: Awaited<ReturnType<typeof lstat>>;\n try {\n stat = await lstat(linkAbs);\n } catch {\n return \"missing\";\n }\n if (!stat.isSymbolicLink()) {\n return \"conflict\";\n }\n // POSIX links read back relative to the link's own dir; Windows junctions read back absolute.\n const dest = await readlink(linkAbs);\n const resolved = resolve(dirname(linkAbs), dest);\n return resolved === resolve(targetAbs) ? \"correct\" : \"conflict\";\n}\n\n/**\n * Create a directory symlink that works cross-platform: a junction on Windows (needs no admin\n * rights and takes an absolute target) and a relative `dir` symlink elsewhere (so the project\n * stays portable when moved). Creates the parent directory first.\n */\nexport async function createDirLink(\n linkAbs: string,\n targetAbs: string\n): Promise<void> {\n await mkdir(dirname(linkAbs), { recursive: true });\n await (process.platform === \"win32\"\n ? symlink(resolve(targetAbs), linkAbs, \"junction\")\n : symlink(relative(dirname(linkAbs), targetAbs), linkAbs, \"dir\"));\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { RefusalError } from \"./exit.js\";\nimport { pathExists } from \"./fs-utils.js\";\nimport { validateManifest } from \"./schema.js\";\nimport type { RegistryPatch } from \"./schema.js\";\n\n// `.saasaloy/manifest.json` records every file a module applied — copied source\n// files and copied skill files — by owning module and content hash. On update the\n// tool re-hashes a managed file: match → safe overwrite; drift → route to AI-merge.\n// This replaces in-file `// saasaloy:managed` markers (see build spec §2.9 / §3.2).\n// Shape is validated by schemas/manifest.schema.json (see lib/schema.ts).\n\nexport interface ManagedEntry {\n /** Name of the module that applied this file. */\n module: string;\n hash: string;\n /**\n * The hash is of bytes found on disk, not bytes the tool wrote (`adoptBase`). Those\n * bytes may hold hand edits the tool knows nothing about, so an adopted entry never\n * earns the `overwrite` verdict: `classifyUpdate` sends it to the merge plan instead.\n * Dropped the first time the tool writes the file itself, which is when the recorded\n * hash finally describes template output.\n */\n adopted?: boolean;\n /**\n * Module-relative source path this file was copied from (e.g. `files/lib/email.ts`).\n * `update` needs it to fetch the same file at two commit SHAs, which the hash alone\n * can't express. Optional: entries written before this shipped don't carry it, and\n * `update` falls back to re-deriving the target from the descriptor (issue #48).\n */\n from?: string;\n}\n\n// A structural config patch that actually landed on disk, recorded so `remove` can undo\n// it — or, for a kind with no inverse, warn the user which file it can't clean up (see\n// the `PlannedPatch` comment in applier.ts). `patch` is the op as authored, which is all\n// `reversePatch` reads: the four config kinds are reversible from this record alone, and\n// the record carries no pre-patch content or hash, so drift is detected structurally\n// against the recorded payload rather than by comparison (#36).\nexport interface ManifestPatch {\n /** Name of the module that applied this patch. */\n module: string;\n /** Project-relative POSIX path of the file that was patched. */\n file: string;\n /** The op as authored (kind + payload + file). */\n patch: RegistryPatch;\n}\n\nexport interface Manifest {\n managed: Record<string, ManagedEntry>;\n links: Record<string, string>;\n patches: ManifestPatch[];\n /** Module name to warnings shown before removal. */\n removeWarnings: Record<string, string[]>;\n}\n\nexport const MANIFEST_FILE = join(\".saasaloy\", \"manifest.json\");\n\n/**\n * Structural equality on the (module, file, patch) triple — good enough since both sides\n * are parsed from the same registry-item.json descriptor, so key order is stable across a\n * `--force` re-apply. `add` dedupes new entries with it; `remove` untracks with it, and\n * needs the structural form rather than reference identity because the plan it walks and\n * the manifest it edits can be separate loads of the same file.\n */\nexport function samePatchEntry(a: ManifestPatch, b: ManifestPatch): boolean {\n return (\n a.module === b.module &&\n a.file === b.file &&\n JSON.stringify(a.patch) === JSON.stringify(b.patch)\n );\n}\n\nexport function emptyManifest(): Manifest {\n return { links: {}, managed: {}, patches: [], removeWarnings: {} };\n}\n\nexport async function loadManifest(root: string): Promise<Manifest> {\n const file = join(root, MANIFEST_FILE);\n if (!(await pathExists(file))) {\n return emptyManifest();\n }\n // A half-written file parses as nothing at all, which is as invalid as a bad hash and\n // takes the same exit code. Left to throw, `SyntaxError` reaches `exitCodeFor` as a\n // plain failure (1) and never names the file it came from.\n let raw: unknown;\n try {\n raw = JSON.parse(await readFile(file, \"utf-8\"));\n } catch (error) {\n throw new RefusalError(\n `${MANIFEST_FILE} is invalid:\\n ${error instanceof Error ? error.message : String(error)}`,\n { cause: error }\n );\n }\n // This file decides whether a target is safe to overwrite, so a hash that isn't a\n // digest or a half-written entry has to stop the command rather than reach the applier\n // as a typed object that lies about its shape (#98). Same posture as `loadConfig`.\n const result = await validateManifest(raw);\n if (!result.valid) {\n throw new RefusalError(\n `${MANIFEST_FILE} is invalid:\\n ${result.errors.join(\"\\n \")}`\n );\n }\n const parsed = raw as Partial<Manifest>;\n return {\n links: parsed.links ?? {},\n managed: parsed.managed ?? {},\n patches: parsed.patches ?? [],\n removeWarnings: parsed.removeWarnings ?? {},\n };\n}\n\nexport async function saveManifest(\n root: string,\n manifest: Manifest\n): Promise<void> {\n const file = join(root, MANIFEST_FILE);\n await mkdir(dirname(file), { recursive: true });\n await writeFile(file, `${JSON.stringify(manifest, null, 2)}\\n`, \"utf-8\");\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport AjvDefault from \"ajv/dist/2020.js\";\nimport type { ErrorObject, ValidateFunction } from \"ajv\";\nimport { pathExists } from \"./fs-utils.js\";\nimport type { Patch } from \"./patch/index.js\";\n\n// ajv is CJS (`module.exports = Ajv2020`); under NodeNext the default import is typed\n// as the module namespace, so re-point it to the class it actually is at runtime.\nconst Ajv2020 = AjvDefault as unknown as typeof AjvDefault.default;\n\n// $schema-validated forcing functions for the three Saasaloy descriptors: the\n// consumer manifest (saasaloy.json), the managed-file manifest (.saasaloy/manifest.json),\n// and the module descriptor (registry-item.json). Authored descriptors validate\n// against the JSON Schema documents in ../schemas so a typo fails fast with a clear\n// error rather than surfacing as a mysterious applier crash later (build spec §3.2/§3.3).\n\nexport type SchemaName =\n \"saasaloy\" | \"manifest\" | \"registry-item\" | \"saasaloy-lock\";\n\nconst SCHEMA_FILES: Record<SchemaName, string> = {\n manifest: \"manifest.schema.json\",\n \"registry-item\": \"registry-item.schema.json\",\n saasaloy: \"saasaloy.schema.json\",\n \"saasaloy-lock\": \"saasaloy-lock.schema.json\",\n};\n\n// Schemas ship beside dist/ (see package.json \"files\"). At runtime import.meta.url is\n// <pkg>/dist/index.js so ../schemas resolves to <pkg>/schemas; under vitest it's\n// <pkg>/src/lib/schema.ts, so the schemas sit one level further up. Try both and cache.\nconst SCHEMA_DIR_CANDIDATES = [\"../schemas\", \"../../schemas\"];\nlet schemaDirPromise: Promise<string> | undefined;\n\nasync function schemaDir(): Promise<string> {\n schemaDirPromise ??= (async () => {\n for (const candidate of SCHEMA_DIR_CANDIDATES) {\n const dir = fileURLToPath(new URL(candidate, import.meta.url));\n if (await pathExists(join(dir, \"registry-item.schema.json\"))) {\n return dir;\n }\n }\n // Fall back to the packaged location for a sensible ENOENT message.\n return fileURLToPath(new URL(\"../schemas\", import.meta.url));\n })();\n return schemaDirPromise;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n /** Human-readable, one-per-line messages; empty when valid. */\n errors: string[];\n}\n\n// One Ajv instance, validators compiled lazily and cached — schemas are read off\n// disk on first use so the CLI pays nothing for schemas it never touches.\nconst ajv = new Ajv2020({ allErrors: true });\nconst validators = new Map<SchemaName, ValidateFunction>();\n\nasync function getValidator(name: SchemaName): Promise<ValidateFunction> {\n const cached = validators.get(name);\n if (cached) {\n return cached;\n }\n const schema = JSON.parse(\n await readFile(join(await schemaDir(), SCHEMA_FILES[name]), \"utf-8\")\n ) as object;\n const validator = ajv.compile(schema);\n validators.set(name, validator);\n return validator;\n}\n\n/** Validate `data` against a named schema, returning clear messages on failure. */\nexport async function validate(\n name: SchemaName,\n data: unknown\n): Promise<ValidationResult> {\n const fn = await getValidator(name);\n const valid = fn(data);\n return { errors: valid ? [] : (fn.errors ?? []).map(formatError), valid };\n}\n\n// Turn an Ajv error into a single readable line. The default `message` omits the\n// offending key for `additionalProperties`/`required`, which is exactly what an\n// author needs to spot a typo, so we splice it back in.\nfunction formatError(err: ErrorObject): string {\n const where = err.instancePath || \"(root)\";\n switch (err.keyword) {\n case \"additionalProperties\": {\n return `${where}: unexpected property \"${String(err.params.additionalProperty)}\"`;\n }\n case \"required\": {\n return `${where}: missing required property \"${String(err.params.missingProperty)}\"`;\n }\n case \"enum\": {\n const allowed = (err.params.allowedValues as unknown[]).join(\", \");\n return `${where}: ${err.message ?? \"is invalid\"} (${allowed})`;\n }\n default: {\n return `${where}: ${err.message ?? \"is invalid\"}`;\n }\n }\n}\n\n// --- Typed views + convenience validators the applier (issue #6) builds on. ---\n\nexport interface SaasaloyConfig {\n aliases: Record<string, string>;\n /**\n * The project's own name, written once by `saasaloy init` and never rewritten. It is the\n * `{{PROJECT_NAME}}` every base file was rendered with, so `update` re-renders the\n * template with the same value instead of guessing from the directory — a git worktree,\n * a renamed folder or a CI checkout path used to rewrite `package.json` `name`,\n * `wrangler.jsonc` `name` and `siteName` to the folder's name. Optional so a project\n * scaffolded before the field existed still validates; `resolveProjectName` falls back.\n */\n name?: string;\n /**\n * The base app `saasaloy init` scaffolded (`web`). It is not a module: it has no\n * descriptor and is never in `installed`. Its files record under the reserved manifest\n * module name `base`, and the lock's `base` object carries its provenance (#120). Until #98\n * the template listed it in `installed[]` and every engine carried an excuse for the\n * one name in that list it could say nothing about. Optional so a project scaffolded\n * before the field existed still validates; `loadConfig` migrates it.\n */\n base?: string;\n installed: string[];\n}\n\nexport interface RegistryFile {\n path: string;\n target: string;\n /**\n * Install this file only when the named module is in the resolved install set (#99).\n * Two entries may share one `target` under disjoint conditions, which is how a module\n * ships a sqlite and a pg variant of one dialect-bound file. `RegistryScaffold.files`\n * is this same type, so the keyword covers a scaffold's workspace files too.\n */\n onlyWith?: string;\n}\n\n// A new workspace a capability births. Its `files[].target`s are workspace-root-relative\n// (no `@alias` — the alias root doesn't exist until this scaffold lands), and it declares\n// the aliases the applier registers into saasaloy.json (ADR 0013).\nexport interface RegistryScaffold {\n /** New workspace directory, project-relative POSIX (e.g. `apps/api`). */\n workspace: string;\n /** Aliases this scaffold registers into saasaloy.json (e.g. `{ \"@api\": \"apps/api/src\" }`). */\n aliases?: Record<string, string>;\n /** Files copied into the workspace; each `target` is relative to `workspace`. */\n files: RegistryFile[];\n}\n\nexport interface RegistryAgent {\n skills?: string[];\n}\n\n// A structural config patch as authored in registry-item.json: an engine `Patch`\n// (kind + payload) plus the project-relative `file` it targets. Serialized as a flat\n// array so one module can patch several files, each op self-describing (ADR 0019).\nexport type RegistryPatch = { file: string } & Patch;\n\nexport interface RegistryItem {\n name: string;\n type: \"saasaloy:capability\" | \"saasaloy:feature\";\n /**\n * Tool versions this descriptor needs. `saasaloy` is a semver range the running CLI has\n * to satisfy; a CLI outside it refuses the whole run before planning a single write,\n * naming this module even when it arrived as a transitive `dependsOn` (#50).\n */\n requires?: { saasaloy?: string };\n dependsOn?: string[];\n /** Modules this one refuses to sit beside; `add` refuses rather than installing both. Recorded in the lockfile so the check works in either install order. */\n conflictsWith?: string[];\n /** Modules exactly one of which has to be present — a capability naming its mutually exclusive drivers. `add` refuses when none is installed or in the resolved graph, and offers the list as a prompt on a terminal. */\n requiresOneOf?: string[];\n /** npm deps merged into the consumer's `dependencies`. Exact-pinned `name@version` (bare/range rejected by the schema). */\n dependencies?: string[];\n /** npm deps merged into the consumer's `devDependencies` (`@types/*`, build tooling). Same exact-pin form; a name in both buckets lands in `dependencies` only. */\n devDependencies?: string[];\n files?: RegistryFile[];\n envVars?: Record<string, string>;\n /** Local-dev values for a subset of `envVars`, pre-filled into `.dev.vars.example`. Never a secret: a loopback URL or a fixed port, the same on every machine. */\n devVars?: Record<string, string>;\n /** Warnings persisted at add time and printed before this module is removed. */\n removeWarnings?: string[];\n patches?: RegistryPatch[];\n scaffolds?: RegistryScaffold[];\n agent?: RegistryAgent;\n}\n\nexport function validateSaasaloyConfig(\n data: unknown\n): Promise<ValidationResult> {\n return validate(\"saasaloy\", data);\n}\n\nexport function validateManifest(data: unknown): Promise<ValidationResult> {\n return validate(\"manifest\", data);\n}\n\nexport function validateRegistryItem(data: unknown): Promise<ValidationResult> {\n return validate(\"registry-item\", data);\n}\n\nexport function validateLock(data: unknown): Promise<ValidationResult> {\n return validate(\"saasaloy-lock\", data);\n}\n","import { builders, generateCode, parseModule } from \"magicast\";\nimport {\n describeBinding,\n foreignBinding,\n isReferenced,\n keepTerminator,\n} from \"./ts-ast.js\";\nimport type { ModuleImports } from \"./ts-ast.js\";\n\n// `magicast` codemod for a chained-call router entry file — Hono's RPC shape, where\n// `const app = new Hono().route(\"/x\", x)` is the exported chain a client derives\n// `typeof app` from. The sibling of ts-module.ts's plugin-array insert, split out\n// because a call chain is a different structure from an array literal.\n//\n// Unlike the other three kinds this one ships its inverse (`removeChainedRoute`), so\n// `saasaloy remove` takes the link and its import back out instead of only warning.\n// Both directions are pure string→string, so the applier and the remover can preview\n// them. Nothing writes an anchor or sentinel comment: magicast is backed by recast, so\n// untouched lines keep their exact source and the chain locates itself (ADR 0006).\n\nexport interface ChainedRoute {\n /** Exported binding whose call chain to extend, e.g. \"default\" or \"app\". */\n exportName: string;\n /** Route path — `.route()`'s first argument, e.g. \"/waitlist\". Also the match key. */\n path: string;\n /** Identifier passed as `.route()`'s second argument, e.g. \"waitlist\". */\n call: string;\n /** Named import to ensure is present for `call`. */\n import: { name: string; from: string };\n}\n\n/**\n * Append `.route(<path>, <call>)` to the chain the module exports as `exportName`,\n * adding the named import if missing. Idempotent and formatting-safe:\n *\n * - the chain already routes `path` → return `source` **unchanged** (never clobber);\n * - the export is a bare identifier (`export default app`) → follow it to its\n * declaration and extend that, so `export type AppType = typeof app` keeps working;\n * - the chain has no `.route()` link yet → this becomes the first one;\n * - import already present → not duplicated;\n * - the local name is already bound to a *different* import → return `source`\n * unchanged, because wiring the route would bind the wrong handler\n * (`chainedRouteInsertRefusal` reports why);\n * - export missing or an unrecognised shape → return `source` unchanged.\n */\nexport function insertChainedRoute(\n source: string,\n patch: ChainedRoute\n): string {\n const mod = parseModule(source);\n const slot = findChainSlot(mod.$ast as unknown as Program, patch.exportName);\n if (!slot) {\n return source;\n } // not the shape we expected — leave it be\n\n for (const link of chainLinks(slot.node)) {\n if (isRouteLink(link, patch.path)) {\n return source;\n } // already routed\n }\n\n const imports = mod.imports;\n if (insertBindingConflict(imports, patch)) {\n return source;\n } // wrong binding — never wire it\n\n // Ensure the named import exists (magicast keys imports by local name).\n if (!(patch.import.name in mod.imports)) {\n mod.imports.$add({\n from: patch.import.from,\n imported: patch.import.name,\n local: patch.import.name,\n });\n }\n\n slot.replace(buildRouteLink(slot.node, patch.path, patch.call));\n return keepTerminator(source, generateCode(mod).code);\n}\n\n/**\n * Why `insertChainedRoute` declined to change `source`, or `undefined` when it had no\n * objection (it applied the link, or the link was already there).\n *\n * The codemods are pure `string → string`, so a refusal is indistinguishable from an\n * idempotent no-op at the call site. `applyPatch` asks this only when nothing changed,\n * which is what lets `add` warn by name instead of silently skipping a route.\n */\nexport function chainedRouteInsertRefusal(\n source: string,\n patch: ChainedRoute\n): string | undefined {\n const mod = parseModule(source);\n const slot = findChainSlot(mod.$ast as unknown as Program, patch.exportName);\n if (!slot) {\n return undefined;\n } // unrecognised shape, reported by the caller as \"missing\"\n for (const link of chainLinks(slot.node)) {\n if (isRouteLink(link, patch.path)) {\n return undefined;\n } // already routed, not a refusal\n }\n return insertBindingConflict(mod.imports, patch);\n}\n\n/**\n * The inverse: drop the `.route()` link matching `path` from the exported chain, and\n * drop the named import it needed.\n *\n * Located by `path`, since that is the key the forward direction is idempotent on, then\n * **verified against the recorded handler** before anything is deleted. A user who\n * repointed the route at their own handler owns that line, and drift is surfaced rather\n * than resolved here.\n *\n * - link already absent → return `source` **unchanged**, so a hand-reverted file is\n * never force-edited (the remover warns and skips instead);\n * - the link routes `path` to anything but `call` → return `source` **unchanged**\n * (`chainedRouteRemoveRefusal` reports why);\n * - the local name now binds a different import → return `source` **unchanged**, since\n * repointing the import changes what the link means without touching the link;\n * - the link is the only one → the bare receiver is left behind (`const app = new\n * Hono();`), which still parses and typechecks;\n * - the import statement holds other specifiers → only this one is removed;\n * - the identifier is still referenced elsewhere in the file → the import stays, so a\n * hand-written second use (`app.use(waitlist.middleware)`) is not left unbound.\n */\nexport function removeChainedRoute(\n source: string,\n patch: ChainedRoute\n): string {\n const mod = parseModule(source);\n const slot = findChainSlot(mod.$ast as unknown as Program, patch.exportName);\n if (!slot) {\n return source;\n }\n\n // Walk outermost-inward, remembering the link that holds the one we're looking for —\n // splicing a link out means re-pointing its holder at the link's own receiver.\n let holder: CallExpression | undefined;\n let found: CallExpression | undefined;\n for (const link of chainLinks(slot.node)) {\n if (isRouteLink(link, patch.path)) {\n found = link;\n break;\n }\n holder = link;\n }\n if (!found) {\n return source;\n } // already gone — never force-edit\n if (handlerDrift(found, patch)) {\n return source;\n } // user's handler — not ours to delete\n // The link can read exactly as written while meaning something else: repointing the\n // import changes what `waitlist` resolves to without touching the `.route()` line. That\n // makes both the link and the import the user's, so neither is ours to delete.\n if (removeBindingConflict(mod.imports, patch)) {\n return source;\n }\n\n const receiver = (found.callee as MemberExpression).object;\n if (holder) {\n (holder.callee as MemberExpression).object = receiver;\n } else {\n slot.replace(receiver);\n }\n\n // Guarded twice: magicast's delete trap throws when the local name isn't imported, and\n // a binding the file still references elsewhere must keep its import or the file stops\n // compiling.\n const program = mod.$ast as unknown as Program;\n if (\n patch.import.name in mod.imports &&\n !isReferenced(program, patch.import.name)\n ) {\n delete mod.imports[patch.import.name];\n }\n return keepTerminator(source, generateCode(mod).code);\n}\n\n/**\n * Why `removeChainedRoute` declined to change `source`, or `undefined` when it had no\n * objection (it removed the link, or the link was already gone). The mirror of\n * `chainedRouteInsertRefusal`, and the same reason for existing: it tells the remover's\n * \"nothing left to revert\" apart from \"that route is yours now\".\n */\nexport function chainedRouteRemoveRefusal(\n source: string,\n patch: ChainedRoute\n): string | undefined {\n const mod = parseModule(source);\n const slot = findChainSlot(mod.$ast as unknown as Program, patch.exportName);\n if (!slot) {\n return undefined;\n }\n for (const link of chainLinks(slot.node)) {\n if (isRouteLink(link, patch.path)) {\n return (\n handlerDrift(link, patch) ?? removeBindingConflict(mod.imports, patch)\n );\n }\n }\n return undefined; // already gone, not a refusal\n}\n\n// --- Ownership -------------------------------------------------------------------\n// Both directions key on the route path, and a path is not proof of ownership. These two\n// answer \"is this link, or this binding, still the one the manifest recorded?\" — the check\n// that keeps a patch from clobbering a hand edit that happens to sit at the same path.\n// `foreignBinding` and `describeBinding` are in ts-ast.ts: ts-module.ts's inverse asks the\n// same question of the same import proxy, so only the message text differs.\n\nfunction insertBindingConflict(\n imports: ModuleImports,\n patch: ChainedRoute\n): string | undefined {\n const held = foreignBinding(imports, patch.import);\n if (!held) {\n return undefined;\n }\n return `${describeBinding(patch.import.name, held)}, so routing ${JSON.stringify(patch.path)} would bind the wrong handler`;\n}\n\nfunction removeBindingConflict(\n imports: ModuleImports,\n patch: ChainedRoute\n): string | undefined {\n const held = foreignBinding(imports, patch.import);\n if (!held) {\n return undefined;\n }\n return `${describeBinding(patch.import.name, held)} now, not from ${JSON.stringify(patch.import.from)}, so the link and its import are not the ones that were applied`;\n}\n\n/**\n * Whether the link found at the recorded path routes it somewhere other than the recorded\n * handler. Anything that isn't the plain `call` identifier (or dotted path) counts, an\n * inline arrow included: the second argument is no longer what the manifest recorded.\n */\nfunction handlerDrift(\n link: CallExpression,\n patch: ChainedRoute\n): string | undefined {\n const argument = link.arguments[1];\n const handler = argument === undefined ? undefined : dottedName(argument);\n if (handler === patch.call) {\n return undefined;\n }\n const now =\n handler === undefined ? \"an inline expression\" : JSON.stringify(handler);\n return `${JSON.stringify(patch.path)} now routes to ${now}, not to ${JSON.stringify(patch.call)}`;\n}\n\n/** `waitlist` → \"waitlist\", `api.waitlist` → \"api.waitlist\", anything else → undefined. */\nfunction dottedName(node: AstNode): string | undefined {\n if (node.type === \"Identifier\") {\n return (node as Identifier).name;\n }\n if (node.type !== \"MemberExpression\") {\n return undefined;\n }\n const member = node as MemberExpression;\n if (member.property.type !== \"Identifier\") {\n return undefined;\n }\n const object = dottedName(member.object);\n return object === undefined\n ? undefined\n : `${object}.${(member.property as Identifier).name}`;\n}\n\n// --- AST shapes -------------------------------------------------------------------\n// magicast types its AST as a broad union; these are the narrow shapes this codemod\n// reads, so the casts below stay local and named.\n\ninterface AstNode {\n type: string;\n}\ninterface Identifier extends AstNode {\n name: string;\n}\ninterface StringLike extends AstNode {\n value: unknown;\n}\ninterface MemberExpression extends AstNode {\n object: AstNode;\n property: AstNode;\n}\ninterface CallExpression extends AstNode {\n callee: AstNode;\n arguments: AstNode[];\n}\ninterface VariableDeclarator extends AstNode {\n id: AstNode;\n init: AstNode | null;\n}\ninterface VariableDeclaration extends AstNode {\n declarations: VariableDeclarator[];\n}\ninterface ExportDefaultDeclaration extends AstNode {\n declaration: AstNode;\n}\ninterface ExportNamedDeclaration extends AstNode {\n declaration: AstNode | null;\n}\ninterface Program extends AstNode {\n body: AstNode[];\n}\n\n/** A writable position in the AST holding the chain expression. */\ninterface ChainSlot {\n /** The expression currently in the slot. */\n node: AstNode;\n /** Put a different expression in its place. */\n replace(next: AstNode): void;\n}\n\n// --- Locating the chain -----------------------------------------------------------\n\nfunction findChainSlot(\n program: Program,\n exportName: string\n): ChainSlot | undefined {\n const exported = findExportSlot(program, exportName);\n if (!exported) {\n return undefined;\n }\n // `export default app` exports a reference, not the chain. Follow it to the\n // declaration so the edit lands on `const app = …` and the export line is untouched.\n if (exported.node.type === \"Identifier\") {\n const declared = findDeclaratorSlot(\n program,\n (exported.node as Identifier).name\n );\n if (declared) {\n return declared;\n }\n }\n return exported;\n}\n\nfunction findExportSlot(\n program: Program,\n exportName: string\n): ChainSlot | undefined {\n for (const statement of program.body) {\n if (\n exportName === \"default\" &&\n statement.type === \"ExportDefaultDeclaration\"\n ) {\n const node = statement as ExportDefaultDeclaration;\n return {\n node: node.declaration,\n replace: (next) => {\n node.declaration = next;\n },\n };\n }\n if (\n exportName !== \"default\" &&\n statement.type === \"ExportNamedDeclaration\"\n ) {\n const declaration = (statement as ExportNamedDeclaration).declaration;\n if (declaration?.type === \"VariableDeclaration\") {\n const slot = declaratorSlot(\n declaration as VariableDeclaration,\n exportName\n );\n if (slot) {\n return slot;\n }\n }\n }\n }\n return undefined;\n}\n\nfunction findDeclaratorSlot(\n program: Program,\n name: string\n): ChainSlot | undefined {\n for (const statement of program.body) {\n const declaration =\n statement.type === \"VariableDeclaration\"\n ? statement\n : statement.type === \"ExportNamedDeclaration\"\n ? (statement as ExportNamedDeclaration).declaration\n : undefined;\n if (declaration?.type !== \"VariableDeclaration\") {\n continue;\n }\n const slot = declaratorSlot(declaration as VariableDeclaration, name);\n if (slot) {\n return slot;\n }\n }\n return undefined;\n}\n\nfunction declaratorSlot(\n declaration: VariableDeclaration,\n name: string\n): ChainSlot | undefined {\n for (const declarator of declaration.declarations) {\n if (declarator.id.type !== \"Identifier\") {\n continue;\n }\n if ((declarator.id as Identifier).name !== name) {\n continue;\n }\n if (!declarator.init) {\n continue;\n }\n return {\n node: declarator.init,\n replace: (next) => {\n declarator.init = next;\n },\n };\n }\n return undefined;\n}\n\n// --- Reading and writing the chain ------------------------------------------------\n\n/**\n * Yield each `x.y(…)` link of a call chain, outermost first.\n *\n * @yields {CallExpression} the next link, walking inward from the outermost call.\n */\nfunction* chainLinks(node: AstNode): Generator<CallExpression> {\n let current = node;\n while (\n current.type === \"CallExpression\" &&\n (current as CallExpression).callee.type === \"MemberExpression\"\n ) {\n const call = current as CallExpression;\n yield call;\n current = (call.callee as MemberExpression).object;\n }\n}\n\nfunction isRouteLink(link: CallExpression, path: string): boolean {\n const property = (link.callee as MemberExpression).property;\n if (\n property.type !== \"Identifier\" ||\n (property as Identifier).name !== \"route\"\n ) {\n return false;\n }\n const first = link.arguments[0];\n return first !== undefined && stringValue(first) === path;\n}\n\n// The parser emits `StringLiteral`; magicast's own builders emit ast-types `Literal`.\n// A chain can hold both once a previous patch has run, so read either.\nfunction stringValue(node: AstNode): string | undefined {\n if (node.type !== \"StringLiteral\" && node.type !== \"Literal\") {\n return undefined;\n }\n const value = (node as StringLike).value;\n return typeof value === \"string\" ? value : undefined;\n}\n\n// magicast has no builder for a chained method call, so parse the link as an\n// expression against a placeholder receiver and graft the real chain in as the\n// receiver. The grafted node keeps its original source, so recast reprints only the\n// appended link.\nfunction buildRouteLink(\n receiver: AstNode,\n path: string,\n call: string\n): AstNode {\n const parsed = builders.raw(\n `__chain__.route(${JSON.stringify(path)}, ${call})`\n ) as unknown as {\n $ast: AstNode;\n };\n const link = parsed.$ast as CallExpression;\n (link.callee as MemberExpression).object = receiver;\n return link;\n}\n","// The bits of AST and import handling both magicast codemods need. chained-route.ts and\n// ts-module.ts ask the same three questions in their inverse direction — \"does the local\n// name still bind what we imported?\", \"is the name still used anywhere else?\", \"did recast\n// eat the file's terminator?\" — so the answers live here once rather than twice.\n\n/** Structural view of magicast's import proxy: local name → what it binds. */\nexport type ModuleImports = Record<\n string,\n { imported?: unknown; from?: unknown } | undefined\n>;\n\n/** A named import as a descriptor declares it. */\nexport interface NamedImport {\n name: string;\n from: string;\n}\n\n/**\n * The binding currently held by the patch's local name, when it is not the recorded one.\n *\n * magicast keys imports by *local* name, so `want.name in mod.imports` proves only that\n * something holds the name — not that it is the binding this patch needs. Both directions\n * care: an insert would wire the wrong value, and a remove would delete an import line the\n * user rewrote. Compare the local name, the imported name, and the source. An unbound name\n * is not a conflict; it is the ordinary case.\n */\nexport function foreignBinding(\n imports: ModuleImports,\n want: NamedImport\n): { imported: string; from: string } | undefined {\n const held = imports[want.name];\n if (!held) {\n return undefined;\n }\n\n const imported = typeof held.imported === \"string\" ? held.imported : \"\";\n const from = typeof held.from === \"string\" ? held.from : \"\";\n if (imported === want.name && from === want.from) {\n return undefined;\n }\n return { imported, from };\n}\n\n/** \"…is imported as a default import from \"./legacy.js\"\" — the shared half of both reasons. */\nexport function describeBinding(\n name: string,\n held: { imported: string; from: string }\n): string {\n const bound =\n held.imported === \"*\"\n ? \"as a namespace import\"\n : held.imported === \"default\"\n ? \"as a default import\"\n : `as ${JSON.stringify(held.imported)}`;\n return `${JSON.stringify(name)} is imported ${bound} from ${JSON.stringify(held.from)}`;\n}\n\n// recast reprints the whole program, and its printer drops a trailing newline the source\n// had. Untouched bytes must stay untouched, so put the terminator back — an add→remove\n// round trip is then byte-identical to the file the user started with.\nexport function keepTerminator(source: string, code: string): string {\n if (source.endsWith(\"\\n\") && !code.endsWith(\"\\n\")) {\n return `${code}\\n`;\n }\n if (!source.endsWith(\"\\n\") && code.endsWith(\"\\n\")) {\n return code.slice(0, -1);\n }\n return code;\n}\n\n/** The one thing `isReferenced` needs of a program: its top-level statements. */\nexport interface ProgramLike {\n body: unknown[];\n}\n\n// Does any non-import part of the program still read `name`? Walks the plain-object AST\n// rather than pulling in a visitor, since the only question is whether an `Identifier`\n// with this name survives outside the import declarations and outside member/property\n// positions (`x.waitlist` is not a use of `waitlist`).\nexport function isReferenced(program: ProgramLike, name: string): boolean {\n let found = false;\n\n function walk(node: unknown, key?: string): void {\n if (found || node === null || typeof node !== \"object\") {\n return;\n }\n if (Array.isArray(node)) {\n for (const child of node) {\n walk(child);\n }\n return;\n }\n const record = node as Record<string, unknown> & { type?: string };\n if (typeof record.type !== \"string\") {\n return;\n }\n if (record.type === \"ImportDeclaration\") {\n return;\n } // the import itself is not a use\n if (record.type === \"Identifier\") {\n // Skip a name in a non-reference slot: `a.name`, `{ name: … }`, `function name()`.\n if (key !== \"property\" && key !== \"key\" && record.name === name) {\n found = true;\n }\n return;\n }\n for (const [childKey, value] of Object.entries(record)) {\n // recast hangs its own bookkeeping off these; walking them re-walks the whole file.\n if (\n childKey === \"loc\" ||\n childKey === \"comments\" ||\n childKey === \"original\"\n ) {\n continue;\n }\n // A computed member or key (`obj[name]`, `{ [name]: … }`) is a real reference, so\n // clear the slot name and let the identifier count.\n walk(value, record.computed === true ? undefined : childKey);\n }\n }\n\n walk(program.body);\n return found;\n}\n","import { parseModule } from \"magicast\";\n\nexport interface ConstArrayInsert {\n /** Module-scope const array to extend. */\n constName: string;\n /** Property whose value identifies an entry. */\n key: string;\n /** JSON object literal to append. */\n entry: Record<string, unknown>;\n}\n\n/** Append an object to a named module-scope const array, matched by one property. */\nexport function insertIntoConstArray(\n source: string,\n patch: ConstArrayInsert\n): string {\n const mod = parseModule(source);\n const array = findConstArray(mod.$ast as unknown as Program, patch.constName);\n const wanted = patch.entry[patch.key];\n if (!array || wanted === undefined || hasEntry(array, patch.key, wanted)) {\n return source;\n }\n\n return appendEntry(source, array, renderObject(patch.entry));\n}\n\n/** Explain why an unchanged insert could not find its declared target shape. */\nexport function constArrayInsertRefusal(\n source: string,\n patch: ConstArrayInsert\n): string | undefined {\n const mod = parseModule(source);\n const array = findConstArray(mod.$ast as unknown as Program, patch.constName);\n if (!array) {\n return `const-array could not find a module-scope const array named ${patch.constName}`;\n }\n if (patch.entry[patch.key] === undefined) {\n return `const-array entry has no identity property named ${patch.key}`;\n }\n return undefined;\n}\n\n/** Remove the object whose identity property matches the recorded entry. */\nexport function removeFromConstArray(\n source: string,\n patch: ConstArrayInsert\n): string {\n const mod = parseModule(source);\n const array = findConstArray(mod.$ast as unknown as Program, patch.constName);\n const wanted = patch.entry[patch.key];\n if (!array || wanted === undefined) {\n return source;\n }\n\n const index = array.elements.findIndex(\n (element) => entryValue(element, patch.key) === wanted\n );\n if (index === -1) {\n return source;\n }\n return removeEntry(source, array, index);\n}\n\nfunction appendEntry(\n source: string,\n array: ArrayExpression,\n entry: string\n): string {\n const close = array.end - 1;\n const closeLineStart = source.lastIndexOf(\"\\n\", close - 1) + 1;\n const closeIndent = source.slice(closeLineStart, close);\n const isMultiline = closeIndent.trim() === \"\" && closeLineStart > array.start;\n if (!isMultiline) {\n const separator = array.elements.length === 0 ? \"\" : \", \";\n return `${source.slice(0, close)}${separator}${entry}${source.slice(close)}`;\n }\n\n let before = source.slice(0, closeLineStart);\n const last = array.elements.at(-1);\n if (last && !source.slice(last.end, close).includes(\",\")) {\n before = `${source.slice(0, last.end)},${source.slice(last.end, closeLineStart)}`;\n }\n const itemIndent = `${closeIndent} `;\n return `${before}${itemIndent}${entry},\\n${source.slice(closeLineStart)}`;\n}\n\nfunction removeEntry(\n source: string,\n array: ArrayExpression,\n index: number\n): string {\n const element = array.elements[index];\n if (!element) {\n return source;\n }\n const lineStart = source.lastIndexOf(\"\\n\", element.start - 1) + 1;\n const lineEnd = source.indexOf(\"\\n\", element.end);\n const ownLine = source.slice(lineStart, element.start).trim() === \"\";\n if (ownLine && lineEnd !== -1) {\n return `${source.slice(0, lineStart)}${source.slice(lineEnd + 1)}`;\n }\n\n const next = array.elements[index + 1];\n if (next) {\n return `${source.slice(0, element.start)}${source.slice(next.start)}`;\n }\n const previous = array.elements[index - 1];\n const start = previous\n ? source.lastIndexOf(\",\", element.start - 1)\n : element.start;\n return `${source.slice(0, start)}${source.slice(element.end)}`;\n}\n\nfunction hasEntry(\n array: ArrayExpression,\n key: string,\n wanted: unknown\n): boolean {\n return array.elements.some((element) => entryValue(element, key) === wanted);\n}\n\nfunction entryValue(element: AstNode | null, key: string): unknown {\n if (element?.type !== \"ObjectExpression\") {\n return undefined;\n }\n for (const property of (element as ObjectExpression).properties) {\n if (property.type !== \"ObjectProperty\" || property.computed) {\n continue;\n }\n if (propertyName(property.key) !== key) {\n continue;\n }\n return literalValue(property.value);\n }\n return undefined;\n}\n\nfunction propertyName(node: AstNode): string | undefined {\n if (node.type === \"Identifier\") {\n return (node as Identifier).name;\n }\n return literalValue(node) as string | undefined;\n}\n\nfunction literalValue(node: AstNode): unknown {\n if (\n node.type !== \"StringLiteral\" &&\n node.type !== \"NumericLiteral\" &&\n node.type !== \"BooleanLiteral\" &&\n node.type !== \"NullLiteral\" &&\n node.type !== \"Literal\"\n ) {\n return undefined;\n }\n return node.type === \"NullLiteral\" ? null : (node as Literal).value;\n}\n\nfunction findConstArray(\n program: Program,\n constName: string\n): ArrayExpression | undefined {\n for (const statement of program.body) {\n const declaration =\n statement.type === \"ExportNamedDeclaration\"\n ? (statement as ExportNamedDeclaration).declaration\n : statement;\n if (\n declaration?.type !== \"VariableDeclaration\" ||\n (declaration as VariableDeclaration).kind !== \"const\"\n ) {\n continue;\n }\n for (const declarator of (declaration as VariableDeclaration)\n .declarations) {\n if (\n declarator.id.type !== \"Identifier\" ||\n (declarator.id as Identifier).name !== constName ||\n !declarator.init\n ) {\n continue;\n }\n const value = unwrapTypeExpression(declarator.init);\n return value.type === \"ArrayExpression\"\n ? (value as ArrayExpression)\n : undefined;\n }\n }\n return undefined;\n}\n\nfunction unwrapTypeExpression(node: AstNode): AstNode {\n let value = node;\n while (\n value.type === \"TSAsExpression\" ||\n value.type === \"TSSatisfiesExpression\" ||\n value.type === \"TypeCastExpression\"\n ) {\n value = (value as TypeExpression).expression;\n }\n return value;\n}\n\nfunction renderObject(entry: Record<string, unknown>): string {\n return `{ ${Object.entries(entry)\n .map(([key, value]) => `${renderKey(key)}: ${renderValue(value)}`)\n .join(\", \")} }`;\n}\n\nfunction renderKey(key: string): string {\n return /^[A-Za-z_$][\\w$]*$/.test(key) ? key : JSON.stringify(key);\n}\n\nfunction renderValue(value: unknown): string {\n if (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"boolean\"\n ) {\n return JSON.stringify(value);\n }\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return String(value);\n }\n if (Array.isArray(value)) {\n return `[${value.map(renderValue).join(\", \")}]`;\n }\n if (typeof value === \"object\") {\n return renderObject(value as Record<string, unknown>);\n }\n throw new TypeError(\"const-array entries must contain JSON values\");\n}\n\ninterface AstNode {\n type: string;\n start: number;\n end: number;\n}\ninterface Identifier extends AstNode {\n name: string;\n}\ninterface Literal extends AstNode {\n value: unknown;\n}\ninterface ObjectProperty extends AstNode {\n computed: boolean;\n key: AstNode;\n value: AstNode;\n}\ninterface ObjectExpression extends AstNode {\n properties: ObjectProperty[];\n}\ninterface ArrayExpression extends AstNode {\n elements: (AstNode | null)[];\n}\ninterface VariableDeclarator extends AstNode {\n id: AstNode;\n init: AstNode | null;\n}\ninterface VariableDeclaration extends AstNode {\n declarations: VariableDeclarator[];\n kind: string;\n}\ninterface ExportNamedDeclaration extends AstNode {\n declaration: AstNode | null;\n}\ninterface TypeExpression extends AstNode {\n expression: AstNode;\n}\ninterface Program extends AstNode {\n body: AstNode[];\n}\n","import { createTwoFilesPatch } from \"diff\";\n\n// Every patch is `--dry-run`/`--diff`-able (build spec §3.4): the engine is pure and\n// always hands back a unified diff of what it *would* write, so the applier can show\n// it and decide whether to commit the change to disk.\n\n/**\n * Render a unified diff between `before` and `after` for `filename`.\n * Returns `\"\"` when the two are identical — the signal an applier uses to\n * report \"nothing to do\" and never touch the file (idempotent re-run).\n */\nexport function toDiff(\n before: string,\n after: string,\n filename: string\n): string {\n if (before === after) {\n return \"\";\n }\n // Same path on both sides: this is an in-place edit, not a rename.\n const patch = createTwoFilesPatch(filename, filename, before, after, \"\", \"\");\n // Drop the leading `Index:`-style banner createPatch would add; createTwoFilesPatch\n // starts straight at the `---`/`+++` header, which is what we want to surface.\n return patch;\n}\n","import {\n applyEdits,\n findNodeAtLocation,\n getNodeValue,\n modify,\n parseTree,\n} from \"jsonc-parser\";\nimport type { FormattingOptions, Node } from \"jsonc-parser\";\n\n// `jsonc-parser` edits for `wrangler.jsonc` binding/route changes (build spec §3.4).\n// It rewrites only the touched region and leaves comments + surrounding formatting\n// intact — the reason we don't just `JSON.parse` → stringify, which would strip the\n// comments Cloudflare configs rely on.\n\nexport interface WranglerBinding {\n /**\n * Array to upsert into, e.g. \"d1_databases\", \"kv_namespaces\", \"routes\".\n *\n * A value with a dot addresses a **nested** array — \"queues.producers\",\n * \"queues.consumers\", \"triggers.crons\" (ADR 0033). Missing parent objects are\n * created on insert and unwound on removal, so the round trip is byte-identical.\n * A value with no dot keeps the top-level behaviour exactly.\n */\n bindingType: string;\n /**\n * The value to insert: an object (a binding or a route, matched by `matchOn`) or a\n * bare string (a flag, e.g. `compatibility_flags: [\"nodejs_compat\"]`, matched by\n * plain equality).\n */\n entry: Record<string, unknown> | string;\n /**\n * Property that identifies an object `entry` for idempotency. Defaults to \"binding\"\n * (bindings); pass \"pattern\" for routes, etc. Ignored for a string `entry`.\n */\n matchOn?: string;\n}\n\n/**\n * Insert `entry` into the top-level `bindingType` array of a `wrangler.jsonc`\n * document, idempotently and formatting-safe:\n *\n * - array missing → create it holding `entry`;\n * - array present, no entry matches (`matchOn` for an object, equality for a string)\n * → append `entry`;\n * - a matching entry already exists → return `source` **unchanged** (never clobber a\n * value the user may have edited).\n */\nexport function upsertWranglerBinding(\n source: string,\n patch: WranglerBinding\n): string {\n const matchOn = patch.matchOn ?? \"binding\";\n const root = parseTree(source);\n if (!root) {\n return source;\n } // unparseable — leave it to the caller/validator to surface\n\n const bindingPath = splitBindingType(patch.bindingType);\n const arrayNode = findNodeAtLocation(root, bindingPath);\n const formattingOptions = inferFormatting(source);\n\n if (arrayNode?.type === \"array\") {\n const existing = (arrayNode.children ?? []).map(\n (child) => getNodeValue(child) as unknown\n );\n const { entry } = patch;\n // A local `const` (not the `patch.entry` property access) so the `typeof` guard's\n // narrowing survives into the closure below — TS doesn't narrow property accesses\n // across a nested function boundary the way it does a plain variable.\n const alreadyPresent =\n typeof entry === \"string\"\n ? existing.includes(entry)\n : existing.some(\n (value) => isRecord(value) && value[matchOn] === entry[matchOn]\n );\n if (alreadyPresent) {\n return source;\n }\n\n const edits = modify(\n source,\n [...bindingPath, existing.length],\n patch.entry,\n {\n formattingOptions,\n isArrayInsertion: true,\n }\n );\n return applyEdits(source, edits);\n }\n\n // No array (or a non-array value) at that key — create the array fresh. `modify`\n // creates any missing parent object along a nested path on its way there.\n const edits = modify(source, bindingPath, [patch.entry], {\n formattingOptions,\n });\n return applyEdits(source, edits);\n}\n\n/**\n * The inverse: take `entry` back out of the top-level `bindingType` array, and take the\n * array with it when that leaves it empty.\n *\n * Located by the same key the forward direction is idempotent on (`matchOn` for an\n * object, equality for a string), then **verified against the recorded entry** before\n * anything is deleted. A user who edited the value we wrote owns it, so drift is\n * surfaced rather than resolved here.\n *\n * - the entry is already absent (or the array is) → return `source` **unchanged**, so a\n * hand-reverted file is never force-edited (the remover warns and skips instead);\n * - the entry under the match key holds anything but the recorded value → return\n * `source` **unchanged** (`wranglerBindingRemoveRefusal` reports why);\n * - the array still holds siblings → only this entry goes, comments and formatting kept;\n * - the array is left empty → the whole `bindingType` property goes, so a module that\n * *created* the array restores the file to its pre-patch bytes. An array that was\n * already there but empty before the patch is the one case that does not round-trip:\n * the manifest records no pre-patch state to tell the two apart, and dropping an empty\n * array is the harmless side of that guess.\n */\nexport function removeWranglerBinding(\n source: string,\n patch: WranglerBinding\n): string {\n const root = parseTree(source);\n if (!root) {\n return source;\n } // unparseable — leave it to the caller/validator to surface\n\n const bindingPath = splitBindingType(patch.bindingType);\n const arrayNode = findNodeAtLocation(root, bindingPath);\n if (arrayNode?.type !== \"array\") {\n return source;\n } // no array — nothing of ours to take out\n\n const existing = (arrayNode.children ?? []).map(\n (child) => getNodeValue(child) as unknown\n );\n const index = findEntryIndex(existing, patch);\n if (index === -1) {\n return source;\n } // already gone — never force-edit\n if (matchWranglerBinding(source, patch)) {\n return source;\n } // the user's entry now — not ours to delete\n\n // Last one out takes the array with it; the forward direction created it. On a\n // nested path it takes every parent the forward direction created too, so\n // `queues` and `triggers` never survive as empty objects.\n const path: (number | string)[] =\n existing.length === 1\n ? emptiedAncestorPath(root, bindingPath)\n : [...bindingPath, index];\n // Same formatting the forward direction infers, for the same reason: jsonc-parser's\n // raw deletion leaves the removed element's separator and indent behind (`} ]`), which\n // the generated project's own `prettier --check` would then fail.\n const edits = modify(source, path, undefined, {\n formattingOptions: inferFormatting(source),\n });\n return applyEdits(source, edits);\n}\n\n/**\n * Why `removeWranglerBinding` declined to change `source`, or `undefined` when it had no\n * objection (it removed the entry, or the entry was already gone).\n *\n * The codemods are pure `string → string`, so a refusal is indistinguishable from an\n * idempotent no-op at the call site. `reversePatch` asks this only when nothing changed,\n * which is what lets `remove` warn by name instead of silently leaving a binding behind.\n *\n * `matchWranglerBinding` already answers exactly this question in the forward direction —\n * \"something holds the match key at a value we did not write\" — so the two directions\n * agree on drift by construction. A string `entry` matches by equality and so can never\n * drift.\n */\nexport function wranglerBindingRemoveRefusal(\n source: string,\n patch: WranglerBinding\n): string | undefined {\n const match = matchWranglerBinding(source, patch);\n if (!match) {\n return undefined;\n }\n return `${match.key} holds ${JSON.stringify(match.current)} now, not the entry that was applied, so it is not ours to delete`;\n}\n\n/**\n * A `bindingType` as a `jsonc-parser` location path. \"d1_databases\" stays one\n * segment; \"queues.producers\" becomes two, which is the whole of the dotted-path\n * extension ADR 0033 asks for.\n */\nfunction splitBindingType(bindingType: string): string[] {\n return bindingType.split(\".\");\n}\n\n/**\n * The shallowest prefix of `bindingPath` that deleting the array would leave empty.\n *\n * Removing the last entry of `queues.producers` should take `producers` **and** the\n * `queues` object the forward direction created, so the file returns to its\n * pre-patch bytes. A parent holding a sibling (`queues.consumers` is still there)\n * stops the walk, and so does a top-level path, which has no parent to unwind.\n */\nfunction emptiedAncestorPath(root: Node, bindingPath: string[]): string[] {\n let cut = bindingPath.length;\n for (let depth = bindingPath.length - 1; depth >= 1; depth -= 1) {\n const parent = findNodeAtLocation(root, bindingPath.slice(0, depth));\n if (parent?.type !== \"object\" || (parent.children?.length ?? 0) !== 1) {\n break;\n }\n cut = depth;\n }\n return bindingPath.slice(0, cut);\n}\n\n/** Where the entry the forward direction would call \"already present\" sits, or -1. */\nfunction findEntryIndex(existing: unknown[], patch: WranglerBinding): number {\n const { entry } = patch;\n if (typeof entry === \"string\") {\n return existing.indexOf(entry);\n }\n const matchOn = patch.matchOn ?? \"binding\";\n return existing.findIndex(\n (value) => isRecord(value) && value[matchOn] === entry[matchOn]\n );\n}\n\n/**\n * What an already-present entry looks like when it *isn't* the one the descriptor\n * wants — the signal `saasaloy update` needs to tell \"already applied\" apart from\n * \"the user edited the value we would have written\" (issue #48, decision 1).\n */\nexport interface BindingMatch {\n /** Which entry matched, as `<bindingType>[<matchOn>=<value>]`. */\n key: string;\n /** The entry as the document holds it today. */\n current: unknown;\n /** The entry the descriptor declares. */\n wanted: unknown;\n}\n\n/**\n * Find the entry `upsertWranglerBinding` would consider \"already present\" and report\n * it when it differs from `patch.entry`. Returns `undefined` when nothing matches\n * (the patch will apply) or when the match is byte-equal (a true idempotent re-run).\n *\n * A string `entry` is matched by plain equality, so a match is always identical —\n * there is nothing to report.\n */\nexport function matchWranglerBinding(\n source: string,\n patch: WranglerBinding\n): BindingMatch | undefined {\n const entry = patch.entry;\n if (typeof entry === \"string\") {\n return undefined;\n }\n\n const root = parseTree(source);\n if (!root) {\n return undefined;\n }\n const arrayNode = findNodeAtLocation(\n root,\n splitBindingType(patch.bindingType)\n );\n if (arrayNode?.type !== \"array\") {\n return undefined;\n }\n\n const matchOn = patch.matchOn ?? \"binding\";\n const wantedId = entry[matchOn];\n for (const child of arrayNode.children ?? []) {\n const value = getNodeValue(child) as unknown;\n if (!isRecord(value) || value[matchOn] !== wantedId) {\n continue;\n }\n if (sameValue(value, entry)) {\n return undefined;\n }\n return {\n key: `${patch.bindingType}[${matchOn}=${String(wantedId)}]`,\n current: value,\n wanted: entry,\n };\n }\n return undefined;\n}\n\n// Order-insensitive structural equality. Both sides come from JSON, so a canonical\n// re-serialization with sorted keys is enough — and cheaper than a deep walk.\nfunction sameValue(a: unknown, b: unknown): boolean {\n return canonical(a) === canonical(b);\n}\n\nfunction canonical(value: unknown): string {\n if (Array.isArray(value)) {\n return `[${value.map(canonical).join(\",\")}]`;\n }\n if (isRecord(value)) {\n return `{${Object.keys(value)\n .toSorted()\n .map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)\n .join(\",\")}}`;\n }\n return JSON.stringify(value) ?? \"null\";\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n// Match the document's own indentation so inserted lines don't stand out. wrangler\n// configs are conventionally 2-space, but respect tabs if that's what the file uses.\n// Exported for pkg-json.ts, the other jsonc-parser codemod — same document-formatting\n// concern, different top-level shape (package.json dependency maps vs. binding arrays).\nexport function inferFormatting(source: string): FormattingOptions {\n const usesTabs = /^\\t/m.test(source);\n const spaceIndent = source.match(/^( +)\\S/m)?.[1];\n return {\n eol: source.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\",\n insertSpaces: !usesTabs,\n tabSize: spaceIndent ? spaceIndent.length : 2,\n };\n}\n","import {\n applyEdits,\n findNodeAtLocation,\n getNodeValue,\n modify,\n parseTree,\n} from \"jsonc-parser\";\nimport { inferFormatting } from \"./jsonc.js\";\n\n// `jsonc-parser` edits for package.json dependency merges — the counterpart to\n// upsertWranglerBinding, for the one case a module's config lives outside its own\n// scaffold: adding a dependency to a package.json ANOTHER module already wrote (e.g.\n// `database` making itself importable from `api`). Rewrites only the touched region,\n// preserving the rest of the document's formatting.\n\nexport interface PackageJsonDependency {\n /** Which dependency map to upsert into. */\n section:\n | \"dependencies\"\n | \"devDependencies\"\n | \"peerDependencies\"\n | \"optionalDependencies\";\n /** npm or workspace package name. */\n name: string;\n /** Version range to write (e.g. \"workspace:*\", \"^1.2.3\"). */\n range: string;\n}\n\n/**\n * Insert `name: range` into a package.json's `section` map, idempotently and\n * formatting-safe:\n *\n * - section missing → create it holding `{ [name]: range }`;\n * - section present, name absent → add the entry;\n * - name already present (any range) → return `source` **unchanged** (never clobber\n * a value the user may have edited, matching `upsertWranglerBinding`).\n */\nexport function upsertPackageJsonDependency(\n source: string,\n patch: PackageJsonDependency\n): string {\n const root = parseTree(source);\n if (!root) {\n return source;\n } // unparseable — leave it to the caller/validator to surface\n\n const formattingOptions = inferFormatting(source);\n const sectionNode = findNodeAtLocation(root, [patch.section]);\n\n if (sectionNode?.type === \"object\") {\n const existing = getNodeValue(sectionNode) as Record<string, unknown>;\n if (Object.hasOwn(existing, patch.name)) {\n return source;\n }\n\n const edits = modify(source, [patch.section, patch.name], patch.range, {\n formattingOptions,\n });\n return applyEdits(source, edits);\n }\n\n // No section (or a non-object value) at that key — create it fresh.\n const edits = modify(\n source,\n [patch.section],\n { [patch.name]: patch.range },\n {\n formattingOptions,\n }\n );\n return applyEdits(source, edits);\n}\n\n/**\n * Report the dependency this patch would have added when it is already declared at a\n * *different* range — the same \"already present, but not what we wanted\" signal\n * `matchWranglerBinding` gives for bindings (issue #48, decision 1). `undefined` when\n * the name is absent (the patch applies) or already pinned to `patch.range`.\n */\nexport function matchPackageJsonDependency(\n source: string,\n patch: PackageJsonDependency\n): { key: string; current: unknown; wanted: unknown } | undefined {\n const root = parseTree(source);\n if (!root) {\n return undefined;\n }\n const sectionNode = findNodeAtLocation(root, [patch.section]);\n if (sectionNode?.type !== \"object\") {\n return undefined;\n }\n\n const existing = getNodeValue(sectionNode) as Record<string, unknown>;\n if (!Object.hasOwn(existing, patch.name)) {\n return undefined;\n }\n const current = existing[patch.name];\n if (current === patch.range) {\n return undefined;\n }\n return {\n key: `${patch.section}[${patch.name}]`,\n current,\n wanted: patch.range,\n };\n}\n","import {\n applyEdits,\n findNodeAtLocation,\n getNodeValue,\n modify,\n parseTree,\n} from \"jsonc-parser\";\nimport { inferFormatting } from \"./jsonc.js\";\n\n// `jsonc-parser` edits for package.json `scripts` entries — the sibling of\n// upsertPackageJsonDependency, for the case where a module needs a command runnable in a\n// workspace ANOTHER module scaffolded (e.g. `database` adding `db:generate` to the app it\n// wires itself into). Rewrites only the touched region, preserving the rest of the\n// document's formatting.\n\nexport interface PackageJsonScript {\n /** Script key to upsert (e.g. \"db:generate\"). */\n name: string;\n /** Command the script runs (e.g. \"drizzle-kit generate\"). */\n value: string;\n}\n\n// Script keys npm and pnpm run on their own, without the user typing them: the four\n// install hooks and the publish hooks. A descriptor comes from a registry the user may\n// not control (third-party `owner/repo/name` coordinates are a documented form), so a\n// patch that lands one of these earns arbitrary code execution on the victim's next\n// `pnpm install` (#98). The denylist is exact plus the `prepublish*` family, and the\n// codemod refuses rather than upserting. `install:deps` and `preparse` are ordinary\n// names and stay allowed, which is why the check is equality, not a prefix scan.\n// npm generates a `pre`/`post` pair around every script it runs, so blocking `prepare`\n// without `preprepare` and `postprepare` leaves two keys that run on the same\n// `npm install`.\nconst LIFECYCLE_SCRIPTS: ReadonlySet<string> = new Set([\n \"install\",\n \"postinstall\",\n \"postprepare\",\n \"preinstall\",\n \"prepare\",\n \"preprepare\",\n]);\n\nfunction isLifecycleScript(name: string): boolean {\n return LIFECYCLE_SCRIPTS.has(name) || name.startsWith(\"prepublish\");\n}\n\n/**\n * Why this codemod declined to write, or `undefined` when it had no objection. Feeds\n * the patch engine's refusal table, so `add` names the rule and the key instead of\n * reporting a silent no-op.\n */\nexport function packageJsonScriptRefusal(\n _source: string,\n patch: PackageJsonScript\n): string | undefined {\n if (!isLifecycleScript(patch.name)) {\n return undefined;\n }\n return `refusing to write the ${JSON.stringify(patch.name)} script: package managers run install and publish lifecycle keys automatically, so a module descriptor may not define one.`;\n}\n\n/**\n * Insert `name: value` into a package.json's `scripts` map, idempotently and\n * formatting-safe:\n *\n * - `name` is an install/publish lifecycle key → return `source` **unchanged**\n * (see `packageJsonScriptRefusal` for the reason the caller reports);\n * - `scripts` missing (or holding a non-object) → create it holding `{ [name]: value }`;\n * - `scripts` present, name absent → add the entry;\n * - name already present (any command, including `\"\"`) → return `source` **unchanged**\n * (never clobber a command the user may have edited, matching\n * `upsertPackageJsonDependency`).\n *\n * Every write goes to the `[\"scripts\", name]` JSON path, so the patch kind can only ever\n * reach the scripts map — no sibling key is created, moved, or read.\n */\nexport function upsertPackageJsonScript(\n source: string,\n patch: PackageJsonScript\n): string {\n if (isLifecycleScript(patch.name)) {\n return source;\n }\n\n const root = parseTree(source);\n if (!root) {\n return source;\n } // unparseable — leave it to the caller/validator to surface\n\n const formattingOptions = inferFormatting(source);\n const scriptsNode = findNodeAtLocation(root, [\"scripts\"]);\n\n if (scriptsNode?.type === \"object\") {\n const existing = getNodeValue(scriptsNode) as Record<string, unknown>;\n if (Object.hasOwn(existing, patch.name)) {\n return source;\n }\n\n const edits = modify(source, [\"scripts\", patch.name], patch.value, {\n formattingOptions,\n });\n return applyEdits(source, edits);\n }\n\n // No `scripts` (or a non-object value) at that key — create it fresh.\n const edits = modify(\n source,\n [\"scripts\"],\n { [patch.name]: patch.value },\n { formattingOptions }\n );\n return applyEdits(source, edits);\n}\n","import { builders, generateCode, parseModule } from \"magicast\";\nimport {\n describeBinding,\n foreignBinding,\n isReferenced,\n keepTerminator,\n} from \"./ts-ast.js\";\nimport type { ModuleImports, ProgramLike } from \"./ts-ast.js\";\n\n// `magicast` codemods for TS/JS module edits (build spec §3.4): the canonical case is\n// pushing `stripe()` into Better Auth's `plugins` array. magicast is built on recast,\n// so untouched lines keep their exact original formatting — the edit is surgical.\n//\n// Both directions ship: `saasaloy remove` takes the call and its import back out instead\n// of only warning. They are pure string→string, so the applier and the remover can\n// preview them.\n\nexport interface PluginArrayInsert {\n /** Exported binding whose first call-argument object holds the array, e.g. \"auth\". */\n exportName: string;\n /** Property on that object which is the array, e.g. \"plugins\". */\n arrayProp: string;\n /** Factory to call and append, e.g. \"stripe\" → produces `stripe()`. */\n call: string;\n /** Named import to ensure is present for `call`. */\n import: { name: string; from: string };\n}\n\n/**\n * Append `call()` to `export const <exportName> = <fn>({ <arrayProp>: [...] })`,\n * adding the named import if missing. Idempotent and formatting-safe:\n *\n * - `call` already in the array → return `source` **unchanged** (never clobber);\n * - array property absent → create it as `[call()]`;\n * - import already present → not duplicated.\n */\nexport function insertIntoPluginArray(\n source: string,\n patch: PluginArrayInsert\n): string {\n const mod = parseModule(source);\n\n const exported = mod.exports[patch.exportName];\n const callArg = exported?.$args?.[0];\n if (!callArg) {\n return source;\n } // not the shape we expected — leave it be\n\n const array: unknown = callArg[patch.arrayProp];\n\n // Already present? Detect by the callee name of each function-call element.\n if (Array.isArray(array) && findCallIndex(array, patch.call) !== -1) {\n return source;\n }\n\n // Ensure the named import exists (magicast keys imports by local name).\n if (!(patch.import.name in mod.imports)) {\n mod.imports.$add({\n from: patch.import.from,\n imported: patch.import.name,\n local: patch.import.name,\n });\n }\n\n const newCall = builders.functionCall(patch.call);\n if (Array.isArray(array)) {\n array.push(newCall);\n } else {\n // No array yet — create the property holding a single call.\n callArg[patch.arrayProp] = [newCall];\n }\n\n return print(source, mod);\n}\n\n/**\n * The inverse: take `call()` back out of the array, take the array with it when that\n * leaves it empty, and drop the named import it needed.\n *\n * Located by the callee name, since that is the key the forward direction is idempotent\n * on, then **verified against what the descriptor recorded** before anything is deleted.\n * The forward direction writes a bare `call()` from a named import, so anything else under\n * that name is the user's edit, and drift is surfaced rather than resolved here.\n *\n * - the call is already absent (or the array is) → return `source` **unchanged**, so a\n * hand-reverted file is never force-edited (the remover warns and skips instead);\n * - the call now takes arguments → return `source` **unchanged**\n * (`pluginArrayRemoveRefusal` reports why);\n * - the local name now binds a different import → return `source` **unchanged**, since\n * repointing the import changes what the call means without touching the call;\n * - the array is left empty → it stays, as an empty array literal. The capability that\n * owns the file ships that property and depends on it being there for the next install;\n * - the import statement holds other specifiers → only this one is removed;\n * - the identifier is still referenced elsewhere in the file → the import stays, so a\n * hand-written second use (`stripe.plans`) is not left unbound.\n */\nexport function removeFromPluginArray(\n source: string,\n patch: PluginArrayInsert\n): string {\n const mod = parseModule(source);\n\n const exported = mod.exports[patch.exportName];\n const callArg = exported?.$args?.[0];\n if (!callArg) {\n return source;\n } // not the shape we expected — leave it be\n\n const array: unknown = callArg[patch.arrayProp];\n if (!Array.isArray(array)) {\n return source;\n } // no array — nothing of ours to take out\n\n const index = findCallIndex(array, patch.call);\n if (index === -1) {\n return source;\n } // already gone — never force-edit\n if (argumentDrift(array[index], patch)) {\n return source;\n } // the user's call now — not ours to delete\n // The call can read exactly as written while meaning something else: repointing the\n // import changes what `stripe` resolves to without touching the `stripe()` line. That\n // makes both the call and the import the user's, so neither is ours to delete.\n if (removeBindingConflict(mod.imports, patch)) {\n return source;\n }\n\n array.splice(index, 1);\n // The emptied array stays. Unlike a `wrangler.jsonc` binding array, which no module\n // ships and every patch therefore creates, the capability that owns this file writes\n // the array itself — `export const email = defineEmail({ providers: [] })`, with a\n // comment above it saying never to omit the property even while it is empty, because\n // the forward codemod has nothing to push into otherwise. Deleting it would return the\n // file to something the capability never shipped.\n\n // Guarded twice: magicast's delete trap throws when the local name isn't imported, and\n // a binding the file still references elsewhere must keep its import or the file stops\n // compiling.\n const program = mod.$ast as unknown as ProgramLike;\n if (\n patch.import.name in mod.imports &&\n !isReferenced(program, patch.import.name)\n ) {\n delete mod.imports[patch.import.name];\n }\n return print(source, mod);\n}\n\n/**\n * Why `removeFromPluginArray` declined to change `source`, or `undefined` when it had no\n * objection (it removed the call, or the call was already gone). The mirror of what\n * `chainedRouteRemoveRefusal` does for a route: it tells the remover's \"nothing left to\n * revert\" apart from \"that plugin is yours now\".\n */\nexport function pluginArrayRemoveRefusal(\n source: string,\n patch: PluginArrayInsert\n): string | undefined {\n const mod = parseModule(source);\n const callArg = mod.exports[patch.exportName]?.$args?.[0];\n if (!callArg) {\n return undefined;\n }\n const array: unknown = callArg[patch.arrayProp];\n if (!Array.isArray(array)) {\n return undefined;\n }\n const index = findCallIndex(array, patch.call);\n if (index === -1) {\n return undefined;\n } // already gone, not a refusal\n return (\n argumentDrift(array[index], patch) ??\n removeBindingConflict(mod.imports, patch)\n );\n}\n\n// The generated project ships Prettier and runs it as part of its own `pnpm lint`, so what\n// the codemod writes has to survive `prettier --check`. Two recast defaults do not: it\n// prints new named imports as `{foo}`, and it drops the file's final newline. Both are\n// ours to correct, not the user's to re-fix after every `saasaloy add` or `remove`.\nfunction print(source: string, mod: ReturnType<typeof parseModule>): string {\n return keepTerminator(\n source,\n generateCode(mod, { format: { objectCurlySpacing: true } }).code\n );\n}\n\n/** Where the call the forward direction would call \"already present\" sits, or -1. */\nfunction findCallIndex(array: unknown[], call: string): number {\n // NB: magicast's array proxy hands raw AST nodes to `.some`/`.findIndex` callbacks\n // (no `$type`/`$callee`), so this indexes each element to get the wrapped proxy.\n // for-of is not equivalent: it would hand back the same raw nodes the callback form\n // does, which is the whole reason this indexes.\n // oxlint-disable-next-line typescript/prefer-for-of\n for (let i = 0; i < array.length; i++) {\n const el: unknown = array[i];\n if (isFunctionCall(el) && el.$callee === call) {\n return i;\n }\n }\n return -1;\n}\n\n/**\n * Whether the call found under the recorded callee takes arguments. The forward direction\n * writes a bare `call()`, so anything with an argument is a line the user wrote over —\n * deleting it would throw away configuration the manifest never recorded.\n */\nfunction argumentDrift(\n element: unknown,\n patch: PluginArrayInsert\n): string | undefined {\n if (!isFunctionCall(element) || (element.$args?.length ?? 0) === 0) {\n return undefined;\n }\n return `${patch.call}() in ${JSON.stringify(patch.arrayProp)} takes arguments now, not the bare call that was applied`;\n}\n\nfunction removeBindingConflict(\n imports: ModuleImports,\n patch: PluginArrayInsert\n): string | undefined {\n const held = foreignBinding(imports, patch.import);\n if (!held) {\n return undefined;\n }\n return `${describeBinding(patch.import.name, held)} now, not from ${JSON.stringify(patch.import.from)}, so the plugin and its import are not the ones that were applied`;\n}\n\nfunction isFunctionCall(\n value: unknown\n): value is { $type: string; $callee: string; $args?: unknown[] } {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (value as { $type?: unknown }).$type === \"function-call\"\n );\n}\n","import {\n chainedRouteInsertRefusal,\n chainedRouteRemoveRefusal,\n insertChainedRoute,\n removeChainedRoute,\n} from \"./chained-route.js\";\nimport type { ChainedRoute } from \"./chained-route.js\";\nimport {\n constArrayInsertRefusal,\n insertIntoConstArray,\n removeFromConstArray,\n} from \"./const-array.js\";\nimport type { ConstArrayInsert } from \"./const-array.js\";\nimport { toDiff } from \"./diff.js\";\nimport {\n matchWranglerBinding,\n removeWranglerBinding,\n upsertWranglerBinding,\n wranglerBindingRemoveRefusal,\n} from \"./jsonc.js\";\nimport type { WranglerBinding } from \"./jsonc.js\";\nimport {\n matchPackageJsonDependency,\n upsertPackageJsonDependency,\n} from \"./pkg-json.js\";\nimport type { PackageJsonDependency } from \"./pkg-json.js\";\nimport {\n packageJsonScriptRefusal,\n upsertPackageJsonScript,\n} from \"./pkg-json-script.js\";\nimport type { PackageJsonScript } from \"./pkg-json-script.js\";\nimport {\n insertIntoPluginArray,\n pluginArrayRemoveRefusal,\n removeFromPluginArray,\n} from \"./ts-module.js\";\nimport type { PluginArrayInsert } from \"./ts-module.js\";\n\n// The config-patch engine (build spec §3.4): the ~10% of module application that isn't\n// a pure file-drop. Small, well-tested AST codemods the applier (#6) invokes for\n// structural edits — `jsonc-parser` for `wrangler.jsonc` bindings/routes and package.json\n// dependency merges, `magicast` for TS/JS module edits (Better Auth plugin arrays). Every\n// patch is pure and `--dry-run`/`--diff`-able: `applyPatch` never writes, it returns the\n// would-be content plus a unified diff, and re-running an already-applied patch is a no-op.\n\nexport {\n chainedRouteInsertRefusal,\n chainedRouteRemoveRefusal,\n insertChainedRoute,\n removeChainedRoute,\n type ChainedRoute,\n} from \"./chained-route.js\";\nexport {\n constArrayInsertRefusal,\n insertIntoConstArray,\n removeFromConstArray,\n type ConstArrayInsert,\n} from \"./const-array.js\";\nexport { toDiff } from \"./diff.js\";\nexport {\n type BindingMatch,\n matchWranglerBinding,\n removeWranglerBinding,\n upsertWranglerBinding,\n type WranglerBinding,\n wranglerBindingRemoveRefusal,\n} from \"./jsonc.js\";\nexport {\n matchPackageJsonDependency,\n upsertPackageJsonDependency,\n type PackageJsonDependency,\n} from \"./pkg-json.js\";\nexport {\n packageJsonScriptRefusal,\n upsertPackageJsonScript,\n type PackageJsonScript,\n} from \"./pkg-json-script.js\";\nexport {\n insertIntoPluginArray,\n type PluginArrayInsert,\n pluginArrayRemoveRefusal,\n removeFromPluginArray,\n} from \"./ts-module.js\";\n\n/** A single structural patch, tagged by the codemod that applies it. */\nexport type Patch =\n | ({ kind: \"const-array\" } & ConstArrayInsert)\n | ({ kind: \"wrangler-binding\" } & WranglerBinding)\n | ({ kind: \"package-json-dependency\" } & PackageJsonDependency)\n | ({ kind: \"package-json-script\" } & PackageJsonScript)\n | ({ kind: \"plugin-array\" } & PluginArrayInsert)\n | ({ kind: \"chained-route\" } & ChainedRoute);\n\nexport type PatchKind = Patch[\"kind\"];\n\n// The `Patch` union at runtime. `Record<PatchKind, true>` is exhaustive in both\n// directions: a kind added to the union without a key here fails typecheck, and a key\n// that names no kind is an excess property. `PATCH_KINDS` is what `schema.test.ts`\n// holds both JSON Schemas to, so the enums can't drift apart again (#98).\nconst PATCH_KIND_KEYS: Record<PatchKind, true> = {\n \"chained-route\": true,\n \"const-array\": true,\n \"package-json-dependency\": true,\n \"package-json-script\": true,\n \"plugin-array\": true,\n \"wrangler-binding\": true,\n};\n\n/** Every patch kind the engine applies, sorted. */\nexport const PATCH_KINDS: readonly PatchKind[] = (\n Object.keys(PATCH_KIND_KEYS) as PatchKind[]\n).toSorted();\n\n/**\n * An entry the codemod found under the identity it matches on, holding a value other\n * than the one the descriptor declares — i.e. the user edited what we would have\n * written. Distinct from a plain `changed: false`, which also covers a clean\n * already-applied re-run (issue #48, decision 1).\n */\nexport interface PatchMatch {\n /** Where the collision is, as `<container>[<identity>]` (e.g. `d1_databases[binding=DB]`). */\n key: string;\n /** The value on disk today. */\n current: unknown;\n /** The value the descriptor declares. */\n wanted: unknown;\n}\n\nexport interface PatchResult {\n /** The would-be file content after the patch (equal to the input on a no-op). */\n content: string;\n /** `false` when the patch was already applied — the applier skips the write. */\n changed: boolean;\n /** Unified diff of the change; `\"\"` when nothing changed. */\n diff: string;\n /**\n * Why an unchanged result was *refused* rather than already-applied — a binding the\n * patch would have wired wrongly, a route the user repointed. `undefined` for a plain\n * idempotent no-op, which is the common case and says nothing worth reporting.\n */\n reason?: string;\n /**\n * Set when the patch was a no-op *because* something already occupies its identity\n * at a different value. `saasaloy update` reports these into the merge plan; `add`\n * ignores it, so its behaviour is unchanged.\n */\n matched?: PatchMatch;\n}\n\n/**\n * Apply one structural `patch` to `source` and report the result. Pure: it computes\n * the new content and a diff but writes nothing, so the caller can preview\n * (`--dry-run`/`--diff`) or commit as it sees fit. Idempotent — a patch already\n * present yields `changed: false` and an empty diff.\n */\nexport function applyPatch(\n source: string,\n patch: Patch,\n filename: string\n): PatchResult {\n const content = applyCodemod(source, patch);\n const changed = content !== source;\n const matched = matchExisting(source, patch);\n return {\n content,\n changed,\n diff: toDiff(source, content, filename),\n reason: changed ? undefined : refusalReason(REFUSALS, source, patch),\n ...(matched ? { matched } : {}),\n };\n}\n\n// Only the two jsonc codemods have an identity to collide on. A `plugin-array` insert\n// is matched by the call expression itself, so a match is always an exact re-run.\nfunction matchExisting(source: string, patch: Patch): PatchMatch | undefined {\n switch (patch.kind) {\n case \"wrangler-binding\": {\n return matchWranglerBinding(source, patch);\n }\n case \"package-json-dependency\": {\n return matchPackageJsonDependency(source, patch);\n }\n default: {\n return undefined;\n }\n }\n}\n\nfunction applyCodemod(source: string, patch: Patch): string {\n switch (patch.kind) {\n case \"const-array\": {\n return insertIntoConstArray(source, patch);\n }\n case \"wrangler-binding\": {\n return upsertWranglerBinding(source, patch);\n }\n case \"package-json-dependency\": {\n return upsertPackageJsonDependency(source, patch);\n }\n case \"package-json-script\": {\n return upsertPackageJsonScript(source, patch);\n }\n case \"plugin-array\": {\n return insertIntoPluginArray(source, patch);\n }\n case \"chained-route\": {\n return insertChainedRoute(source, patch);\n }\n default: {\n const exhaustive: never = patch;\n throw new Error(`unknown patch kind: ${JSON.stringify(exhaustive)}`);\n }\n }\n}\n\n// The one table of kind → inverse codemod. The four kinds that edit a *config* file\n// reverse: `chained-route` (#83), then `wrangler-binding`, `plugin-array` and\n// `const-array` (#36). The two `package.json` kinds deliberately do not — uninstalling an\n// npm dependency isn't derivable offline and other code may already use it, and nothing\n// asks for a script back (plan-remove-command-2026-07-25.md, non-goals). `remove`\n// drops-and-warns for every kind absent from this table, and both `isReversibleKind` and\n// `reversePatch` read it, so adding an inverse stays one edit.\ntype Inverse<K extends PatchKind> = (\n source: string,\n patch: Extract<Patch, { kind: K }>\n) => string;\n\nconst INVERSES: { [K in PatchKind]?: Inverse<K> } = {\n \"chained-route\": removeChainedRoute,\n \"const-array\": removeFromConstArray,\n \"plugin-array\": removeFromPluginArray,\n \"wrangler-binding\": removeWranglerBinding,\n};\n\n/** Whether `reversePatch` can undo a patch of this kind. */\nexport function isReversibleKind(kind: string): boolean {\n return Object.hasOwn(INVERSES, kind);\n}\n\n/**\n * Undo one structural `patch`, the mirror of `applyPatch`. Returns `undefined` for a\n * kind with no inverse yet, so the caller can tell \"nothing to reverse here\" from \"the\n * reversal ran and changed nothing\". Pure and idempotent, like the forward direction:\n * a patch already reversed yields `changed: false` and an empty diff, which is what\n * keeps a hand-reverted file from being force-edited.\n */\nexport function reversePatch(\n source: string,\n patch: Patch,\n filename: string\n): PatchResult | undefined {\n const inverse = INVERSES[patch.kind] as\n ((source: string, patch: Patch) => string) | undefined;\n if (!inverse) {\n return undefined;\n }\n const content = inverse(source, patch);\n const changed = content !== source;\n return {\n content,\n changed,\n diff: toDiff(source, content, filename),\n reason: changed\n ? undefined\n : refusalReason(REVERSAL_REFUSALS, source, patch),\n };\n}\n\n// The tables of kind → \"why did nothing change?\", one per direction. A codemod is pure\n// `string → string`, so a refusal to touch the file looks exactly like an idempotent\n// no-op at the call site; these tell the two apart, so `add` and `remove` can warn by\n// name instead of skipping in silence. Asking costs a second parse, so both callers ask\n// only when nothing changed. The forward table is short because most kinds only ever\n// no-op on an edit that is already present; `package-json-script` joined it in #98,\n// because it refuses an install-lifecycle key outright and a refusal a user never sees is\n// a security hole that reads as a typo. The reversal table follows `INVERSES`, minus\n// `const-array`, whose removal only ever no-ops on an entry that is already gone.\ntype Refusal<K extends PatchKind> = (\n source: string,\n patch: Extract<Patch, { kind: K }>\n) => string | undefined;\n\nconst REFUSALS: { [K in PatchKind]?: Refusal<K> } = {\n \"chained-route\": chainedRouteInsertRefusal,\n \"const-array\": constArrayInsertRefusal,\n \"package-json-script\": packageJsonScriptRefusal,\n};\n\nconst REVERSAL_REFUSALS: { [K in PatchKind]?: Refusal<K> } = {\n \"chained-route\": chainedRouteRemoveRefusal,\n \"plugin-array\": pluginArrayRemoveRefusal,\n \"wrangler-binding\": wranglerBindingRemoveRefusal,\n};\n\nfunction refusalReason(\n table: { [K in PatchKind]?: Refusal<K> },\n source: string,\n patch: Patch\n): string | undefined {\n const ask = table[patch.kind] as\n ((source: string, patch: Patch) => string | undefined) | undefined;\n return ask?.(source, patch);\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { RefusalError } from \"./exit.js\";\nimport { pathExists } from \"./fs-utils.js\";\nimport type { SaasaloyConfig } from \"./schema.js\";\nimport { validateSaasaloyConfig } from \"./schema.js\";\n\n// `saasaloy.json` is the consumer manifest at a generated project's root: the alias\n// map that resolves module file targets, plus the `installed` list that drives\n// dependsOn resolution (build spec §3.2). This loads/saves it and turns an\n// alias-prefixed target (`@api/routes/x.ts`) into a project-relative path.\n\nexport const CONFIG_FILE = \"saasaloy.json\";\n\n// The raw parse keeps the optional `$schema` pointer so a round-trip save doesn't\n// strip an author's editor-validation hint.\nexport type LoadedConfig = SaasaloyConfig & { $schema?: string };\n\nexport async function loadConfig(root: string): Promise<LoadedConfig> {\n const file = join(root, CONFIG_FILE);\n if (!(await pathExists(file))) {\n throw new RefusalError(\n `No ${CONFIG_FILE} found in ${root}. Run \\`saasaloy init\\` first, or cd into a Saasaloy project.`\n );\n }\n const parsed = JSON.parse(await readFile(file, \"utf-8\")) as unknown;\n const result = await validateSaasaloyConfig(parsed);\n if (!result.valid) {\n throw new RefusalError(\n `${CONFIG_FILE} is invalid:\\n ${result.errors.join(\"\\n \")}`\n );\n }\n return migrateBase(parsed as LoadedConfig);\n}\n\n/**\n * The base app moved out of `installed[]` and into its own `base` field (#98). A project\n * scaffolded before that still lists `web` as if the tool had applied it, which is what\n * forced every engine to carry a \"except the template's own `web`\" branch. Lift it here,\n * once, on load: from this point on `installed[]` holds only modules `saasaloy add` put\n * there, and the next `saveConfig` persists the corrected shape.\n *\n * Only `web` is lifted. It is the one name the template ever wrote, and guessing at any\n * other would silently unmanage a real module.\n */\nconst LEGACY_BASE = \"web\";\n\nexport function migrateBase(config: LoadedConfig): LoadedConfig {\n if (config.base !== undefined || !config.installed.includes(LEGACY_BASE)) {\n return config;\n }\n return {\n ...config,\n base: LEGACY_BASE,\n installed: config.installed.filter((name) => name !== LEGACY_BASE),\n };\n}\n\nexport async function saveConfig(\n root: string,\n config: LoadedConfig\n): Promise<void> {\n const file = join(root, CONFIG_FILE);\n await writeFile(file, `${JSON.stringify(config, null, 2)}\\n`, \"utf-8\");\n}\n\n// Resolve `@api/routes/x.ts` against the alias map to a project-relative POSIX path\n// (e.g. `apps/api/src/routes/x.ts`). POSIX separators keep manifest keys stable\n// across OSes; callers split on \"/\" when touching the filesystem.\nexport function resolveTarget(\n aliases: Record<string, string>,\n target: string\n): string {\n const slash = target.indexOf(\"/\");\n if (slash === -1) {\n throw new RefusalError(\n `Malformed target \"${target}\" — expected \"@alias/rest\".`\n );\n }\n const alias = target.slice(0, slash);\n const rest = target.slice(slash + 1);\n const base = aliases[alias];\n if (base === undefined) {\n const known = Object.keys(aliases).join(\", \") || \"(none)\";\n throw new RefusalError(\n `Unknown alias \"${alias}\" in target \"${target}\". Known aliases: ${known}.`\n );\n }\n // `base` is already POSIX + no leading slash (enforced by the schema); join by hand\n // to avoid the platform separator that node:path would introduce on Windows.\n const trimmed = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n return `${trimmed}/${rest}`;\n}\n","// A semver range matcher, small enough to own (#50). The descriptor's `requires` field\n// names a range the running CLI has to satisfy, so the tool needs to answer \"does\n// version V fall inside range R?\" and nothing else — no version bumping, no sorting a\n// release feed, no coercion of loose input.\n//\n// That is why there is no `semver` dependency here. The npm package is ~10 files of\n// range arithmetic the CLI would ship to every consumer for one boolean, and the CLI's\n// runtime dependency list is deliberately short (`@clack/prompts`, `ajv`, `diff`,\n// `giget`, `jsonc-parser`, `magicast`, `picocolors`). The grammar below is the subset\n// the field can legally use, and every shape it accepts is pinned by a test.\n//\n// Supported: `*` / `x` / empty (any), partial versions (`1`, `1.2`, `1.x`), `^`, `~`,\n// `>`/`>=`/`<`/`<=`/`=`, whitespace as AND, `||` as OR, and the hyphen range `A - B`.\n//\n// Prereleases follow node-semver's default rule: `1.0.0-rc.1` does not satisfy `*` or\n// `>=0.9.0`. A prerelease is only admitted when some comparator in the same AND-set\n// names the same major.minor.patch *and* carries a prerelease of its own. Without that\n// rule every release candidate would silently pass a range written for stable versions.\n\nexport interface SemVer {\n major: number;\n minor: number;\n patch: number;\n /** Dot-separated prerelease identifiers, numeric ones parsed as numbers. Empty on a release. */\n prerelease: (string | number)[];\n}\n\n/** One `>=1.2.0`-shaped constraint. `=` is the exact-match form partial ranges expand into. */\nexport interface Comparator {\n op: \">\" | \">=\" | \"<\" | \"<=\" | \"=\";\n version: SemVer;\n}\n\n/**\n * A parsed range: an OR of AND-sets, mirroring `a || b` over `>=1 <2`. An empty AND-set\n * is `*` — it matches every release. An empty OR list is impossible; `parseRange`\n * returns `undefined` instead.\n */\nexport type Range = Comparator[][];\n\n// No leading zeroes in a numeric part: `01.2.3` is not a semver version, and accepting\n// it would make `01.2.3` and `1.2.3` two spellings of one release.\nconst VERSION_PATTERN =\n /^v?(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([0-9A-Za-z][0-9A-Za-z.-]*))?(?:\\+[0-9A-Za-z][0-9A-Za-z.-]*)?$/;\n\nconst NUMERIC = /^(?:0|[1-9]\\d*)$/;\n\nfunction parsePrerelease(raw: string | undefined): (string | number)[] {\n if (raw === undefined) {\n return [];\n }\n return raw\n .split(\".\")\n .map((part) => (NUMERIC.test(part) ? Number(part) : part));\n}\n\n/** A `SemVer` for a full `major.minor.patch` version, or `undefined` when it is not one. */\nexport function parseVersion(input: string): SemVer | undefined {\n const match = VERSION_PATTERN.exec(input.trim());\n if (!match) {\n return undefined;\n }\n return {\n major: Number(match[1]),\n minor: Number(match[2]),\n patch: Number(match[3]),\n prerelease: parsePrerelease(match[4]),\n };\n}\n\nfunction compareIdentifiers(a: string | number, b: string | number): number {\n const aNumeric = typeof a === \"number\";\n const bNumeric = typeof b === \"number\";\n // \"Numeric identifiers always have lower precedence than non-numeric identifiers.\"\n if (aNumeric && bNumeric) {\n return a < b ? -1 : a > b ? 1 : 0;\n }\n if (aNumeric) {\n return -1;\n }\n if (bNumeric) {\n return 1;\n }\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\n/** Negative when `a` precedes `b`, positive when it follows, zero when they are equal. */\nexport function compareVersions(a: SemVer, b: SemVer): number {\n for (const key of [\"major\", \"minor\", \"patch\"] as const) {\n if (a[key] !== b[key]) {\n return a[key] < b[key] ? -1 : 1;\n }\n }\n // A version with a prerelease precedes the release it leads to.\n if (a.prerelease.length === 0 && b.prerelease.length === 0) {\n return 0;\n }\n if (a.prerelease.length === 0) {\n return 1;\n }\n if (b.prerelease.length === 0) {\n return -1;\n }\n const shared = Math.min(a.prerelease.length, b.prerelease.length);\n for (let i = 0; i < shared; i++) {\n const result = compareIdentifiers(a.prerelease[i]!, b.prerelease[i]!);\n if (result !== 0) {\n return result;\n }\n }\n // A longer identifier list wins when every shared identifier is equal.\n return a.prerelease.length - b.prerelease.length;\n}\n\n/** One `1`, `1.2`, `1.2.3`, `1.x` token, with the parts the author left out marked absent. */\ninterface Partial {\n major?: number;\n minor?: number;\n patch?: number;\n prerelease: (string | number)[];\n}\n\nconst WILDCARD = new Set([\"\", \"*\", \"x\", \"X\"]);\nconst PARTIAL_PATTERN =\n /^v?(0|[1-9]\\d*|[xX*])(?:\\.(0|[1-9]\\d*|[xX*])(?:\\.(0|[1-9]\\d*|[xX*])(?:-([0-9A-Za-z][0-9A-Za-z.-]*))?(?:\\+[0-9A-Za-z][0-9A-Za-z.-]*)?)?)?$/;\n\nfunction parsePartial(raw: string): Partial | undefined {\n const input = raw.trim();\n if (WILDCARD.has(input)) {\n return { prerelease: [] };\n }\n const match = PARTIAL_PATTERN.exec(input);\n if (!match) {\n return undefined;\n }\n const part = (value: string | undefined): number | undefined =>\n value === undefined || WILDCARD.has(value) ? undefined : Number(value);\n const major = part(match[1]);\n const minor = part(match[2]);\n const patch = part(match[3]);\n // `1.x.3` names a patch under an unknown minor, which constrains nothing coherent.\n if (\n (major === undefined && (minor !== undefined || patch !== undefined)) ||\n (minor === undefined && patch !== undefined)\n ) {\n return undefined;\n }\n return {\n ...(major === undefined ? {} : { major }),\n ...(minor === undefined ? {} : { minor }),\n ...(patch === undefined ? {} : { patch }),\n prerelease: parsePrerelease(match[4]),\n };\n}\n\nfunction version(\n major: number,\n minor = 0,\n patch = 0,\n prerelease: (string | number)[] = []\n): SemVer {\n return { major, minor, patch, prerelease };\n}\n\n/** The lowest version a partial admits: `1.2` → `1.2.0`, `1` → `1.0.0`. */\nfunction floor(partial: Partial): SemVer {\n return version(\n partial.major ?? 0,\n partial.minor ?? 0,\n partial.patch ?? 0,\n partial.prerelease\n );\n}\n\n/** The first version *above* a partial: `1.2` → `1.3.0`, `1` → `2.0.0`. */\nfunction ceiling(partial: Partial): SemVer | undefined {\n if (partial.major === undefined) {\n return undefined;\n }\n if (partial.minor === undefined) {\n return version(partial.major + 1);\n }\n if (partial.patch === undefined) {\n return version(partial.major, partial.minor + 1);\n }\n return version(partial.major, partial.minor, partial.patch + 1);\n}\n\n/** `^1.2.3` → `<2.0.0`, `^0.2.3` → `<0.3.0`, `^0.0.3` → `<0.0.4`: the leftmost non-zero part moves. */\nfunction caretCeiling(partial: Partial): SemVer | undefined {\n const { major, minor, patch } = partial;\n if (major === undefined) {\n return undefined;\n }\n if (major !== 0 || minor === undefined) {\n return version(major + 1);\n }\n if (minor !== 0 || patch === undefined) {\n return version(0, minor + 1);\n }\n return version(0, 0, patch + 1);\n}\n\nconst OPERATOR_PATTERN = /^(>=|<=|>|<|=)?\\s*(.*)$/;\n\n/** The comparators one whitespace-separated token expands into, or `undefined` when it is not a token. */\nfunction parseToken(token: string): Comparator[] | undefined {\n const match = OPERATOR_PATTERN.exec(token);\n if (!match) {\n return undefined;\n }\n const raw = match[2] ?? \"\";\n const op = match[1];\n if (op !== undefined && WILDCARD.has(raw.trim())) {\n // `>=*` and friends: an operator with nothing to compare against.\n return raw.trim() === \"\" ? undefined : [];\n }\n if (raw.startsWith(\"^\") || raw.startsWith(\"~\")) {\n if (op !== undefined) {\n return undefined; // `>=^1.0.0` is not a range.\n }\n // A bare `^` or `~` parses as \"any\" in node-semver. Here it is a truncated range an\n // author meant to finish, so it is reported rather than silently widened.\n if (raw.slice(1).trim() === \"\") {\n return undefined;\n }\n const partial = parsePartial(raw.slice(1));\n if (!partial) {\n return undefined;\n }\n if (partial.major === undefined) {\n return [];\n }\n const upper = raw.startsWith(\"^\")\n ? caretCeiling(partial)\n : ceiling(partial);\n const lower: Comparator = { op: \">=\", version: floor(partial) };\n // `~1.2` and `~1.2.3` both stop at the next minor; `~1` stops at the next major.\n const tildeUpper =\n partial.minor === undefined\n ? version(partial.major + 1)\n : version(partial.major, partial.minor + 1);\n const bound = raw.startsWith(\"^\") ? upper : tildeUpper;\n return bound ? [lower, { op: \"<\", version: bound }] : [lower];\n }\n\n const partial = parsePartial(raw);\n if (!partial) {\n return undefined;\n }\n if (partial.major === undefined) {\n // `*`, `x`, and a bare `=` with a wildcard constrain nothing.\n return [];\n }\n const low = floor(partial);\n const high = ceiling(partial);\n switch (op) {\n case \">\": {\n // `>1.2` means \"after everything 1.2.x\", so it floors at the next minor.\n return [\n partial.patch === undefined && high\n ? { op: \">=\", version: high }\n : { op: \">\", version: low },\n ];\n }\n case \">=\": {\n return [{ op: \">=\", version: low }];\n }\n case \"<\": {\n return [{ op: \"<\", version: low }];\n }\n case \"<=\": {\n return [\n partial.patch === undefined && high\n ? { op: \"<\", version: high }\n : { op: \"<=\", version: low },\n ];\n }\n default: {\n // Bare or `=`: an exact version pins, a partial becomes the range it covers.\n if (partial.patch !== undefined) {\n return [{ op: \"=\", version: low }];\n }\n return high\n ? [\n { op: \">=\", version: low },\n { op: \"<\", version: high },\n ]\n : [{ op: \">=\", version: low }];\n }\n }\n}\n\nconst HYPHEN_PATTERN = /\\s+-\\s+/;\n\n/** The comparators one AND-set (`>=1 <2`, or `1.2.3 - 2.3.4`) expands into. */\nfunction parseComparatorSet(input: string): Comparator[] | undefined {\n const trimmed = input.trim();\n if (trimmed === \"\") {\n return [];\n }\n if (HYPHEN_PATTERN.test(trimmed)) {\n const sides = trimmed.split(HYPHEN_PATTERN);\n if (sides.length !== 2) {\n return undefined;\n }\n const low = parsePartial(sides[0]!);\n const high = parsePartial(sides[1]!);\n if (!low || !high) {\n return undefined;\n }\n const out: Comparator[] = [];\n if (low.major !== undefined) {\n out.push({ op: \">=\", version: floor(low) });\n }\n if (high.major === undefined) {\n return out;\n }\n // `1 - 2` includes all of 2.x; `1 - 2.3.4` stops at that exact version.\n const bound = ceiling(high);\n out.push(\n high.patch === undefined && bound\n ? { op: \"<\", version: bound }\n : { op: \"<=\", version: floor(high) }\n );\n return out;\n }\n\n const comparators: Comparator[] = [];\n for (const token of trimmed.split(/\\s+/)) {\n const parsed = parseToken(token);\n if (!parsed) {\n return undefined;\n }\n comparators.push(...parsed);\n }\n return comparators;\n}\n\n/** The parsed range, or `undefined` when the string is not a range this grammar accepts. */\nexport function parseRange(input: string): Range | undefined {\n const sets: Range = [];\n for (const part of input.split(\"||\")) {\n // An empty side of `||` (as in `\"||\"`) is a typo, not \"match anything\".\n if (input.includes(\"||\") && part.trim() === \"\") {\n return undefined;\n }\n const set = parseComparatorSet(part);\n if (!set) {\n return undefined;\n }\n sets.push(set);\n }\n return sets;\n}\n\n/** True when the string is a range `satisfies` can evaluate. `doctor` reports the rest. */\nexport function isValidRange(input: string): boolean {\n return parseRange(input) !== undefined;\n}\n\nfunction matches(value: SemVer, comparator: Comparator): boolean {\n const order = compareVersions(value, comparator.version);\n switch (comparator.op) {\n case \">\": {\n return order > 0;\n }\n case \">=\": {\n return order >= 0;\n }\n case \"<\": {\n return order < 0;\n }\n case \"<=\": {\n return order <= 0;\n }\n default: {\n return order === 0;\n }\n }\n}\n\nfunction sameTuple(a: SemVer, b: SemVer): boolean {\n return a.major === b.major && a.minor === b.minor && a.patch === b.patch;\n}\n\nfunction satisfiesSet(value: SemVer, set: Comparator[]): boolean {\n if (!set.every((comparator) => matches(value, comparator))) {\n return false;\n }\n if (value.prerelease.length === 0) {\n return true;\n }\n // node-semver's default: a prerelease is only in range when the author wrote a\n // prerelease of the same version into the range, so `>=0.9` never picks up `1.0.0-rc.1`.\n return set.some(\n (comparator) =>\n comparator.version.prerelease.length > 0 &&\n sameTuple(value, comparator.version)\n );\n}\n\n/** True when `version` falls inside `range`. False when either side does not parse. */\nexport function satisfies(candidate: string, range: string): boolean {\n const parsed = parseVersion(candidate);\n const sets = parseRange(range);\n if (!parsed || !sets) {\n return false;\n }\n return sets.some((set) => satisfiesSet(parsed, set));\n}\n","import { isValidRange, satisfies } from \"./semver.js\";\nimport type { Graph } from \"./resolve.js\";\n\n// `requires.saasaloy` enforcement — \"this descriptor needs a CLI at least this new\".\n//\n// A descriptor sets `additionalProperties: false`, so an old CLI meeting a field it has\n// never heard of already fails. It fails as an Ajv dump about an unexpected property,\n// which tells the user nothing about what to do, and a field whose *meaning* changed\n// fails not at all: the old CLI reads it the old way and half-applies the module. One\n// declared range turns both cases into the same sentence, before anything is written.\n//\n// Kept apart from `requires.ts`, which enforces `requiresOneOf` — a module-choice rule\n// with nothing to do with the CLI's version. Two unrelated meanings of \"requires\" in one\n// file would be a permanent reading tax.\n//\n// The check is fatal, never a warning. A module that says it needs a newer CLI is saying\n// the applier in front of it will get the module wrong, and a warning would leave the\n// project half-written with no way back.\n\n/** How a consumer gets a newer CLI. Named in every refusal so the fix is in the message. */\nexport const UPGRADE_COMMAND = \"pnpm add --global saasaloy@latest\";\n\n/** What `readVersion()` returns when the CLI's own package.json will not parse. */\nexport const UNKNOWN_VERSION = \"unknown\";\n\n/** Why one module's `requires` could not be satisfied. */\nexport type MismatchReason =\n /** The range parsed and the running CLI falls outside it. */\n | \"unsatisfied\"\n /** The range is not a range — an author error, reported rather than assumed to pass. */\n | \"unparseable\"\n /** The CLI cannot read its own version, so no range can be honestly evaluated. */\n | \"unknown-version\";\n\nexport interface CliMismatch {\n /** The module whose descriptor declared the range. May be a transitive prerequisite. */\n declaredBy: string;\n /** The range exactly as the descriptor spells it. */\n range: string;\n reason: MismatchReason;\n}\n\nexport interface DetectCliMismatchesArgs {\n /** The resolved `dependsOn` graph for this run — every descriptor it touched. */\n graph: Graph;\n /** The running CLI's version, from `readVersion()`. */\n cliVersion: string;\n}\n\n/**\n * Every module in the graph whose `requires.saasaloy` the running CLI fails, in\n * topological order so a prerequisite is reported before the module that dragged it in.\n * Empty means the run may proceed.\n *\n * Transitive by construction: the graph holds every descriptor resolution touched, not\n * only the one the user named, so a prerequisite's range is enforced exactly like the\n * requested module's.\n */\nexport function detectCliMismatches(\n args: DetectCliMismatchesArgs\n): CliMismatch[] {\n const { graph, cliVersion } = args;\n const mismatches: CliMismatch[] = [];\n\n for (const name of graph.order) {\n const range = graph.modules.get(name)?.item.requires?.saasaloy;\n // The field is optional, and its absence means \"any CLI\" — every descriptor written\n // before the field existed keeps working, on every version.\n if (range === undefined) {\n continue;\n }\n if (!isValidRange(range)) {\n mismatches.push({ declaredBy: name, range, reason: \"unparseable\" });\n continue;\n }\n // An unreadable version refuses rather than passing silently: \"I could not check\"\n // is not \"you are fine\", and the failure mode of guessing is a half-applied module.\n if (cliVersion === UNKNOWN_VERSION) {\n mismatches.push({ declaredBy: name, range, reason: \"unknown-version\" });\n continue;\n }\n if (!satisfies(cliVersion, range)) {\n mismatches.push({ declaredBy: name, range, reason: \"unsatisfied\" });\n }\n }\n\n return mismatches;\n}\n\n// One mismatch as a sentence. `requested` is what the user typed, so a range raised by a\n// transitive prerequisite says so rather than naming a module the user never mentioned —\n// the same shape `requires.ts` and `conflicts.ts` use.\nfunction describe(\n mismatch: CliMismatch,\n requested: string,\n cliVersion: string\n): string {\n const { declaredBy, range, reason } = mismatch;\n const subject =\n declaredBy === requested\n ? declaredBy\n : `${declaredBy} (required by ${requested})`;\n if (reason === \"unparseable\") {\n return `${subject} declares requires.saasaloy \"${range}\", which isn't a semver range — the module's author has to fix it.`;\n }\n if (reason === \"unknown-version\") {\n return `${subject} needs saasaloy ${range}, and this CLI's own version could not be read. Upgrade with \\`${UPGRADE_COMMAND}\\`.`;\n }\n return `${subject} needs saasaloy ${range}, and ${cliVersion} is installed. Upgrade with \\`${UPGRADE_COMMAND}\\`.`;\n}\n\n/** The refusal a command prints. One line per mismatch, under a heading. */\nexport function formatCliMismatches(\n mismatches: CliMismatch[],\n requested: string,\n cliVersion: string\n): string {\n const heading = `Cannot apply ${requested} — this CLI is too old for ${mismatches.length > 1 ? \"these modules\" : \"it\"}:`;\n return [\n heading,\n ...mismatches.map((m) => ` ${describe(m, requested, cliVersion)}`),\n `Nothing was written.`,\n ].join(\"\\n\");\n}\n","import type { Lockfile } from \"./lock.js\";\nimport type { Graph } from \"./resolve.js\";\nimport type { SaasaloyConfig } from \"./schema.js\";\n\n// `conflictsWith` enforcement for `saasaloy add` — the deterministic core, kept out of\n// commands/add.ts so it can be unit-tested (the commands have no test files).\n//\n// Two modules can be mutually exclusive: two drivers behind one capability, two mailers\n// behind one interface. Either side may be the one that declares it, and the refusal has\n// to fire whichever module went in first, so the check runs in both directions:\n//\n// forward — a descriptor in the resolved graph names an already-installed module;\n// reverse — an already-installed module's lock entry names a module in the graph.\n//\n// The reverse direction is why `conflictsWith` is recorded into saasaloy-lock.json beside\n// `dependsOn`: descriptors aren't kept after install, so the lock is the only offline\n// record of what an installed module refuses to sit beside. Same mechanism buildRemovePlan\n// already uses for reverse-dependency detection.\n//\n// A conflict is reported, never resolved. `add` refuses; it uninstalls nothing.\n\nexport interface ModuleConflict {\n /** The module that declared the conflict — in its descriptor, or in its lock entry. */\n declaredBy: string;\n /** The module named in that `conflictsWith` list. */\n conflictsWith: string;\n /**\n * Whichever of the two is already installed, and therefore the one `saasaloy remove`\n * can clear. Undefined when both modules arrive in this same run.\n */\n installed?: string;\n}\n\nexport interface ConflictReport {\n /** Every conflicting pair found, one per pair. Non-empty means `add` must refuse. */\n conflicts: ModuleConflict[];\n /**\n * Installed modules with no lock entry at all: nothing records what they conflict\n * with, so the reverse direction is unverifiable for them (mirrors\n * `RemovePlan.missingLockEntries`). A lock entry written before this field existed is\n * indistinguishable from one whose module declares no conflicts, so it can't be\n * flagged here — this covers the gap that is actually visible.\n *\n * Every name in `installed[]` is a module `saasaloy add` applied, so every one of them\n * belongs here when its lock entry is missing. The base app is no longer in that list:\n * it has its own `base` field (#98), which is what retired the `managed` allowlist this\n * report used to need.\n */\n missingLockEntries: string[];\n}\n\nexport interface DetectConflictsArgs {\n /** The resolved `dependsOn` graph for this run — descriptors included. */\n graph: Graph;\n config: SaasaloyConfig;\n lock: Lockfile;\n}\n\n// Order-independent key, so a pair both sides declare is reported once. The separator is\n// written as the two-character escape, never a raw NUL byte, or git treats this file as\n// binary and it stops being reviewable.\nfunction pairKey(a: string, b: string): string {\n return a < b ? `${a}\\0${b}` : `${b}\\0${a}`;\n}\n\nexport function detectConflicts(args: DetectConflictsArgs): ConflictReport {\n const { graph, config, lock } = args;\n const installed = new Set(config.installed);\n const conflicts: ModuleConflict[] = [];\n const seen = new Set<string>();\n\n function record(declaredBy: string, conflictsWith: string): void {\n // A module naming itself is a typo, not a self-conflict — ignore it.\n if (declaredBy === conflictsWith) {\n return;\n }\n const key = pairKey(declaredBy, conflictsWith);\n if (seen.has(key)) {\n return;\n }\n seen.add(key);\n const installedSide = installed.has(conflictsWith)\n ? conflictsWith\n : installed.has(declaredBy)\n ? declaredBy\n : undefined;\n conflicts.push({\n declaredBy,\n conflictsWith,\n ...(installedSide ? { installed: installedSide } : {}),\n });\n }\n\n // Forward. A fresh descriptor names something already installed, or something else in\n // this same graph — two mutually exclusive modules pulled in by one `add`.\n for (const [name, mod] of graph.modules) {\n for (const other of mod.item.conflictsWith ?? []) {\n if (installed.has(other) || graph.modules.has(other)) {\n record(name, other);\n }\n }\n }\n\n // Reverse. An installed module's lock entry names something arriving in this run.\n // Installed modules whose descriptor is in the graph are skipped: the forward pass just\n // read that descriptor, and a fresh descriptor beats a recorded copy of one.\n const missingLockEntries: string[] = [];\n for (const name of config.installed) {\n if (graph.modules.has(name)) {\n continue;\n }\n const entry = lock.modules[name];\n if (!entry) {\n missingLockEntries.push(name);\n continue;\n }\n for (const other of entry.conflictsWith ?? []) {\n if (graph.modules.has(other)) {\n record(name, other);\n }\n }\n }\n\n return { conflicts, missingLockEntries };\n}\n\n/**\n * Which command is reporting. `update` runs the same check because a new version can\n * introduce a `dependsOn` on a second driver, and installing it as a prerequisite would\n * land the exact pair `add` refuses (#98).\n */\nexport type ConflictAction = \"add\" | \"update\";\n\nconst GERUND: Record<ConflictAction, string> = {\n add: \"adding\",\n update: \"updating\",\n};\n\n// One conflict as a sentence: both module names, which side declared it, and the way out.\n// `requested` is what the user typed, so a conflict raised by a transitive prerequisite\n// says so rather than naming a module the user never mentioned.\nfunction describe(\n conflict: ModuleConflict,\n requested: string,\n action: ConflictAction\n): string {\n const { declaredBy, conflictsWith, installed } = conflict;\n\n if (!installed) {\n const first =\n declaredBy === requested\n ? declaredBy\n : `${declaredBy} (required by ${requested})`;\n return `${first} declares a conflict with ${conflictsWith}, and ${GERUND[action]} ${requested} installs both. Add only one of them.`;\n }\n\n const incoming = installed === declaredBy ? conflictsWith : declaredBy;\n const phrase =\n incoming === requested\n ? incoming\n : `${incoming} (required by ${requested})`;\n const sentence =\n installed === declaredBy\n ? `${installed} is already installed and declares a conflict with ${phrase}`\n : `${phrase} declares a conflict with ${installed}, which is already installed`;\n return `${sentence}. Run \\`saasaloy remove ${installed}\\` first.`;\n}\n\n/** The refusal `add` and `update` print. One line per conflicting pair, under a heading. */\nexport function formatConflicts(\n conflicts: ModuleConflict[],\n requested: string,\n action: ConflictAction = \"add\"\n): string {\n const heading = `Cannot ${action} ${requested} — module conflict${conflicts.length > 1 ? \"s\" : \"\"}:`;\n return [\n heading,\n ...conflicts.map((c) => ` ${describe(c, requested, action)}`),\n ].join(\"\\n\");\n}\n","import type { Plan } from \"./applier.js\";\n\n// The project's design contract (`DESIGN.md`) is derived from what `packages/ui/`\n// actually ships, so anything a module writes there can invalidate it. `add` uses\n// this to prompt for a re-derivation.\n\nconst UI_PREFIX = \"packages/ui/\";\n\n// Both halves of a plan can land in `packages/ui/`: a file the module copies in, and\n// a structural patch whose target happens to live there. Only the actions that write\n// count — a held or unchanged entry leaves the file as it was.\nexport function planWritesUi(plan: Pick<Plan, \"files\" | \"patches\">): boolean {\n const writesFile = plan.files.some(\n (file) =>\n (file.action === \"create\" || file.action === \"overwrite\") &&\n file.target.startsWith(UI_PREFIX)\n );\n if (writesFile) {\n return true;\n }\n return plan.patches.some(\n (patch) => patch.action === \"apply\" && patch.file.startsWith(UI_PREFIX)\n );\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, posix } from \"node:path\";\nimport {\n assertNoSymlinkPath,\n pathExists,\n resolveWithinRoot,\n} from \"./fs-utils.js\";\n\n// `apps/api/.dev.vars.example` — the checked-in template a developer copies to\n// `.dev.vars` before `wrangler dev`. The base's `_gitignore` has carved out an\n// exception for this exact filename since the template shipped, and until #98 nothing\n// ever wrote it: every module printed its env vars into the `add` transcript, where\n// they scrolled away. `add` now generates the file from the descriptors' own `envVars`\n// maps, so the record of what the project needs lives in the project.\n//\n// Two rules keep the regeneration safe:\n//\n// 1. A value already in the file wins. The file is checked in, so a team edits it,\n// and `saasaloy add` must never revert that edit.\n// 2. Nothing is ever removed. `saasaloy remove` owns deletions; an `add` that dropped\n// a key would silently break a second module that reads the same variable.\n\nexport const DEV_VARS_EXAMPLE = \".dev.vars.example\";\n\n/** The alias whose workspace holds the Worker that reads `.dev.vars`. */\nconst API_ALIAS = \"@api\";\n\n/**\n * The workspace root of the Worker, or undefined when this project has no api workspace\n * yet. The `@api` alias points at the Worker's *source* directory (`apps/api/src`), so\n * its parent is the workspace `wrangler dev` runs in — where `.dev.vars` and\n * `wrangler.jsonc` both live.\n *\n * Exported because `saasaloy env` routes every non-`PUBLIC_` variable to this same\n * workspace (#50): `.dev.vars` is a wrangler file, so the Worker is the only thing that\n * reads one, whichever package declared the variable.\n */\nexport function apiWorkspace(\n aliases: Record<string, string>\n): string | undefined {\n const src = aliases[API_ALIAS];\n if (!src) {\n return undefined;\n }\n const workspace = posix.dirname(src.replace(/\\/+$/, \"\"));\n return workspace === \".\" || workspace === \"/\" ? undefined : workspace;\n}\n\nconst HEADER = [\n \"# Environment variables this project's modules declare.\",\n \"# Generated by `saasaloy add`, one entry per declared variable, comments and all.\",\n \"#\",\n \"# Copy this file to `.dev.vars` and fill in the blanks: `.dev.vars` is what\",\n \"# `wrangler dev` reads, and it is gitignored. This example is not — keep it\",\n \"# checked in, and keep real secrets out of it.\",\n];\n\n/**\n * Where the file belongs, or undefined when this project has no api workspace yet.\n * The `@api` alias points at the Worker's source directory (`apps/api/src`), so its\n * parent is the workspace root that `wrangler dev` runs in.\n */\nexport function devVarsExamplePath(\n aliases: Record<string, string>\n): string | undefined {\n const workspace = apiWorkspace(aliases);\n return workspace ? posix.join(workspace, DEV_VARS_EXAMPLE) : undefined;\n}\n\n/**\n * The `KEY=value` pairs already in a `.dev.vars.example`. Comments and blank lines are\n * dropped: they are regenerated from the descriptors, which are the current truth about\n * what a variable is for. Only the values a person typed are worth carrying over.\n */\nexport function parseDevVars(text: string): Record<string, string> {\n const values: Record<string, string> = {};\n for (const line of text.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) {\n continue;\n }\n const eq = trimmed.indexOf(\"=\");\n if (eq <= 0) {\n continue;\n }\n values[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1);\n }\n return values;\n}\n\nexport interface RenderDevVarsArgs {\n /** Every variable the installed descriptors declare: name → description. */\n envVars: Record<string, string>;\n /** Local-dev values the descriptors supply for some of them. */\n devVars: Record<string, string>;\n /** Values already in the file, which always win over `devVars`. */\n existing: Record<string, string>;\n}\n\nexport function renderDevVarsExample(args: RenderDevVarsArgs): string {\n const { envVars, devVars, existing } = args;\n // Everything the file already carried stays, even when no installed module declares\n // it any more — a hand-added variable is not ours to delete.\n const names = [\n ...new Set([...Object.keys(envVars), ...Object.keys(existing)]),\n ].toSorted();\n\n const blocks = names.map((name) => {\n const value = existing[name] ?? devVars[name] ?? \"\";\n const description = envVars[name];\n const lines = description\n ? description.split(\"\\n\").map((line) => `# ${line}`)\n : [\"# Declared outside saasaloy — no module describes this one.\"];\n lines.push(`${name}=${value}`);\n return lines.join(\"\\n\");\n });\n\n return `${[HEADER.join(\"\\n\"), ...blocks].join(\"\\n\\n\")}\\n`;\n}\n\nexport interface WriteDevVarsArgs extends Omit<RenderDevVarsArgs, \"existing\"> {\n root: string;\n aliases: Record<string, string>;\n}\n\n/**\n * Write (or refresh) the example file. Returns the project-relative path written, or\n * undefined when there is no api workspace or nothing to declare — a project with no\n * environment variables gets no file rather than an empty one.\n */\nexport async function writeDevVarsExample(\n args: WriteDevVarsArgs\n): Promise<string | undefined> {\n const { root, aliases, envVars, devVars } = args;\n const target = devVarsExamplePath(aliases);\n if (!target) {\n return undefined;\n }\n const abs = resolveWithinRoot(root, target);\n // `resolveWithinRoot` proves the path is lexically inside the project; the read and the\n // write below both follow links, so a planted symlink on any component would carry them\n // outside it. Refuse before either (#98), matching `applier` and `remover`.\n await assertNoSymlinkPath(root, abs);\n // One read, reused for the parse and the compare. Reading twice would compare the\n // rendered content against a file that may have changed since the values were parsed\n // out of it, and then overwrite that change with content rendered from the stale copy.\n const before = (await pathExists(abs))\n ? await readFile(abs, \"utf-8\")\n : undefined;\n const existing = before === undefined ? {} : parseDevVars(before);\n if (Object.keys(envVars).length === 0 && Object.keys(existing).length === 0) {\n return undefined;\n }\n const content = renderDevVarsExample({ devVars, envVars, existing });\n if (before === content) {\n return undefined;\n }\n await mkdir(dirname(abs), { recursive: true });\n await writeFile(abs, content, \"utf-8\");\n return target;\n}\n","// A dependency-free line diff for `--diff`. Classic LCS backtrack: emit each line\n// prefixed with \" \" (context), \"-\" (removed) or \"+\" (added). Good enough to show a\n// human (or hand to an agent) what an update would change; we deliberately don't\n// pull in a diff library — the CLI stays lean and its deps stay auditable.\n\nexport interface DiffLine {\n kind: \"context\" | \"del\" | \"add\";\n text: string;\n}\n\nexport function lineDiff(oldText: string, newText: string): DiffLine[] {\n // An empty side is a pure creation/deletion. Special-cased because \"\".split(\"\\n\")\n // is [\"\"], which would inject a spurious blank del/add line whenever the other\n // side has no empty line (e.g. a created file without a trailing newline).\n if (oldText === \"\") {\n return newText.split(\"\\n\").map((text): DiffLine => ({ kind: \"add\", text }));\n }\n if (newText === \"\") {\n return oldText.split(\"\\n\").map((text): DiffLine => ({ kind: \"del\", text }));\n }\n const a = oldText.split(\"\\n\");\n const b = newText.split(\"\\n\");\n const m = a.length;\n const n = b.length;\n\n // dp[i][j] = length of the LCS of a[i..] and b[j..].\n const dp: number[][] = Array.from({ length: m + 1 }, () =>\n Array.from<number>({ length: n + 1 }).fill(0)\n );\n for (let i = m - 1; i >= 0; i--) {\n for (let j = n - 1; j >= 0; j--) {\n dp[i]![j] =\n a[i] === b[j]\n ? dp[i + 1]![j + 1]! + 1\n : Math.max(dp[i + 1]![j]!, dp[i]![j + 1]!);\n }\n }\n\n const out: DiffLine[] = [];\n let i = 0;\n let j = 0;\n while (i < m && j < n) {\n if (a[i] === b[j]) {\n out.push({ kind: \"context\", text: a[i]! });\n i++;\n j++;\n } else if (dp[i + 1]![j]! >= dp[i]![j + 1]!) {\n out.push({ kind: \"del\", text: a[i]! });\n i++;\n } else {\n out.push({ kind: \"add\", text: b[j]! });\n j++;\n }\n }\n while (i < m) {\n out.push({ kind: \"del\", text: a[i++]! });\n }\n while (j < n) {\n out.push({ kind: \"add\", text: b[j++]! });\n }\n return out;\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { RefusalError } from \"./exit.js\";\nimport { pathExists } from \"./fs-utils.js\";\nimport type { ModuleProvenance } from \"./registry.js\";\nimport type { Graph } from \"./resolve.js\";\nimport { validateLock } from \"./schema.js\";\n\n// `saasaloy-lock.json` records, for every installed module, the registry source it came\n// from and the exact commit SHA it resolved to — the npm-style lock to `saasaloy.json`'s\n// intent (ADR 0012). The resolved SHA is the integrity anchor: a re-install against the\n// committed lock reproduces identical bytes. Shape: schemas/saasaloy-lock.schema.json.\n\nexport const LOCK_FILE = \"saasaloy-lock.json\";\nexport const LOCKFILE_VERSION = 1;\nconst LOCK_SCHEMA_URL =\n \"https://saasaloy.dev/schemas/saasaloy-lock.schema.json\";\n\nexport interface LockModule extends ModuleProvenance {\n /** The module's declared dependencies, so the resolved graph is self-describing. */\n dependsOn?: string[];\n /** The module's declared `conflictsWith`, so `add` can refuse a conflicting module\n * that goes in second — the descriptor is gone by then, the lock is not. */\n conflictsWith?: string[];\n}\n\n/**\n * Where the base came from (#120). The base ships inside the CLI package rather than in\n * a registry, so its provenance is a CLI version rather than a commit: `templateHash` is\n * a sha256 over the shipped template's sorted `(path, hash)` pairs and is what `outdated`\n * compares, and `cliVersion` is the label printed beside it. Every build is `0.0.0` until\n * a release process exists, which is why the hash, not the version, carries the verdict.\n */\nexport interface LockBase {\n /** The base app's name — `saasaloy.json`'s `base` field, `web`. */\n name: string;\n /** The CLI that rendered or adopted the base, from `readVersion()`. */\n cliVersion: string;\n /** sha256 over the shipped template as `templateHash()` computes it. */\n templateHash: string;\n}\n\nexport interface Lockfile {\n $schema?: string;\n lockfileVersion: number;\n modules: Record<string, LockModule>;\n /** Absent on a project scaffolded before the base carried provenance; `update` adopts it. */\n base?: LockBase;\n}\n\nexport function emptyLock(): Lockfile {\n return {\n $schema: LOCK_SCHEMA_URL,\n lockfileVersion: LOCKFILE_VERSION,\n modules: {},\n };\n}\n\nexport async function loadLock(root: string): Promise<Lockfile> {\n const file = join(root, LOCK_FILE);\n if (!(await pathExists(file))) {\n return emptyLock();\n }\n // Unparseable JSON is the same invalid state as a schema miss, so it takes the same\n // exit code. Left to throw, `SyntaxError` reaches `exitCodeFor` as a plain failure (1)\n // and never names the file it came from.\n let raw: unknown;\n try {\n raw = JSON.parse(await readFile(file, \"utf-8\"));\n } catch (error) {\n throw new RefusalError(\n `${LOCK_FILE} is invalid:\\n ${error instanceof Error ? error.message : String(error)}`,\n { cause: error }\n );\n }\n // The lock is what `update` diffs against and what `add` reads a module's\n // `conflictsWith` back out of, so an entry with no `resolved` SHA or an unknown\n // `lockfileVersion` has to stop the command here (#98). Same posture as `loadConfig`.\n const result = await validateLock(raw);\n if (!result.valid) {\n throw new RefusalError(\n `${LOCK_FILE} is invalid:\\n ${result.errors.join(\"\\n \")}`\n );\n }\n const parsed = raw as Partial<Lockfile>;\n return {\n $schema: parsed.$schema ?? LOCK_SCHEMA_URL,\n lockfileVersion: parsed.lockfileVersion ?? LOCKFILE_VERSION,\n modules: parsed.modules ?? {},\n ...(parsed.base ? { base: parsed.base } : {}),\n };\n}\n\nexport async function saveLock(root: string, lock: Lockfile): Promise<void> {\n const file = join(root, LOCK_FILE);\n await writeFile(file, `${JSON.stringify(lock, null, 2)}\\n`, \"utf-8\");\n}\n\n// Record the modules that were actually applied under one source's provenance —\n// intra-repo, so they share the same source/ref/SHA. Only `installed` is written: an\n// already-installed dependency keeps the SHA it was fetched at, so the lock never\n// misstates the provenance of bytes on disk. `graph` supplies each module's declared\n// `dependsOn` and `conflictsWith` — the two lists `remove` and `add` need to read back\n// offline, once the descriptor they came from is gone.\nexport function upsertLock(\n lock: Lockfile,\n provenance: ModuleProvenance,\n installed: string[],\n graph: Graph\n): void {\n for (const name of installed) {\n const item = graph.modules.get(name)?.item;\n const dependsOn = item?.dependsOn;\n const conflictsWith = item?.conflictsWith;\n lock.modules[name] = {\n ...provenance,\n ...(dependsOn && dependsOn.length > 0 ? { dependsOn } : {}),\n ...(conflictsWith && conflictsWith.length > 0 ? { conflictsWith } : {}),\n };\n }\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { pathExists } from \"./fs-utils.js\";\n\n// Merge a module's npm deps into the consumer project's root package.json (build spec\n// §2.4 \"apply files + npm deps\"). Descriptors carry two buckets — `dependencies[]` and\n// `devDependencies[]` — each an exact-pinned `name@version` (schema-enforced). A bare\n// name still parses (falling back to \"latest\"), but descriptors no longer author them.\n//\n// Scope note: v1 adds deps at the project root. Routing each dep to the specific\n// workspace it belongs to (apps/api vs packages/db) needs the capability modules'\n// workspace conventions (issues #8/#9), which don't exist yet — root placement is\n// deterministic, reversible, and hoists across the pnpm workspace until then.\n\n/** Split `name@version` into parts, keeping the leading `@` of a scoped name intact. */\nexport function parseDep(spec: string): { name: string; version: string } {\n const at = spec.lastIndexOf(\"@\");\n if (at > 0) {\n return { name: spec.slice(0, at), version: spec.slice(at + 1) };\n }\n return { name: spec, version: \"latest\" };\n}\n\nexport interface DepChange {\n name: string;\n version: string;\n}\n\nexport interface AddDepsResult {\n /** Deps newly written into `dependencies`. */\n added: DepChange[];\n /** Deps newly written into `devDependencies`. */\n devAdded: DepChange[];\n /** Deps already present in either bucket (left untouched). */\n skipped: string[];\n /** Human-readable version disagreements — the kept version won, the other was ignored. */\n conflicts: string[];\n}\n\n/**\n * Compute which deps are new vs already declared, without touching disk. `dependencies`\n * is planned first and claims a name across both buckets: a package can't land in both,\n * and `dependencies` wins (a `devDependencies` entry for an already-claimed name is\n * dropped). Within a bucket, the first module to declare a dep wins (topological order).\n */\nexport function planDeps(\n pkg: PackageJson,\n deps: string[],\n devDeps: string[] = []\n): AddDepsResult {\n const conflicts: string[] = [];\n const skipped: string[] = [];\n // Name → chosen version across both buckets, so a later module — or the dev bucket —\n // pinning a different version is a real disagreement worth surfacing, not silently dropping.\n const seen = new Map<string, string>();\n\n function planBucket(\n specs: string[],\n existing: Record<string, string>\n ): DepChange[] {\n const out: DepChange[] = [];\n for (const spec of specs) {\n const { name, version } = parseDep(spec);\n const prior = seen.get(name);\n if (prior !== undefined) {\n if (prior !== version) {\n conflicts.push(`${name}: keeping ${prior}, ignoring ${version}`);\n }\n continue;\n }\n seen.set(name, version);\n const current = existing[name];\n if (current === undefined) {\n out.push({ name, version });\n } else {\n if (version !== \"latest\" && current !== version) {\n conflicts.push(\n `${name}: package.json already has ${current}, ignoring ${version}`\n );\n }\n skipped.push(name);\n }\n }\n return out;\n }\n\n const added = planBucket(deps, pkg.dependencies ?? {});\n const devAdded = planBucket(devDeps, pkg.devDependencies ?? {});\n return { added, conflicts, devAdded, skipped };\n}\n\nexport interface PackageJson {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n [key: string]: unknown;\n}\n\nexport async function readRootPackageJson(\n root: string\n): Promise<PackageJson | null> {\n const file = join(root, \"package.json\");\n if (!(await pathExists(file))) {\n return null;\n }\n return JSON.parse(await readFile(file, \"utf-8\")) as PackageJson;\n}\n\n/** Merge additions into an existing bucket, keeping keys sorted. */\nfunction mergeSorted(\n existing: Record<string, string> | undefined,\n added: DepChange[]\n): Record<string, string> {\n const merged = { ...existing };\n for (const { name, version } of added) {\n merged[name] = version;\n }\n const sorted: Record<string, string> = {};\n for (const key of Object.keys(merged).toSorted()) {\n sorted[key] = merged[key]!;\n }\n return sorted;\n}\n\n/** Apply the planned additions to package.json on disk, keeping each bucket sorted. */\nexport async function writeDeps(\n root: string,\n pkg: PackageJson,\n added: DepChange[],\n devAdded: DepChange[] = []\n): Promise<void> {\n if (added.length === 0 && devAdded.length === 0) {\n return;\n }\n if (added.length > 0) {\n pkg.dependencies = mergeSorted(pkg.dependencies, added);\n }\n if (devAdded.length > 0) {\n pkg.devDependencies = mergeSorted(pkg.devDependencies, devAdded);\n }\n const file = join(root, \"package.json\");\n await writeFile(file, `${JSON.stringify(pkg, null, 2)}\\n`, \"utf-8\");\n}\n","import { dirname, join, parse } from \"node:path\";\nimport { pathExists } from \"./fs-utils.js\";\n\n// Walk up from `start` looking for a Saasaloy project marker so commands work from\n// any subdirectory, like git. `package.json` is deliberately NOT a marker: in a\n// monorepo every package has one, which would stop the search at the nearest\n// package instead of the project root that owns saasaloy.json. Falls back to the\n// starting dir when nothing is found.\nconst MARKERS = [\"saasaloy.json\"];\n\nexport async function findProjectRoot(\n start: string = process.cwd()\n): Promise<string> {\n let dir = start;\n const { root } = parse(dir);\n for (;;) {\n for (const marker of MARKERS) {\n if (await pathExists(join(dir, marker))) {\n return dir;\n }\n }\n if (dir === root) {\n return start;\n }\n dir = dirname(dir);\n }\n}\n","import { mkdtemp, readFile, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { isAbsolute, join, resolve } from \"node:path\";\nimport { downloadTemplate } from \"giget\";\nimport { RefusalError } from \"./exit.js\";\nimport { pathExists, readDirNames } from \"./fs-utils.js\";\nimport type { RegistryItem } from \"./schema.js\";\nimport { validateRegistryItem } from \"./schema.js\";\n\n// A module registry is a GitHub repo whose `modules/<name>/` folders each hold one\n// module's `registry-item.json` descriptor plus the files it drops in (build spec §2.4,\n// ADR 0012 — remote-first, the repo *is* the registry). `saasaloy add` resolves a ref to\n// a commit SHA and fetches the module subtree at that SHA; the resolved SHA is written to\n// `saasaloy-lock.json` for reproducibility. A local checkout (SAASALOY_REGISTRY_DIR) is a\n// dev/offline override. This module is the single seam that knows *where* descriptors live.\n\n// Point the applier at a local `modules/` checkout instead of fetching — for developing\n// modules and for offline/CI runs against fixtures.\nexport const REGISTRY_ENV = \"SAASALOY_REGISTRY_DIR\";\n\n// Where a bare `saasaloy add <name>` resolves: the CLI's own repo is the first-party\n// registry. An explicit `owner/repo/name` coordinate overrides it.\nexport const DEFAULT_OWNER = \"mimukit\";\nexport const DEFAULT_REPO = \"saasaloy\";\n\nexport interface LoadedModule {\n /** Absolute path to a local copy of the module's folder (source of `files[].path`). */\n dir: string;\n item: RegistryItem;\n}\n\n/** Where a module was resolved from — the lockfile entry, minus `dependsOn`. */\nexport interface ModuleProvenance {\n /** `owner/repo` for a remote source, or `local` for a SAASALOY_REGISTRY_DIR checkout. */\n source: string;\n /** The ref requested (branch/tag/SHA), or `local`. */\n ref: string;\n /** The exact commit SHA resolved, or `local` — the integrity anchor. */\n resolved: string;\n}\n\n/** The seam that knows *where* descriptors live. Remote (default) or local (override). */\nexport interface RegistrySource {\n /** Human label for messages (e.g. `mimukit/saasaloy@main` or a local path). */\n readonly label: string;\n /** Read + validate one module descriptor by name; `dir` is a local copy of its folder. */\n readModule(name: string, requiredBy?: string): Promise<LoadedModule>;\n /** Names of the modules this source offers — powers the interactive picker. */\n listModules(): Promise<string[]>;\n /** Provenance for the lockfile (source + ref + resolved SHA). */\n provenance(): ModuleProvenance;\n /**\n * The commit SHA this source's ref currently points at (`local` for a local\n * checkout). `saasaloy update` compares it against the lock's `resolved` to decide\n * whether anything moved — the same resolution path `readModule` pins to, exposed.\n */\n resolveSha(): Promise<string>;\n /**\n * Subjects of the commits touching `modulePath` between two SHAs, oldest first —\n * the natural-language \"intent\" half of the merge plan (issue #48, decision 4).\n * Best-effort: returns `[]` rather than throwing when the history isn't reachable\n * (force-push, deleted tag, rate limit, local source), so a merge plan degrades to\n * diff-only instead of failing.\n */\n commitSubjects(\n modulePath: string,\n baseSha: string,\n headSha: string\n ): Promise<string[]>;\n /** Drop any temp working dirs created while fetching. No-op for a local source. */\n cleanup?(): Promise<void>;\n}\n\n// --- Module coordinate parsing ---------------------------------------------------\n\nexport interface Coordinate {\n owner?: string;\n repo?: string;\n /** Branch/tag/SHA; undefined means the repo's default branch. */\n ref?: string;\n /** The module to add; undefined means \"show a picker\". */\n module?: string;\n}\n\n// Grammar (build spec §3, ADR 0012):\n// waitlist → default repo, module `waitlist`\n// owner/repo/waitlist → third-party repo, module `waitlist`\n// owner/repo@ref/waitlist → pinned ref\n// owner/repo → no module ⇒ picker over that repo\n// (nothing) → picker over the default repo\n// A ref carrying a `/` (e.g. a `feature/x` branch) isn't addressable in v1 — pin such a\n// branch's tip SHA instead, or rely on the default branch. Pinning a ref on the *default*\n// repo (`waitlist@v2`) is likewise unsupported: a ref requires an explicit `owner/repo`.\nexport function parseCoordinate(input?: string): Coordinate {\n if (!input) {\n return {};\n }\n\n let ref: string | undefined;\n let rest = input;\n const at = input.indexOf(\"@\");\n if (at !== -1) {\n const afterAt = input.slice(at + 1);\n const slash = afterAt.indexOf(\"/\");\n ref = slash === -1 ? afterAt : afterAt.slice(0, slash);\n const beforeAt = input.slice(0, at);\n const afterRef = slash === -1 ? \"\" : afterAt.slice(slash + 1);\n rest = afterRef ? `${beforeAt}/${afterRef}` : beforeAt;\n if (!ref) {\n throw new RefusalError(\n `Malformed coordinate \"${input}\" — empty ref after \"@\".`\n );\n }\n }\n\n const segs = rest.split(\"/\").filter(Boolean);\n\n if (ref === undefined && segs.length === 1) {\n return { module: segs[0] };\n }\n if (segs.length === 2) {\n return { owner: segs[0], repo: segs[1], ref };\n }\n if (segs.length === 3) {\n return { owner: segs[0], repo: segs[1], ref, module: segs[2] };\n }\n\n throw new RefusalError(\n `Malformed coordinate \"${input}\" — expected \"name\", \"owner/repo\", or \"owner/repo[@ref]/name\".`\n );\n}\n\n/** Build the source a coordinate resolves against: local override if set, else remote. */\nexport function createRegistrySource(coord: Coordinate): RegistrySource {\n const override = process.env[REGISTRY_ENV];\n if (override) {\n const dir = isAbsolute(override)\n ? override\n : resolve(process.cwd(), override);\n return new LocalRegistrySource(dir);\n }\n return new RemoteRegistrySource(\n coord.owner ?? DEFAULT_OWNER,\n coord.repo ?? DEFAULT_REPO,\n coord.ref\n );\n}\n\n// --- Shared descriptor loading ---------------------------------------------------\n\n/** Read + validate `<dir>/registry-item.json`, asserting it declares `name`. */\nexport async function loadModuleFolder(\n dir: string,\n name: string,\n requiredBy?: string\n): Promise<LoadedModule> {\n const file = join(dir, \"registry-item.json\");\n if (!(await pathExists(file))) {\n const because = requiredBy ? ` (required by ${requiredBy})` : \"\";\n // Refused, not failed: the coordinate names something the registry does not have,\n // so a retry cannot help (#98's 0/1/2 scheme).\n throw new RefusalError(\n `Unknown module \"${name}\"${because} — no ${name}/registry-item.json in the registry.`\n );\n }\n // A truncated or hand-edited descriptor threw a bare SyntaxError that named no module\n // and exited 1. It is bad input, not a transient failure, so it refuses with the name.\n let parsed: unknown;\n try {\n parsed = JSON.parse(await readFile(file, \"utf-8\")) as unknown;\n } catch (error) {\n throw new RefusalError(\n `Module \"${name}\" has an unreadable descriptor: ${file} is not valid JSON.`,\n { cause: error }\n );\n }\n const result = await validateRegistryItem(parsed);\n if (!result.valid) {\n throw new RefusalError(\n `Module \"${name}\" has an invalid descriptor:\\n ${result.errors.join(\"\\n \")}`\n );\n }\n const item = parsed as RegistryItem;\n if (item.name !== name) {\n throw new RefusalError(\n `Module folder \"${name}\" declares name \"${item.name}\" — the folder and descriptor name must match.`\n );\n }\n return { dir, item };\n}\n\n// --- Local source (SAASALOY_REGISTRY_DIR) ---------------------------------------\n\n/** A local `modules/` directory: `<dir>/<name>/registry-item.json`. Dev/offline. */\nexport class LocalRegistrySource implements RegistrySource {\n constructor(private readonly dir: string) {}\n\n get label(): string {\n return this.dir;\n }\n\n async readModule(name: string, requiredBy?: string): Promise<LoadedModule> {\n await this.assertExists();\n return loadModuleFolder(join(this.dir, name), name, requiredBy);\n }\n\n async listModules(): Promise<string[]> {\n await this.assertExists();\n const names: string[] = [];\n for (const name of await readDirNames(this.dir)) {\n if (await pathExists(join(this.dir, name, \"registry-item.json\"))) {\n names.push(name);\n }\n }\n return names.toSorted();\n }\n\n provenance(): ModuleProvenance {\n return { ref: \"local\", resolved: \"local\", source: \"local\" };\n }\n\n // A working copy has no commit identity and no history to read — `update` treats\n // both as \"no merge base, no intent\" and degrades the merge plan accordingly.\n async resolveSha(): Promise<string> {\n return \"local\";\n }\n\n async commitSubjects(\n _modulePath: string,\n _baseSha: string,\n _headSha: string\n ): Promise<string[]> {\n return [];\n }\n\n private async assertExists(): Promise<void> {\n if (!(await pathExists(this.dir))) {\n throw new RefusalError(`${REGISTRY_ENV}=${this.dir} does not exist.`);\n }\n }\n}\n\n// --- Remote source (GitHub, default) --------------------------------------------\n\nconst GITHUB_API = \"https://api.github.com\";\n\n/**\n * Point the GitHub API calls at another base URL. The remote path is what every real user\n * hits, so it has to be testable, and the only seam that reaches it is an env var: the\n * end-to-end harness spawns the CLI as a subprocess, which no in-process injection can\n * reach. giget reads `GIGET_GITHUB_URL` for the tarball half and builds\n * `${base}/repos/<owner>/<repo>/tarball/<ref>` from it, so one fixture server can serve\n * both halves when both variables name it.\n *\n * Not a general-purpose GitHub Enterprise switch: nothing else in the CLI reads it, and\n * a real GHES base would need `GIGET_GITHUB_URL` set to match.\n */\nexport const GITHUB_API_ENV = \"SAASALOY_GITHUB_API\";\n\n// Read at call time, not at import: a test sets it after this module is loaded, and the\n// e2e subprocess inherits it from its environment.\nfunction githubApiBase(): string {\n const override = process.env[GITHUB_API_ENV];\n return override ? override.replace(/\\/$/, \"\") : GITHUB_API;\n}\n\n// Every GitHub call gets one. Without it a connection that opens and then stalls — a\n// captive portal, a half-dead proxy — hangs `saasaloy add` with no output and no way\n// out but Ctrl-C (#98). Fifteen seconds is far above a healthy round trip and far below\n// a person's patience. No retry: a failure here is reported with the offline escape\n// hatch below, which is faster than waiting through a second attempt.\nconst FETCH_TIMEOUT_MS = 15_000;\n\n// The module download gets a longer one. It is a tarball over a link the API calls never\n// touch, so 15s would fail a healthy fetch on a slow connection, and giget 3.3.0 takes\n// neither a timeout nor an AbortSignal — the race below can only stop *waiting* on the\n// download, not stop the download itself. That is enough: the files land in a temp dir\n// this process deletes, and the point is that `saasaloy add` never hangs with no output.\nconst DOWNLOAD_TIMEOUT_MS = 60_000;\n\n/**\n * Reject when `work` outruns `ms`. The work keeps running; only the wait ends. Used where\n * the underlying call exposes no cancellation of its own.\n */\nasync function withDeadline<T>(\n work: Promise<T>,\n ms: number,\n what: string\n): Promise<T> {\n // The loser of the race still settles. Without a handler of its own, a rejection\n // arriving after the deadline is an unhandled rejection, which takes the process down\n // instead of the message below.\n work.catch(() => {\n // Reported by the race, or irrelevant because the deadline already won.\n });\n let timer: ReturnType<typeof setTimeout> | undefined;\n const deadline = new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => {\n reject(new Error(`${what} did not finish in ${ms / 1000}s`));\n }, ms);\n });\n try {\n return await Promise.race([work, deadline]);\n } finally {\n clearTimeout(timer);\n }\n}\n\n/** The line every network failure ends with, so the offline path is never a secret. */\nexport const OFFLINE_HINT = `Set ${REGISTRY_ENV} to a local \\`modules/\\` checkout to work offline.`;\n\n// GITHUB_TOKEN (or GIGET_AUTH) lifts the 60→5000 req/hr limit and unlocks private repos.\n// Read at call time so tests and long-lived processes can set it after import.\nfunction authToken(): string | undefined {\n return process.env.GITHUB_TOKEN ?? process.env.GIGET_AUTH;\n}\n\ninterface TreeEntry {\n path: string;\n}\n\ninterface CommitEntry {\n sha: string;\n commit: { message: string };\n}\n\n/** First line of a commit message — this repo's Conventional Commits read as a changelog. */\nfunction subjectOf(message: string): string {\n return message.split(\"\\n\", 1)[0]?.trim() ?? \"\";\n}\n\n/** A GitHub repo fetched via giget; descriptors + files pulled at a pinned commit SHA. */\nexport class RemoteRegistrySource implements RegistrySource {\n private resolved?: { ref: string; sha: string };\n private readonly tempDirs: string[] = [];\n\n constructor(\n private readonly owner: string,\n private readonly repo: string,\n private readonly ref?: string\n ) {}\n\n get label(): string {\n return `${this.owner}/${this.repo}${this.ref ? `@${this.ref}` : \"\"}`;\n }\n\n async readModule(name: string, requiredBy?: string): Promise<LoadedModule> {\n const { sha } = await this.resolve();\n const parent = await mkdtemp(join(tmpdir(), \"saasaloy-reg-\"));\n this.tempDirs.push(parent);\n const dir = join(parent, name);\n try {\n await withDeadline(\n downloadTemplate(\n `github:${this.owner}/${this.repo}/modules/${name}#${sha}`,\n {\n auth: authToken(),\n dir,\n force: true,\n }\n ),\n DOWNLOAD_TIMEOUT_MS,\n `Download of module \"${name}\"`\n );\n } catch (error) {\n const because = requiredBy ? ` (required by ${requiredBy})` : \"\";\n const detail = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Could not fetch module \"${name}\"${because} from ${this.owner}/${this.repo} — ${detail}. ${OFFLINE_HINT}`,\n { cause: error }\n );\n }\n return loadModuleFolder(dir, name, requiredBy);\n }\n\n async listModules(): Promise<string[]> {\n const { sha } = await this.resolve();\n const tree = await this.api<{ tree?: TreeEntry[] }>(\n `/repos/${this.owner}/${this.repo}/git/trees/${sha}?recursive=1`\n );\n const names: string[] = [];\n for (const entry of tree.tree ?? []) {\n const name = /^modules\\/([a-z0-9][a-z0-9-]*)\\/registry-item\\.json$/.exec(\n entry.path\n )?.[1];\n if (name) {\n names.push(name);\n }\n }\n return names.toSorted();\n }\n\n provenance(): ModuleProvenance {\n if (!this.resolved) {\n throw new Error(\n \"provenance() called before the source resolved a commit SHA.\"\n );\n }\n return {\n ref: this.resolved.ref,\n resolved: this.resolved.sha,\n source: `${this.owner}/${this.repo}`,\n };\n }\n\n async resolveSha(): Promise<string> {\n return (await this.resolve()).sha;\n }\n\n // GitHub has no \"commits touching this path between A and B\" endpoint, so intersect\n // two queries: `compare` gives the commits between the SHAs, `commits?path=` gives\n // the ones touching the module. Both are reported best-effort — the merge plan is\n // still useful without intent, and an update must never fail over a changelog.\n async commitSubjects(\n modulePath: string,\n baseSha: string,\n headSha: string\n ): Promise<string[]> {\n if (baseSha === headSha) {\n return [];\n }\n try {\n const between = await this.api<{ commits?: CommitEntry[] }>(\n `/repos/${this.owner}/${this.repo}/compare/${baseSha}...${headSha}`\n );\n const touching = await this.api<CommitEntry[]>(\n `/repos/${this.owner}/${this.repo}/commits?sha=${headSha}&path=${encodeURIComponent(modulePath)}&per_page=100`\n );\n const touchingShas = new Set(touching.map((c) => c.sha));\n return (between.commits ?? [])\n .filter((c) => touchingShas.has(c.sha))\n .map((c) => subjectOf(c.commit.message))\n .filter((subject) => subject.length > 0);\n } catch {\n return [];\n }\n }\n\n async cleanup(): Promise<void> {\n await Promise.all(\n this.tempDirs.map((dir) => rm(dir, { force: true, recursive: true }))\n );\n this.tempDirs.length = 0;\n }\n\n // Resolve the requested ref (or the repo's default branch) to a concrete commit SHA,\n // once per source — every module in one install pins to the same SHA.\n private async resolve(): Promise<{ ref: string; sha: string }> {\n if (this.resolved) {\n return this.resolved;\n }\n const ref =\n this.ref ??\n (\n await this.api<{ default_branch: string }>(\n `/repos/${this.owner}/${this.repo}`\n )\n ).default_branch;\n const sha = (\n await this.apiText(\n `/repos/${this.owner}/${this.repo}/commits/${ref}`,\n \"application/vnd.github.sha\"\n )\n ).trim();\n this.resolved = { ref, sha };\n return this.resolved;\n }\n\n private headers(accept: string): Record<string, string> {\n const headers: Record<string, string> = {\n Accept: accept,\n \"User-Agent\": \"saasaloy-cli\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n };\n // Never attach the token to a cleartext base. The only non-HTTPS base is a\n // SAASALOY_GITHUB_API override (the test fixture server), which needs no auth; a\n // typo'd `http://` override must not leak GITHUB_TOKEN on the wire.\n const auth = authToken();\n if (auth && githubApiBase().startsWith(\"https://\")) {\n headers.Authorization = `Bearer ${auth}`;\n }\n return headers;\n }\n\n private async api<T>(path: string): Promise<T> {\n return JSON.parse(\n await this.apiText(path, \"application/vnd.github+json\")\n ) as T;\n }\n\n private async apiText(path: string, accept: string): Promise<string> {\n let res: Response;\n try {\n res = await fetch(`${githubApiBase()}${path}`, {\n headers: this.headers(accept),\n signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),\n });\n } catch (error) {\n // A timeout arrives as a TimeoutError and everything else (DNS, TLS, refused\n // connection) as a TypeError; both read the same to a user, and both are worth\n // pointing at the offline path. The original is kept as `cause` so\n // SAASALOY_DEBUG can print it.\n const timedOut = error instanceof Error && error.name === \"TimeoutError\";\n throw new Error(\n `Could not reach GitHub for ${this.owner}/${this.repo} (${path})` +\n `${timedOut ? ` — no response in ${FETCH_TIMEOUT_MS / 1000}s` : \"\"}. ${OFFLINE_HINT}`,\n { cause: error }\n );\n }\n if (!res.ok) {\n if (\n res.status === 403 &&\n res.headers.get(\"x-ratelimit-remaining\") === \"0\"\n ) {\n throw new Error(\n `GitHub API rate limit hit for ${this.owner}/${this.repo}. Set GITHUB_TOKEN to raise it. ${OFFLINE_HINT}`\n );\n }\n if (res.status === 404) {\n throw new RefusalError(\n `Not found on GitHub: ${this.owner}/${this.repo} (${path}). ${OFFLINE_HINT}`\n );\n }\n throw new Error(\n `GitHub API error ${res.status} for ${this.owner}/${this.repo} (${path}). ${OFFLINE_HINT}`\n );\n }\n return res.text();\n }\n}\n","import type { Graph } from \"./resolve.js\";\nimport type { SaasaloyConfig } from \"./schema.js\";\n\n// `requiresOneOf` enforcement for `saasaloy add` — the deterministic core, kept out of\n// commands/add.ts so it can be unit-tested, mirroring conflicts.ts.\n//\n// `dependsOn` says \"install this too\". `conflictsWith` says \"refuse this beside me\".\n// Neither can say \"pick one of these\", which is what a capability split across mutually\n// exclusive drivers needs: `database` scaffolds `packages/db` and declares the `./client`\n// export, but the file behind that export ships in `database-d1` or `database-postgres`.\n// Installing the core alone leaves a project whose `@repo/db/client` import resolves to\n// nothing, and nothing said so at `add` time (#98, ADR 0026's amendment).\n//\n// So the descriptor gains `requiresOneOf`: a list of modules, exactly one of which has to\n// be present. \"Present\" means installed already, or arriving in this same resolved graph —\n// a driver pulled in by `auth`'s `dependsOn` satisfies the core's requirement without the\n// user naming it.\n//\n// The check reports; it never resolves. `add` refuses, or offers the list as a prompt on\n// an interactive terminal.\n\nexport interface MissingRequirement {\n /** The module whose descriptor declared `requiresOneOf`. */\n declaredBy: string;\n /** The candidates, exactly one of which has to be installed. Descriptor order. */\n options: string[];\n}\n\nexport interface DetectMissingRequirementsArgs {\n /** The resolved `dependsOn` graph for this run — descriptors included. */\n graph: Graph;\n config: SaasaloyConfig;\n}\n\n/**\n * Every module in the graph whose `requiresOneOf` list nothing satisfies, in topological\n * order. Empty means the run may proceed.\n */\nexport function detectMissingRequirements(\n args: DetectMissingRequirementsArgs\n): MissingRequirement[] {\n const { graph, config } = args;\n const installed = new Set(config.installed);\n const missing: MissingRequirement[] = [];\n\n // `graph.order` rather than `graph.modules`, so a prerequisite is reported before the\n // module that dragged it in — the order the user would install them in.\n for (const name of graph.order) {\n const options = graph.modules.get(name)?.item.requiresOneOf ?? [];\n // An empty list is \"no requirement\", not \"nothing can satisfy me\". An author who\n // writes `\"requiresOneOf\": []` gets a no-op rather than a module nobody can add.\n if (options.length === 0) {\n continue;\n }\n const satisfied = options.some(\n (option) => graph.modules.has(option) || installed.has(option)\n );\n if (!satisfied) {\n missing.push({ declaredBy: name, options: [...options] });\n }\n }\n\n return missing;\n}\n\n// One unmet requirement as a sentence. `requested` is what the user typed, so a\n// requirement raised by a transitive prerequisite says so rather than naming a module the\n// user never mentioned — same shape as conflicts.ts's `describe`.\nfunction describe(missing: MissingRequirement, requested: string): string {\n const { declaredBy, options } = missing;\n const subject =\n declaredBy === requested\n ? declaredBy\n : `${declaredBy} (required by ${requested})`;\n const first = options[0];\n const suggestion = first\n ? ` Run \\`saasaloy add ${first}\\` first, or pick another from that list.`\n : \"\";\n return `${subject} needs one of: ${options.join(\", \")}, and none is installed.${suggestion}`;\n}\n\n/** The refusal `add` prints. One line per unmet requirement, under a heading. */\nexport function formatMissingRequirements(\n missing: MissingRequirement[],\n requested: string\n): string {\n const heading = `Cannot add ${requested} — unmet requirement${missing.length > 1 ? \"s\" : \"\"}:`;\n return [heading, ...missing.map((m) => ` ${describe(m, requested)}`)].join(\n \"\\n\"\n );\n}\n","import type { LoadedModule, RegistrySource } from \"./registry.js\";\n\n// Recursive `dependsOn` resolution, topologically sorted so prerequisites are applied\n// before the modules that need them (build spec §2.7). A depth-first post-order walk\n// yields that order and catches dependency cycles along the way. Dependencies resolve\n// intra-repo: a bare `dependsOn` name is a sibling in the same registry source (ADR 0012;\n// cross-repo `owner/repo#module` is a follow-up, #26).\n\nexport interface Graph {\n /** Topological order: every module appears after the modules it dependsOn; requested is last. */\n order: string[];\n /** Every descriptor touched during resolution, keyed by name. */\n modules: Map<string, LoadedModule>;\n}\n\nexport async function resolveGraph(\n source: RegistrySource,\n requested: string\n): Promise<Graph> {\n const modules = new Map<string, LoadedModule>();\n const order: string[] = [];\n const done = new Set<string>();\n const onPath = new Set<string>(); // names currently on the DFS stack — a revisit is a cycle\n const stack: string[] = [];\n\n async function visit(name: string, requiredBy?: string): Promise<void> {\n if (done.has(name)) {\n return;\n }\n if (onPath.has(name)) {\n const cycle = [...stack.slice(stack.indexOf(name)), name].join(\" → \");\n throw new Error(`Dependency cycle detected: ${cycle}.`);\n }\n onPath.add(name);\n stack.push(name);\n const mod = await source.readModule(name, requiredBy);\n modules.set(name, mod);\n for (const dep of mod.item.dependsOn ?? []) {\n await visit(dep, name);\n }\n onPath.delete(name);\n stack.pop();\n done.add(name);\n order.push(name); // post-order: prerequisites land before this module\n }\n\n await visit(requested);\n return { modules, order };\n}\n\n/**\n * Fold a second resolution into the first, returning a new graph and mutating neither.\n *\n * `add` needs this when a `requiresOneOf` prompt picks a driver after the requested\n * module's graph is already resolved: the picked module is resolved on its own and merged\n * here, rather than re-resolving everything, because a remote source re-downloads every\n * descriptor it reads (`registry.ts`'s `readModule` extracts to a temp dir per call).\n *\n * The base wins on a name both graphs carry — it is the descriptor the run already\n * planned against. `extra.order` is a post-order walk, so appending the names the base\n * lacks, in that order, keeps every prerequisite ahead of the module that needs it.\n */\nexport function mergeGraph(base: Graph, extra: Graph): Graph {\n const modules = new Map(base.modules);\n const order = [...base.order];\n const seen = new Set(order);\n for (const name of extra.order) {\n if (seen.has(name)) {\n continue;\n }\n const mod = extra.modules.get(name);\n if (!mod) {\n continue;\n }\n seen.add(name);\n modules.set(name, mod);\n order.push(name);\n }\n return { modules, order };\n}\n","// Shared helpers for clack terminal output, used by the commands' `note` boxes.\n\n/**\n * Send a command's whole clack TUI to stderr, leaving stdout free for the one thing a\n * caller might pipe. `saasaloy update` writes its merge plan to stdout, so\n * `saasaloy update email | claude` has to work with no flag (issue #48, decision 9) —\n * every clack function takes this as its options object.\n */\nexport const TUI_ON_STDERR = { output: process.stderr } as const;\n\n// pnpm (and our own picocolors output) embed SGR codes; strip them when measuring\n// or when a block needs recoloring uniformly (embedded resets cancel a wrapper color).\n// The control character is deliberate — this pattern exists to match ANSI escapes,\n// which is exactly what `no-control-regex` flags. Suppressed at this line and at the\n// copy in scripts/update-deps.ts, and nowhere else.\n// oxlint-disable-next-line no-control-regex\nconst ANSI_PATTERN = /\\u001B\\[[0-9;]*m/g;\nexport function stripAnsi(text: string): string {\n return text.replace(ANSI_PATTERN, \"\");\n}\n\n// Can this session hold a prompt? Both streams have to be a terminal: clack needs\n// raw-mode stdin to read keys and a real stdout to draw its rail, so a pipe on either\n// side means nobody can answer. Read at call time — like `wrapForNote` reads `columns`\n// below — so the answer reflects the live process rather than module-load order.\n// `process.env.CI` is deliberately absent: every genuinely non-interactive context (CI\n// runners, `docker run` without -t, a pipe, `ssh host cmd`) already has no TTY, while a\n// developer with CI exported would silently lose every prompt in the CLI.\nexport function isInteractive(): boolean {\n return process.stdin.isTTY && process.stdout.isTTY;\n}\n\n// Hard-wrap text to the terminal width so a `note` box can't overflow the rail.\n// clack's box adds a border + padding (~6 cols), so we wrap a bit narrower. Widths\n// are measured on the ANSI-stripped text so colored words wrap by their visible\n// length. Words longer than the width (URLs, hashes) are split so nothing runs off\n// the edge — except words carrying ANSI codes, which a raw slice could cut mid-escape;\n// those are left whole on their own line.\nexport function wrapForNote(text: string): string {\n const width = Math.max(24, (process.stdout.columns ?? 80) - 6);\n const out: string[] = [];\n for (const line of text.split(\"\\n\")) {\n let current = \"\";\n for (const word of line.split(\" \")) {\n let chunk = word;\n // Break a single over-long word across lines.\n while (stripAnsi(chunk).length > width && !chunk.includes(\"\\u001B\")) {\n if (current) {\n out.push(current);\n current = \"\";\n }\n out.push(chunk.slice(0, width));\n chunk = chunk.slice(width);\n }\n const candidate = current ? `${current} ${chunk}` : chunk;\n if (stripAnsi(candidate).length > width) {\n out.push(current);\n current = chunk;\n } else {\n current = candidate;\n }\n }\n out.push(current);\n }\n return out.join(\"\\n\");\n}\n","// A module that ships UI writes a **block** into the ui package's blocks folder, exactly\n// like every block the base ships, and nothing puts it on a page. There is no descriptor\n// field for that: the signal is the target itself, `@ui/blocks/<name>.tsx`, and the\n// convention is the alias prefix (ADR 0030). `add` reads it to print the wire-up pointer,\n// `remove` reads it to say what deleting the file does not undo.\n//\n// The check runs against the *resolved* target, because that is what a plan carries by\n// the time either command reports on it — so resolve the folder back through the same\n// alias map the applier used rather than hardcoding one project layout.\n\nconst UI_ALIAS = \"@ui\";\nconst BLOCKS_DIR = \"blocks\";\n\n// What `saasaloy init` writes into a fresh project's saasaloy.json. A project that has\n// dropped the alias still gets a truthful answer for the layout the base ships.\nconst DEFAULT_UI_ROOT = \"packages/ui/src\";\n\n/** The project-relative POSIX prefix every ui block target starts with. */\nexport function uiBlocksPrefix(aliases: Record<string, string>): string {\n return `${aliases[UI_ALIAS] ?? DEFAULT_UI_ROOT}/${BLOCKS_DIR}/`;\n}\n\n/**\n * The subset of `files` that land in the ui package's blocks folder. Generic over the\n * caller's own file type so `add` keeps its `module` and `remove` keeps its action.\n */\nexport function uiBlockFiles<T extends { target: string }>(\n files: readonly T[],\n aliases: Record<string, string>\n): T[] {\n const prefix = uiBlocksPrefix(aliases);\n return files.filter((file) => file.target.startsWith(prefix));\n}\n","import pc from \"picocolors\";\n\n// One shape for `saasaloy <command> --help`. Before #98 the flag reached a command's\n// argument parser, which did not know it, so `saasaloy add --help` died with \"Unknown\n// argument(s): --help\" — the one message a person asking for help cannot use. Every\n// command now answers it the same way, and every command rejects a flag it does not\n// know rather than ignoring it.\n\nexport interface CommandHelp {\n /** The command's own name, e.g. `add`. */\n name: string;\n /** The one-line description, taken from the command registry. */\n describe: string;\n /** The usage line, without the leading `saasaloy`. */\n usage: string;\n /** Flag → what it does. Rendered in declaration order. */\n flags: Record<string, string>;\n}\n\n/** The flags every command accepts, listed after its own. */\nexport const COMMON_FLAGS: Record<string, string> = {\n \"-h, --help\": \"show this help\",\n};\n\nexport function printCommandHelp(help: CommandHelp): void {\n console.log(\n `${pc.bold(`saasaloy ${help.name}`)} ${pc.dim(`— ${help.describe}`)}\\n`\n );\n console.log(`${pc.bold(\"Usage:\")} ${help.usage}\\n`);\n console.log(pc.bold(\"Flags:\"));\n const flags = { ...help.flags, ...COMMON_FLAGS };\n const width = Math.max(...Object.keys(flags).map((flag) => flag.length));\n for (const [flag, describe] of Object.entries(flags)) {\n console.log(` ${pc.cyan(flag.padEnd(width))} ${pc.dim(describe)}`);\n }\n}\n\n/** True when the user asked for a command's help rather than for the command. */\nexport function wantsHelp(argv: string[]): boolean {\n return argv.includes(\"--help\") || argv.includes(\"-h\");\n}\n","import { readFile } from \"node:fs/promises\";\nimport { fileURLToPath } from \"node:url\";\n\n// The version comes off the package's own package.json rather than a build-time\n// constant, so a bug report names the build that is actually installed. At runtime\n// import.meta.url is <pkg>/dist/index.js and under vitest it is <pkg>/src/version.ts, so\n// `../package.json` resolves to <pkg>/package.json either way. That depth is load-bearing:\n// this file cannot move under `src/lib/` without breaking the source-mode path, because\n// tsup bundles the whole CLI into the single flat `dist/index.js`.\n//\n// It lives here rather than in `cli.ts` because `commands/add.ts` needs it for the\n// `requires.saasaloy` check (#50), and `cli.ts` already imports `commands/index.ts` —\n// reading it from there would close an import cycle.\n\n/** What this returns when the CLI's own package.json is missing, unreadable, or version-less. */\nexport const UNKNOWN_VERSION = \"unknown\";\n\n/** The running CLI's version, or `\"unknown\"` when its package.json will not parse. */\nexport async function readVersion(): Promise<string> {\n try {\n const file = fileURLToPath(new URL(\"../package.json\", import.meta.url));\n const parsed = JSON.parse(await readFile(file, \"utf-8\")) as {\n version?: unknown;\n };\n return typeof parsed.version === \"string\"\n ? parsed.version\n : UNKNOWN_VERSION;\n } catch {\n // A missing or unreadable package.json is not worth failing `--version` over.\n return UNKNOWN_VERSION;\n }\n}\n","// The one-line description of each command, in the lifecycle order a user walks\n// (scaffold → compose → configure → inspect → refresh → undo → browse), with the\n// author-facing `new` and `doctor`\n// last. It lives here rather than in\n// `index.ts` because each command's own `--help` prints its description too, and\n// importing the registry from a command module would close an import cycle\n// (`index.ts` → `add.ts` → `index.ts`). `index.ts` reads this map to build the\n// registry, so the order below is still the order help and the picker render.\n\nexport const DESCRIPTIONS = {\n init: \"scaffold a new Saasaloy project (base: Astro landing + ui + config)\",\n add: \"apply a module into the current project (resolves dependsOn)\",\n env: \"fill in the environment variables the installed modules declare (--check gates a deploy)\",\n outdated:\n \"report the base template and each installed module, current vs latest (--check gates CI)\",\n update:\n \"re-apply the base template and modules at a newer version, with a merge plan for anything you edited\",\n remove: \"undo a module's applied files via the manifest (offline)\",\n list: \"list the modules a registry offers, marking the ones installed here\",\n new: \"scaffold a new module in a registry repo (descriptor + files/ + skill stub)\",\n doctor:\n \"validate local module descriptors, or a project's saasaloy.json against its manifest\",\n} as const satisfies Record<string, string>;\n","import { cancel, intro, log, note, outro } from \"@clack/prompts\";\nimport { join, relative } from \"node:path\";\nimport pc from \"picocolors\";\nimport { readBaseDeclaration } from \"../lib/base.js\";\nimport {\n checkBase,\n checkPartialInstalls,\n checkProject,\n checkTarget,\n resolveDoctorTarget,\n} from \"../lib/doctor.js\";\nimport type { BaseReport, ModuleReport } from \"../lib/doctor.js\";\nimport {\n EXIT_OK,\n EXIT_REFUSED,\n exitCodeFor,\n formatFailure,\n} from \"../lib/exit.js\";\nimport { pathExists } from \"../lib/fs-utils.js\";\nimport { loadLock } from \"../lib/lock.js\";\nimport { loadManifest } from \"../lib/manifest.js\";\nimport { CONFIG_FILE, loadConfig } from \"../lib/saasaloy-config.js\";\nimport { baseTemplateDir } from \"../lib/scaffold.js\";\nimport { wrapForNote } from \"../lib/tui.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\n\n// `saasaloy doctor [path]` — validate module descriptors before they reach a stranger's\n// machine. It checks a local folder: one module (`doctor modules/waitlist`) or a whole\n// registry (`doctor modules`). Nothing named defaults to the current directory. Validating\n// a remote coordinate — the consumer asking \"is this module safe to add?\" — is a separate\n// command and a follow-up issue.\n//\n// A path carrying a `saasaloy.json` is checked as a project instead: the rules there read\n// `installed` against `.saasaloy/manifest.json` and report a module that owns no files\n// (#107), and a Base section says whether the template is recorded and which of its\n// files drifted (#120). Drift there is information, not a problem: it never fails the\n// run, and nothing here writes.\n//\n// The rules live in `lib/doctor.ts`; this file is presentation and exit codes only. An\n// invalid descriptor is bad input, so a run with findings exits 2 (the refusal code),\n// which is what makes `doctor` usable as a pre-publish gate in CI.\n\ninterface Options {\n path?: string;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\"--help\", \"-h\"]);\nconst USAGE = \"saasaloy doctor [<path>]\";\nconst HELP: CommandHelp = {\n name: \"doctor\",\n describe: DESCRIPTIONS.doctor,\n usage: USAGE,\n flags: {},\n};\n\n/** The directory checked when nothing is named — the current directory. */\nconst DEFAULT_PATH = \".\";\n\nexport function parseArgs(argv: string[]): Options {\n const positional: string[] = [];\n const unknown: string[] = [];\n for (const arg of argv) {\n if (!arg.startsWith(\"-\")) {\n positional.push(arg);\n } else if (!KNOWN_FLAGS.has(arg)) {\n unknown.push(arg);\n }\n }\n unknown.push(...positional.slice(1));\n return { path: positional[0], unknown };\n}\n\nfunction renderReport(report: ModuleReport, root: string): string {\n const lines = report.findings.map(\n (found) => ` ${pc.yellow(found.where)} ${found.message}`\n );\n return [pc.cyan(relative(root, report.dir) || report.module), ...lines].join(\n \"\\n\"\n );\n}\n\n/** The Base box: record or `untracked`, the recorded CLI, per-file drift, and the seed list. */\nfunction renderBase(report: BaseReport): string {\n if (report.status === \"untracked\" || !report.record) {\n return [\n `${pc.yellow(\"untracked\")} — no record of which template scaffolded this project.`,\n pc.dim(\n \"Run `saasaloy update` to record the base at this CLI; nothing is applied on that run.\"\n ),\n ].join(\"\\n\");\n }\n const lines = [\n `${pc.cyan(report.record.name)} ${pc.dim(`recorded at CLI ${report.record.cliVersion} (template ${report.record.templateHash.slice(0, 7)})`)}`,\n `${pc.green(\"✔\")} ${report.matching} file${report.matching === 1 ? \"\" : \"s\"} match the record`,\n ...report.drifted.map(\n (target) =>\n `${pc.yellow(\"✎\")} ${target} ${pc.dim(\"edited since it was recorded\")}`\n ),\n ...report.missing.map(\n (target) =>\n `${pc.red(\"✗\")} ${target} ${pc.dim(\"missing — `saasaloy update` restores it\")}`\n ),\n ];\n if (report.seed.length > 0) {\n lines.push(pc.dim(`seed, not checked: ${report.seed.join(\", \")}`));\n }\n return lines.join(\"\\n\");\n}\n\n/** The project side of `doctor`: what `saasaloy.json` and the manifest say about each other. */\nasync function reportProject(path: string): Promise<number> {\n const config = await loadConfig(path);\n const manifest = await loadManifest(path);\n const lock = await loadLock(path);\n const base = await checkBase({\n root: path,\n lock,\n manifest,\n seedFiles: (await readBaseDeclaration(await baseTemplateDir())).seedFiles,\n });\n note(wrapForNote(renderBase(base)), \"Base\");\n // Two halves of the same question: a module installed that owns nothing (#107), and a\n // module that owns work but was never marked installed (#49).\n const findings = [\n ...checkProject({ config, manifest }),\n ...checkPartialInstalls({ installed: config.installed, manifest }),\n ];\n if (findings.length === 0) {\n const count = config.installed.length;\n note(\n wrapForNote(\n config.installed.map((name) => `${pc.green(\"✔\")} ${name}`).join(\"\\n\") ||\n pc.dim(\"No modules installed.\")\n ),\n `Checked ${count} installed module${count === 1 ? \"\" : \"s\"}`\n );\n outro(pc.green(\"No problems found.\"));\n return EXIT_OK;\n }\n\n note(\n wrapForNote(\n findings\n .map((found) => `${pc.yellow(found.module)} ${found.message}`)\n .join(\"\\n\")\n ),\n `${pc.yellow(\"Project state\")} ${pc.dim(`(${findings.length})`)}`\n );\n cancel(\n `${findings.length} problem${findings.length === 1 ? \"\" : \"s\"} in ${CONFIG_FILE}.`\n );\n return EXIT_REFUSED;\n}\n\nexport async function runDoctor(argv: string[]): Promise<number> {\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n intro(pc.bgCyan(pc.black(\" saasaloy doctor \")));\n\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n\n const path = opts.path ?? DEFAULT_PATH;\n try {\n if (!(await pathExists(path))) {\n cancel(\n `No such path: ${path} — point \\`doctor\\` at a module folder or at a directory of them.`\n );\n return EXIT_REFUSED;\n }\n\n // A path carrying a `saasaloy.json` is a project, not a registry, so the project\n // rules run instead of the descriptor ones (#107). The default path is the current\n // directory, so `saasaloy doctor` picks project or registry mode by what's there.\n if (await pathExists(join(path, CONFIG_FILE))) {\n return await reportProject(path);\n }\n\n const target = await resolveDoctorTarget(path);\n if (target.names.length === 0) {\n cancel(\n `No module folders in ${path} — a module folder is one that carries a registry-item.json.`\n );\n return EXIT_REFUSED;\n }\n\n const reports = await checkTarget(target);\n const bad = reports.filter((report) => report.findings.length > 0);\n const total = bad.reduce((sum, report) => sum + report.findings.length, 0);\n\n if (bad.length === 0) {\n const checked = reports.map((report) => report.module);\n note(\n wrapForNote(\n checked.map((name) => `${pc.green(\"✔\")} ${name}`).join(\"\\n\")\n ),\n `Checked ${checked.length} module${checked.length === 1 ? \"\" : \"s\"}`\n );\n outro(pc.green(\"No problems found.\"));\n return EXIT_OK;\n }\n\n for (const report of bad) {\n note(\n wrapForNote(renderReport(report, target.registryDir)),\n `${pc.yellow(report.module)} ${pc.dim(`(${report.findings.length})`)}`\n );\n }\n log.info(\n pc.dim(\n `Checked ${reports.length} module${reports.length === 1 ? \"\" : \"s\"}; ${reports.length - bad.length} clean.`\n )\n );\n cancel(\n `${total} problem${total === 1 ? \"\" : \"s\"} in ${bad.length} module${bad.length === 1 ? \"\" : \"s\"}.`\n );\n return EXIT_REFUSED;\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { mkdtemp, readdir, readFile, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join, posix } from \"node:path\";\nimport { hashContent, readIfPresent, resolveWithinRoot } from \"./fs-utils.js\";\nimport type { LockBase, Lockfile } from \"./lock.js\";\nimport { saveLock } from \"./lock.js\";\nimport type { ManagedEntry, Manifest } from \"./manifest.js\";\nimport { saveManifest } from \"./manifest.js\";\nimport {\n BASE_DECLARATION,\n baseTemplateDir,\n copyTemplate,\n renderedName,\n templateVars,\n} from \"./scaffold.js\";\nimport type { WrittenFile } from \"./scaffold.js\";\nimport type { ModuleComparison, ModuleUpdateInput } from \"./updater.js\";\n\nexport { BASE_DECLARATION } from \"./scaffold.js\";\n\n// The base template's provenance (#120, ADR 0032). `saasaloy init` copies the template\n// bundled inside the CLI package, and until this file nothing recorded what it copied:\n// no manifest entry, no lock entry, no version stamp (ADR 0022's \"one-time gift\"). So\n// `outdated`, `update` and `doctor` could say nothing about the largest body of code the\n// tool generates. The base now records like a module — one `managed` entry per rendered\n// file under the reserved module name `base` — plus a lock record keyed by CLI version\n// and template hash, because the base ships with the CLI rather than in a registry.\n//\n// Three rules keep the record honest:\n// - hashes are of *rendered* bytes: `{{PROJECT_NAME}}` is substituted at copy time, and\n// hashing the template source would mark every substituted file drifted forever;\n// - `templateHash` is over the template *as shipped*, so two CLI builds carrying an\n// identical template compare equal and a republished template is still detected;\n// - seed files (`_saasaloy-base.json`'s `seedFiles`) are recorded but never updated —\n// the owner is meant to rewrite them.\n\n/** The reserved module name base files record under. Never in `saasaloy.json`'s `installed`. */\nexport const BASE_MODULE = \"base\";\n\n/** `saasaloy.json`'s `base` value — the one base app the template ships. */\nexport const DEFAULT_BASE_NAME = \"web\";\n\n/** The lock's record of the base — re-exported under the name the plan uses. */\nexport type BaseRecord = LockBase;\n\n/** Why the base's merge plan is two-way, stamped on its section header. */\nexport const BASE_NO_MERGE_BASE =\n \"the template ships with the CLI; the previous version isn't on disk\";\n\n/** The one intent line a drifted base file carries, for the agent reading the plan. */\nexport const BASE_INTENT =\n \"the base template changed; keep local edits, take the upstream change\";\n\nexport interface BaseDeclaration {\n /** Project-relative POSIX paths the owner is meant to rewrite; recorded, never updated. */\n seedFiles: string[];\n /**\n * Globs for files the template ships once and the project owns afterwards — the blocks,\n * the components, `globals.css`, the favicon. The template's own `AGENTS.md` has always\n * said so; until this list nothing enforced it, and an update overwrote them. An owned\n * file that is absent is still created or restored. An owned file that exists goes to\n * the merge plan, never to a write. `**` matches any number of segments, `*` one.\n */\n ownedFiles: string[];\n}\n\n/**\n * A `*`/`**` glob compiled against project-relative POSIX targets. Deliberately small:\n * the declaration is ours, so the patterns are the handful this file documents rather\n * than arbitrary user input.\n */\nexport function compileGlob(pattern: string): RegExp {\n const source = pattern\n .split(\"/\")\n .map((segment) =>\n segment === \"**\"\n ? \"[^\\0]*\"\n : segment\n .replaceAll(/[.+^${}()|[\\]\\\\]/g, String.raw`\\$&`)\n .replaceAll(\"*\", \"[^/]*\")\n )\n .join(\"/\")\n .replaceAll(\"[^\\0]*/\", \"(?:[^\\0]*/)?\");\n return new RegExp(`^${source}$`);\n}\n\n/** The predicate `update` asks whether a target is the project's rather than the template's. */\nexport function ownedMatcher(\n declaration: BaseDeclaration\n): (target: string) => boolean {\n const patterns = declaration.ownedFiles.map(compileGlob);\n return (target: string) => patterns.some((re) => re.test(target));\n}\n\n/** One file the template ships, before and after the `_` → `.` rename. */\nexport interface TemplateFile {\n /** Template-relative POSIX source path — the manifest's `from`. */\n from: string;\n /** Project-relative POSIX path once rendered — the manifest key. */\n target: string;\n /** Absolute path of the source file inside the template. */\n abs: string;\n}\n\n/**\n * Read `_saasaloy-base.json` off a template. A template with none has no seed files. The\n * declaration ships inside this package, so a malformed one is a build error worth a\n * plain throw rather than a refusal aimed at the user.\n */\nexport async function readBaseDeclaration(\n templateDir: string\n): Promise<BaseDeclaration> {\n const raw = await readIfPresent(join(templateDir, BASE_DECLARATION));\n if (raw === undefined) {\n return { seedFiles: [], ownedFiles: [] };\n }\n const parsed = JSON.parse(raw) as {\n seedFiles?: unknown;\n ownedFiles?: unknown;\n };\n const seedFiles = parsed.seedFiles;\n if (\n !Array.isArray(seedFiles) ||\n !seedFiles.every((entry) => typeof entry === \"string\" && entry !== \"\")\n ) {\n throw new Error(\n `${BASE_DECLARATION} must declare \"seedFiles\" as a list of project-relative paths.`\n );\n }\n const ownedFiles = parsed.ownedFiles ?? [];\n if (\n !Array.isArray(ownedFiles) ||\n !ownedFiles.every((entry) => typeof entry === \"string\" && entry !== \"\")\n ) {\n throw new Error(\n `${BASE_DECLARATION} must declare \"ownedFiles\" as a list of project-relative globs.`\n );\n }\n return {\n seedFiles: seedFiles as string[],\n ownedFiles: ownedFiles as string[],\n };\n}\n\n/** Every file the template ships, sorted by target, the declaration excluded. */\nexport async function listTemplateFiles(\n templateDir: string\n): Promise<TemplateFile[]> {\n const out: TemplateFile[] = [];\n const walk = async (\n dir: string,\n fromPrefix: string,\n targetPrefix: string,\n isRoot: boolean\n ): Promise<void> => {\n for (const entry of await readdir(dir, { withFileTypes: true })) {\n if (isRoot && entry.name === BASE_DECLARATION) {\n continue;\n }\n const from = posix.join(fromPrefix, entry.name);\n const target = posix.join(targetPrefix, renderedName(entry.name));\n const abs = join(dir, entry.name);\n if (entry.isDirectory()) {\n await walk(abs, from, target, false);\n } else if (entry.isFile()) {\n out.push({ from, target, abs });\n }\n }\n };\n await walk(templateDir, \"\", \"\", true);\n return out.toSorted((a, b) => (a.target < b.target ? -1 : 1));\n}\n\n// Computed on demand and cached per process: the template is about fifty files and the\n// walk is milliseconds, and a runtime hash cannot drift from the files the way a\n// build-time constant could.\nconst hashCache = new Map<string, Promise<string>>();\n\n/**\n * sha256 over the sorted `(target, sha256 of source bytes)` pairs of the template as\n * shipped. Source bytes, not rendered: this identifies the template, and a project name\n * must not change it.\n */\nexport function templateHash(templateDir: string): Promise<string> {\n let cached = hashCache.get(templateDir);\n if (!cached) {\n cached = (async () => {\n const digest = createHash(\"sha256\");\n for (const file of await listTemplateFiles(templateDir)) {\n digest.update(file.target);\n digest.update(\"\\0\");\n digest.update(hashContent(await readFile(file.abs, \"utf-8\")));\n digest.update(\"\\n\");\n }\n return digest.digest(\"hex\");\n })();\n hashCache.set(templateDir, cached);\n }\n return cached;\n}\n\n/** The lock's `base` record for a template at `templateHash`, rendered by `cliVersion`. */\nexport function baseRecord(\n cliVersion: string,\n hash: string,\n name = DEFAULT_BASE_NAME\n): BaseRecord {\n return { name, cliVersion, templateHash: hash };\n}\n\n/** The manifest entries the base owns, keyed by target. */\nexport function baseEntries(manifest: Manifest): Record<string, ManagedEntry> {\n const out: Record<string, ManagedEntry> = {};\n for (const [target, entry] of Object.entries(manifest.managed)) {\n if (entry.module === BASE_MODULE) {\n out[target] = entry;\n }\n }\n return out;\n}\n\n/** What `recordBaseFiles` needs of a file: the three fields `copyTemplate` returns, plus provenance. */\nexport type BaseFileRecord = Pick<WrittenFile, \"target\" | \"from\" | \"hash\"> & {\n /** The hash came off disk, not out of the render — see `ManagedEntry.adopted`. */\n adopted?: boolean;\n};\n\n/**\n * Replace the base's manifest entries with `files`. Entries other modules own are left\n * alone; a base entry `files` no longer names is dropped, so the record is always the\n * template that was last rendered or adopted.\n */\nexport function recordBaseFiles(\n manifest: Manifest,\n files: readonly BaseFileRecord[]\n): void {\n for (const [target, entry] of Object.entries(manifest.managed)) {\n if (entry.module === BASE_MODULE) {\n delete manifest.managed[target];\n }\n }\n for (const file of files) {\n manifest.managed[file.target] = {\n module: BASE_MODULE,\n hash: file.hash,\n ...(file.adopted ? { adopted: true } : {}),\n from: file.from,\n };\n }\n}\n\n/**\n * Whether the project carries a usable base record: the lock's `base` *and* at least one\n * manifest entry for it. A lock record with no manifest entries has nothing to classify\n * against, so it counts as untracked and `update` re-adopts.\n */\nexport function isBaseTracked(lock: Lockfile, manifest: Manifest): boolean {\n return (\n lock.base !== undefined && Object.keys(baseEntries(manifest)).length > 0\n );\n}\n\n/**\n * Base targets the manifest tracks that are not on disk — the files `update` classifies as\n * `restore`. `compareBase` needs this because `templateHash` only says whether the *template*\n * moved: a file deleted from disk (or recorded absent by `adoptBase`) leaves the hash equal,\n * so without this the base reads `current` and the restore never runs (#120).\n *\n * Seed files are excluded. They are recorded but never updated, so a missing one produces no\n * plan action and would leave the base permanently `outdated` with nothing to apply.\n */\nexport async function missingBaseTargets(\n root: string,\n manifest: Manifest,\n templateDir: string\n): Promise<string[]> {\n const seed = new Set((await readBaseDeclaration(templateDir)).seedFiles);\n const missing: string[] = [];\n for (const target of Object.keys(baseEntries(manifest))) {\n if (seed.has(target)) {\n continue;\n }\n if ((await readIfPresent(resolveWithinRoot(root, target))) === undefined) {\n missing.push(target);\n }\n }\n return missing.toSorted((a, b) => (a < b ? -1 : 1));\n}\n\n/** The template rendered with a project's own vars, in a temp dir the caller removes. */\nexport interface RenderedTemplate {\n dir: string;\n files: WrittenFile[];\n cleanup: () => Promise<void>;\n}\n\n/**\n * Render the bundled template for `root` into a temp dir, source names kept, so each\n * file's `from` resolves inside the render and its `hash` is of the bytes `init` would\n * have written for this project.\n *\n * `projectName` is the project's own name, read from `saasaloy.json` — never\n * `basename(root)`. The directory is the wrong source: a git worktree, a renamed folder\n * or a CI checkout path all differ from the name `init` rendered with, and substituting\n * one of those rewrites `package.json` `name`, `wrangler.jsonc` `name` and `siteName` to\n * a name the project never chose.\n */\nexport async function renderTemplate(\n root: string,\n templateDir: string,\n projectName: string\n): Promise<RenderedTemplate> {\n const dir = await mkdtemp(join(tmpdir(), \"saasaloy-base-render-\"));\n const files = await copyTemplate(\n templateDir,\n dir,\n templateVars(projectName),\n {\n keepNames: true,\n }\n );\n return {\n dir,\n files,\n cleanup: () => rm(dir, { recursive: true, force: true }),\n };\n}\n\nexport interface AdoptBaseArgs {\n root: string;\n /** The running CLI's version — the record says the base was adopted here. */\n cliVersion: string;\n /** The project's own name, from `saasaloy.json` — never the directory's. */\n projectName: string;\n /** Loaded state, mutated in place and saved unless `dryRun`. */\n manifest: Manifest;\n lock: Lockfile;\n /** Report only: neither state file is written. */\n dryRun?: boolean;\n /** Defaults to the bundled template; tests point it at a fixture. */\n templateDir?: string;\n}\n\nexport interface AdoptBaseResult {\n /** Targets recorded at the hash they have on disk. */\n adopted: string[];\n /** Template files not on disk, recorded at their rendered hash so the next update restores them. */\n absent: string[];\n record: BaseRecord;\n}\n\n/**\n * Give an unrecorded project a base record at the running CLI (#120). Every file today's\n * template ships is recorded at the hash it has on disk — edits and all — so the next\n * `update` classifies against what is really there and never manufactures a wall of\n * conflicts on a customised project. A file the template no longer ships is not recorded;\n * a template file missing from disk is recorded at its rendered hash, which `update`\n * reads as \"tracked but missing\" and restores. Adoption records the present, not the\n * past: it skips at most one generation of base changes.\n *\n * A file recorded off disk is flagged `adopted`, and that flag is the whole safety of\n * this step. Without it the next `update` reads the recorded hash as proof the file is\n * untouched template output and overwrites every hand edit in the project — the failure\n * this flag exists to end. An adopted file goes to the merge plan instead.\n */\nexport async function adoptBase(args: AdoptBaseArgs): Promise<AdoptBaseResult> {\n const {\n root,\n cliVersion,\n projectName,\n manifest,\n lock,\n dryRun = false,\n } = args;\n const templateDir = args.templateDir ?? (await baseTemplateDir());\n const render = await renderTemplate(root, templateDir, projectName);\n try {\n const files: BaseFileRecord[] = [];\n const adopted: string[] = [];\n const absent: string[] = [];\n for (const file of render.files) {\n const mine = await readIfPresent(resolveWithinRoot(root, file.target));\n if (mine === undefined) {\n absent.push(file.target);\n files.push({ target: file.target, from: file.from, hash: file.hash });\n } else {\n adopted.push(file.target);\n files.push({\n target: file.target,\n from: file.from,\n hash: hashContent(mine),\n adopted: true,\n });\n }\n }\n const record = baseRecord(\n cliVersion,\n await templateHash(templateDir),\n lock.base?.name ?? DEFAULT_BASE_NAME\n );\n if (!dryRun) {\n recordBaseFiles(manifest, files);\n lock.base = record;\n await saveManifest(root, manifest);\n await saveLock(root, lock);\n }\n return { adopted, absent, record };\n } finally {\n await render.cleanup();\n }\n}\n\nexport interface BaseUpdateArgs {\n root: string;\n manifest: Manifest;\n lock: Lockfile;\n /** The template the running CLI ships — `baseTemplateDir()`, or a fixture. */\n templateDir: string;\n /** The running CLI's version, written into the record a clean run moves to. */\n cliVersion: string;\n /** The project's own name, from `saasaloy.json` — never the directory's. */\n projectName: string;\n /** The `outdated` row from `compareBase`, carried into the plan for the summary. */\n comparison: ModuleComparison;\n}\n\nexport interface BaseUpdateHandle {\n input: ModuleUpdateInput;\n /** Remove the rendered template; call once the plan has been built and executed. */\n cleanup: () => Promise<void>;\n}\n\n/**\n * The base as a `ModuleUpdateInput` the existing classifier can run (#120). The bundled\n * template is rendered for this project into a temp dir and described as a one-scaffold\n * descriptor rooted at the project, so `listModuleFiles` places each file where `init`\n * put it. There is no `base` revision — the old template lives inside the old CLI, which\n * is not on disk — so the plan takes the `noMergeBase` path modules already have. Seed\n * files are left out of the descriptor and named in `ignoreTargets`, and every recorded\n * patch against a shipped file is queued for re-apply.\n *\n * No `dependencies` are declared: the root `package.json` is itself a managed base file,\n * so a pin bump rides its overwrite or its merge plan rather than a second write path.\n */\nexport async function baseUpdateInput(\n args: BaseUpdateArgs\n): Promise<BaseUpdateHandle> {\n const {\n root,\n manifest,\n lock,\n templateDir,\n cliVersion,\n projectName,\n comparison,\n } = args;\n const render = await renderTemplate(root, templateDir, projectName);\n const declaration = await readBaseDeclaration(templateDir);\n const seed = new Set(declaration.seedFiles);\n const shipped = render.files.filter((file) => !seed.has(file.target));\n const targets = new Set(shipped.map((file) => file.target));\n return {\n input: {\n comparison,\n theirs: {\n dir: render.dir,\n item: {\n name: BASE_MODULE,\n type: \"saasaloy:capability\",\n scaffolds: [\n {\n workspace: \".\",\n files: shipped.map((file) => ({\n path: file.from,\n target: file.target,\n })),\n },\n ],\n },\n },\n noMergeBase: BASE_NO_MERGE_BASE,\n intent: [],\n ignoreTargets: seed,\n isOwned: ownedMatcher(declaration),\n reapplyPatches: manifest.patches.filter((patch) =>\n targets.has(patch.file)\n ),\n baseRecord: baseRecord(\n cliVersion,\n await templateHash(templateDir),\n lock.base?.name ?? DEFAULT_BASE_NAME\n ),\n },\n cleanup: render.cleanup,\n };\n}\n","import { mkdir, readdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join, posix } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport cliPackage from \"../../package.json\" with { type: \"json\" };\nimport { hashContent, pathExists } from \"./fs-utils.js\";\n\n// Copy a template tree into a target dir, applying two conventions:\n// - files named `_foo` become `.foo` (npm refuses to publish literal dotfiles\n// like `.gitignore` inside a package, so templates store them de-dotted)\n// - `{{VAR}}` tokens in file contents are replaced from `vars`\n// All template files are UTF-8 text, so every file gets token substitution.\n\nexport type TemplateVars = Record<string, string>;\n\n/**\n * The base template's own declaration (#120): which of its files are seed — meant to be\n * rewritten by the owner, so never updated — as an explicit path list. It describes the\n * template and is not part of it, so `copyTemplate` skips it by this exact name, before\n * the `_` → `.` rename would turn it into a dotfile in someone's project.\n */\nexport const BASE_DECLARATION = \"_saasaloy-base.json\";\n\n// The base template is bundled at <pkg>/templates/base. At runtime import.meta.url is\n// <pkg>/dist/index.js, so `../templates` resolves; under vitest it is <pkg>/src/lib/\n// scaffold.ts, so the template sits one level further up. Try both, the way\n// `lib/schema.ts` finds its schemas — without it `init` is only reachable from a build,\n// which is what kept it untested (#47).\nconst TEMPLATE_DIR_CANDIDATES = [\"../templates/base\", \"../../templates/base\"];\n\n/** Absolute path of the bundled base template. `init` copies it; `doctor` reads its aliases. */\nexport async function baseTemplateDir(): Promise<string> {\n for (const candidate of TEMPLATE_DIR_CANDIDATES) {\n const dir = fileURLToPath(new URL(candidate, import.meta.url));\n if (await pathExists(join(dir, \"package.json\"))) {\n return dir;\n }\n }\n // Fall back to the packaged location, for a sensible ENOENT naming the real path.\n return fileURLToPath(new URL(TEMPLATE_DIR_CANDIDATES[0]!, import.meta.url));\n}\n\n// The substitutions the base template expects. CLI_VERSION stamps the DESIGN.md\n// seed, so a generated contract records which CLI wrote it.\nexport function templateVars(projectName: string): TemplateVars {\n return { PROJECT_NAME: projectName, CLI_VERSION: cliPackage.version };\n}\n\n/** The `_name` → `.name` rename, applied to one path segment. */\nexport function renderedName(name: string): string {\n return name.startsWith(\"_\") ? `.${name.slice(1)}` : name;\n}\n\n/** One file `copyTemplate` wrote, described the way the manifest records it (#120). */\nexport interface WrittenFile {\n /** Absolute path of the file as written. */\n path: string;\n /** Project-relative POSIX path after the rename — the manifest key. */\n target: string;\n /** Template-relative POSIX path before the rename — the manifest's `from`. */\n from: string;\n /** sha256 of the rendered bytes, so hashing the template source is never mistaken for it. */\n hash: string;\n}\n\nexport interface CopyTemplateOptions {\n /**\n * Write under the source names (`_gitignore` stays `_gitignore`). `update` renders the\n * template this way so each file's `from` resolves inside the render, while `target`\n * still says where it lands in the project.\n */\n keepNames?: boolean;\n}\n\nexport async function copyTemplate(\n srcDir: string,\n destDir: string,\n vars: TemplateVars,\n options: CopyTemplateOptions = {}\n): Promise<WrittenFile[]> {\n return copyTree(srcDir, destDir, vars, options, \"\", \"\", true);\n}\n\nasync function copyTree(\n srcDir: string,\n destDir: string,\n vars: TemplateVars,\n options: CopyTemplateOptions,\n fromPrefix: string,\n targetPrefix: string,\n isRoot: boolean\n): Promise<WrittenFile[]> {\n const written: WrittenFile[] = [];\n await mkdir(destDir, { recursive: true });\n const entries = await readdir(srcDir, { withFileTypes: true });\n for (const entry of entries) {\n // The declaration describes the template; it is never part of a project.\n if (isRoot && entry.name === BASE_DECLARATION) {\n continue;\n }\n const srcPath = join(srcDir, entry.name);\n const outName = renderedName(entry.name);\n const destPath = join(destDir, options.keepNames ? entry.name : outName);\n const from = posix.join(fromPrefix, entry.name);\n const target = posix.join(targetPrefix, outName);\n if (entry.isDirectory()) {\n written.push(\n ...(await copyTree(\n srcPath,\n destPath,\n vars,\n options,\n from,\n target,\n false\n ))\n );\n } else if (entry.isFile()) {\n const raw = await readFile(srcPath, \"utf-8\");\n const rendered = applyVars(raw, vars);\n await writeFile(destPath, rendered, \"utf-8\");\n written.push({\n path: destPath,\n target,\n from,\n hash: hashContent(rendered),\n });\n }\n }\n return written;\n}\n\nfunction applyVars(content: string, vars: TemplateVars): string {\n return content.replaceAll(\n /\\{\\{(\\w+)\\}\\}/g,\n (match, key: string) => vars[key] ?? match\n );\n}\n","{\n \"name\": \"saasaloy\",\n \"version\": \"0.2.0\",\n \"description\": \"Composable SaaS accelerator kit.\",\n \"keywords\": [\n \"saas\",\n \"scaffold\",\n \"starter\",\n \"cli\",\n \"astro\",\n \"cloudflare-workers\",\n \"drizzle\",\n \"generator\"\n ],\n \"homepage\": \"https://github.com/mimukit/saasaloy#readme\",\n \"bugs\": {\n \"url\": \"https://github.com/mimukit/saasaloy/issues\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/mimukit/saasaloy.git\",\n \"directory\": \"packages/cli\"\n },\n \"license\": \"MIT\",\n \"type\": \"module\",\n \"bin\": {\n \"saasaloy\": \"./dist/index.js\"\n },\n \"files\": [\n \"dist\",\n \"templates\",\n \"schemas/*.schema.json\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"engines\": {\n \"node\": \">=24.13.0\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"prepack\": \"pnpm -w run readme:cli && pnpm run build\",\n \"dev\": \"tsup --watch\",\n \"start\": \"node ./dist/index.js\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run --coverage\",\n \"test:e2e\": \"vitest run --config vitest.integration.config.ts test/e2e\",\n \"test:matrix\": \"vitest run --config vitest.integration.config.ts test/matrix\",\n \"release\": \"release-it\",\n \"release:dry\": \"release-it --dry-run\"\n },\n \"devDependencies\": {\n \"@release-it/conventional-changelog\": \"12.0.0\",\n \"@types/node\": \"26.1.1\",\n \"@vitest/coverage-v8\": \"4.1.10\",\n \"release-it\": \"21.0.2\",\n \"tsup\": \"8.5.1\",\n \"vitest\": \"4.1.10\"\n },\n \"dependencies\": {\n \"@clack/prompts\": \"1.7.0\",\n \"ajv\": \"8.17.1\",\n \"diff\": \"9.0.0\",\n \"giget\": \"3.3.0\",\n \"jsonc-parser\": \"3.3.1\",\n \"magicast\": \"0.5.3\",\n \"picocolors\": \"1.1.1\"\n }\n}\n","import { readFile } from \"node:fs/promises\";\nimport { basename, dirname, join, posix, resolve } from \"node:path\";\nimport { BASE_MODULE, baseEntries, isBaseTracked } from \"./base.js\";\nimport {\n hashContent,\n joinModulePath,\n pathExists,\n readDirNames,\n readIfPresent,\n resolveWithinRoot,\n} from \"./fs-utils.js\";\nimport type { LockBase, Lockfile } from \"./lock.js\";\nimport type { Manifest } from \"./manifest.js\";\nimport { loadConfig } from \"./saasaloy-config.js\";\nimport { baseTemplateDir } from \"./scaffold.js\";\nimport { isValidRange } from \"./semver.js\";\nimport { validateRegistryItem } from \"./schema.js\";\nimport type { SaasaloyConfig } from \"./schema.js\";\n\n// The checks behind `saasaloy doctor`. Split from the command so the rules are testable\n// without a terminal, and so a future `doctor owner/repo/name` (the consumer path, a\n// follow-up) can reuse them against a downloaded folder.\n//\n// Everything here is local: a module folder, its siblings in the same registry, and the\n// alias map the base template establishes. Nothing is fetched, so an author can run it\n// before publishing rather than finding out on a stranger's machine.\n//\n// `checkProject` at the foot of the file reads the other side — a consumer project's own\n// state files — and is the one check here that says nothing about a descriptor.\n\nexport interface Finding {\n /** The module folder the finding belongs to. */\n module: string;\n /**\n * Where inside the descriptor, in the shape ajv prints (`/files/0/target`). A project\n * check points into the project's own state instead, e.g. `/installed/waitlist`.\n */\n where: string;\n message: string;\n}\n\nexport interface ModuleReport {\n module: string;\n /** Absolute path of the module folder. */\n dir: string;\n findings: Finding[];\n}\n\n/** The aliases a descriptor's `files[].target` may name. */\nexport interface AliasSources {\n /** `@web`, `@ui` — established by `saasaloy init`, before any module applies. */\n base: Record<string, string>;\n /** Every alias a scaffold in this registry registers, e.g. `@api` from the api module. */\n fromScaffolds: Record<string, string>;\n}\n\n// Every skill folder a module ships is prefixed, so two registries' skills can sit in one\n// `.claude/skills/` without colliding (ADR 0014).\nconst SKILL_PREFIX = \"saasaloy-\";\n\n// `dependencies` and `devDependencies` are exact-pinned `name@version` (ADR 0017). The\n// schema enforces the same shape; this repeats it so the report says what is wrong with\n// the entry rather than only that a pattern did not match.\nconst PINNED_DEP = /^(@[^/@]+\\/)?[^/@]+@\\d+\\.\\d+\\.\\d+(-[\\w.-]+)?(\\+[\\w.-]+)?$/;\n\nfunction finding(module: string, where: string, message: string): Finding {\n return { message, module, where };\n}\n\n// The descriptor is parsed before the schema has vouched for it — that is the point of\n// doctor — so every field read below goes through one of these instead of a cast the\n// JSON can violate. A wrong-shaped field reads as absent here and the schema pass names\n// it; iterating `\"scaffolds\": {}` directly would throw and swallow the whole report.\nfunction asArray(value: unknown): unknown[] {\n return Array.isArray(value) ? value : [];\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\n/**\n * Names of the module folders under `dir` — a folder counts when it carries a descriptor,\n * the same rule `LocalRegistrySource.listModules` uses.\n */\nexport async function registryModuleNames(dir: string): Promise<string[]> {\n const names: string[] = [];\n for (const name of await readDirNames(dir)) {\n if (await pathExists(join(dir, name, \"registry-item.json\"))) {\n names.push(name);\n }\n }\n return names.toSorted();\n}\n\n/**\n * The alias map a descriptor in `registryDir` may target. The base template's own\n * `saasaloy.json` supplies the aliases every project starts with; each capability's\n * `scaffolds[].aliases` supplies the ones a project gains by installing it.\n */\nexport async function collectAliases(\n registryDir: string,\n names: string[]\n): Promise<AliasSources> {\n const templateDir = await baseTemplateDir();\n let base: Record<string, string> = {};\n if (await pathExists(join(templateDir, \"saasaloy.json\"))) {\n base = (await loadConfig(templateDir)).aliases;\n }\n\n const fromScaffolds: Record<string, string> = {};\n for (const name of names) {\n const item = asRecord(await readDescriptor(join(registryDir, name)));\n for (const scaffold of asArray(item.scaffolds)) {\n const aliases = asRecord(asRecord(scaffold).aliases);\n for (const [alias, prefix] of Object.entries(aliases)) {\n if (typeof prefix === \"string\") {\n fromScaffolds[alias] = prefix;\n }\n }\n }\n }\n return { base, fromScaffolds };\n}\n\n/** Parse a module folder's descriptor, or `undefined` when it is missing or unreadable. */\nasync function readDescriptor(dir: string): Promise<unknown> {\n const file = join(dir, \"registry-item.json\");\n if (!(await pathExists(file))) {\n return undefined;\n }\n try {\n return JSON.parse(await readFile(file, \"utf-8\")) as unknown;\n } catch {\n return undefined;\n }\n}\n\nexport interface CheckModuleInput {\n /** Absolute path of the module folder. */\n dir: string;\n /** Every module name the surrounding registry offers, for `dependsOn` resolution. */\n siblings: string[];\n aliases: AliasSources;\n}\n\n/**\n * Every violation one module folder carries. Schema errors and structural errors are\n * reported together rather than the schema pass short-circuiting: an author fixing a\n * descriptor wants the whole list, not the first line of it.\n */\nexport async function checkModule(\n input: CheckModuleInput\n): Promise<ModuleReport> {\n const { aliases, dir, siblings } = input;\n const module = basename(dir);\n const findings: Finding[] = [];\n const report = (): ModuleReport => ({ dir, findings, module });\n\n const file = join(dir, \"registry-item.json\");\n if (!(await pathExists(file))) {\n findings.push(\n finding(module, \"(root)\", `no registry-item.json in ${module}/`)\n );\n return report();\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(await readFile(file, \"utf-8\")) as unknown;\n } catch (error) {\n findings.push(\n finding(\n module,\n \"(root)\",\n `registry-item.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n return report();\n }\n\n const schema = await validateRegistryItem(parsed);\n const schemaFindings = schema.errors.map((error) => {\n const [where, ...rest] = error.split(\": \");\n return finding(module, where ?? \"(root)\", rest.join(\": \"));\n });\n\n // The structural checks below read the parsed object even when the schema rejected it.\n // A descriptor usually fails on one property, and reporting only that hides the\n // missing file or the unknown alias the author would fix in the same pass.\n const item = asRecord(parsed);\n\n if (typeof item.name === \"string\" && item.name !== module) {\n findings.push(\n finding(\n module,\n \"/name\",\n `declares name \"${item.name}\" — the folder and the descriptor name must match`\n )\n );\n }\n\n const known = { ...aliases.base, ...aliases.fromScaffolds };\n const knownList = Object.keys(known).toSorted().join(\", \") || \"(none)\";\n for (const [index, entry] of asArray(item.files).entries()) {\n await checkSourcePath(findings, module, dir, `/files/${index}/path`, entry);\n const target = asRecord(entry).target;\n const alias = typeof target === \"string\" ? target.split(\"/\")[0] : undefined;\n if (alias && !(alias in known)) {\n findings.push(\n finding(\n module,\n `/files/${index}/target`,\n `unknown alias \"${alias}\" in \"${String(target)}\" — known aliases: ${knownList}`\n )\n );\n }\n }\n\n for (const [index, scaffold] of asArray(item.scaffolds).entries()) {\n for (const [fileIndex, entry] of asArray(\n asRecord(scaffold).files\n ).entries()) {\n await checkSourcePath(\n findings,\n module,\n dir,\n `/scaffolds/${index}/files/${fileIndex}/path`,\n entry\n );\n }\n }\n\n const siblingSet = new Set(siblings);\n for (const [field, names] of [\n [\"dependsOn\", item.dependsOn],\n [\"requiresOneOf\", item.requiresOneOf],\n [\"conflictsWith\", item.conflictsWith],\n ] as const) {\n for (const [index, name] of asArray(names).entries()) {\n if (typeof name === \"string\" && !siblingSet.has(name)) {\n findings.push(\n finding(\n module,\n `/${field}/${index}`,\n `names \"${name}\", which this registry does not offer`\n )\n );\n }\n }\n }\n\n for (const [field, deps] of [\n [\"dependencies\", item.dependencies],\n [\"devDependencies\", item.devDependencies],\n ] as const) {\n for (const [index, dep] of asArray(deps).entries()) {\n if (typeof dep === \"string\" && !PINNED_DEP.test(dep)) {\n findings.push(\n finding(\n module,\n `/${field}/${index}`,\n `\"${dep}\" is not exact-pinned — write it as name@1.2.3 (ADR 0017; \\`pnpm deps:update\\` fills the version)`\n )\n );\n }\n }\n }\n\n // `requires.saasaloy` is checked structurally only: is it a string, and is it a range\n // this CLI can evaluate (#50). It is deliberately *not* compared against the running\n // version — doctor is the author's tool, and a registry author's CLI is not the\n // consumer's. An author on 0.9 writing `>=2` for a field landing next quarter is right,\n // and a doctor that failed them for it would teach them to stop declaring the field.\n const requires = asRecord(item.requires).saasaloy;\n if (requires !== undefined) {\n if (typeof requires !== \"string\") {\n findings.push(\n finding(\n module,\n \"/requires/saasaloy\",\n `must be a semver range string, not ${Array.isArray(requires) ? \"an array\" : typeof requires}`\n )\n );\n } else if (!isValidRange(requires)) {\n findings.push(\n finding(\n module,\n \"/requires/saasaloy\",\n `\"${requires}\" isn't a semver range — write one like \">=0.3\", \">=0.3 <2\", \"^1.2.0\" or \"1.x\"`\n )\n );\n }\n }\n\n // Every `devVars` key is a local value for an env var the module declares. A key with\n // no `envVars` entry is written to nothing, which the schema documents and cannot check.\n for (const key of Object.keys(asRecord(item.devVars))) {\n if (!(key in asRecord(item.envVars))) {\n findings.push(\n finding(\n module,\n `/devVars/${key}`,\n `has no matching entry in envVars, so nothing describes it`\n )\n );\n }\n }\n\n for (const [index, skill] of asArray(asRecord(item.agent).skills).entries()) {\n if (typeof skill !== \"string\") {\n continue;\n }\n const folder = posix.basename(skill);\n if (!folder.startsWith(SKILL_PREFIX)) {\n findings.push(\n finding(\n module,\n `/agent/skills/${index}`,\n `skill folder \"${folder}\" must start with \"${SKILL_PREFIX}\" (ADR 0014)`\n )\n );\n }\n const missing = await missingModulePath(dir, skill);\n if (missing) {\n findings.push(\n finding(\n module,\n `/agent/skills/${index}`,\n missing === \"escape\"\n ? `\"${skill}\" escapes the module folder`\n : `no such folder: ${skill}`\n )\n );\n }\n }\n\n // Schema errors come first, minus any the checks above already explained better at the\n // same path. An unpinned dependency fails a schema pattern *and* a rule here; printing\n // both leaves the author reading a raw regex next to the sentence that answers it.\n const explained = new Set(findings.map((found) => found.where));\n findings.unshift(\n ...schemaFindings.filter((found) => !explained.has(found.where))\n );\n\n return report();\n}\n\nasync function checkSourcePath(\n findings: Finding[],\n module: string,\n dir: string,\n where: string,\n entry: unknown\n): Promise<void> {\n const path = asRecord(entry).path;\n if (typeof path !== \"string\" || path === \"\") {\n return;\n }\n const missing = await missingModulePath(dir, path);\n if (missing) {\n findings.push(\n finding(\n module,\n where,\n missing === \"escape\"\n ? `\"${path}\" escapes the module folder — the applier refuses this descriptor`\n : `no such file: ${path}`\n )\n );\n }\n}\n\n/**\n * Whether a descriptor-authored path is absent under the module folder, and why. The\n * applier resolves the same paths through `joinModulePath`, so a `../` that slips past\n * doctor here would be reported present and then refused on a stranger's machine.\n */\nasync function missingModulePath(\n dir: string,\n relPosix: string\n): Promise<false | \"absent\" | \"escape\"> {\n let abs: string;\n try {\n abs = joinModulePath(dir, relPosix);\n } catch {\n return \"escape\";\n }\n return (await pathExists(abs)) ? false : \"absent\";\n}\n\nexport interface ProjectCheckArgs {\n config: SaasaloyConfig;\n manifest: Manifest;\n}\n\n/**\n * The one project-state rule (#107): a module `saasaloy.json` lists as installed that owns\n * no file in `.saasaloy/manifest.json`. `add` warns once when it reclaims a target whose\n * file was hand-deleted, and that warning scrolls away; this is how the state stays\n * findable afterwards. The inverse — files tracked for a module that is not installed —\n * is `checkPartialInstalls` below (#49).\n *\n * Already-loaded state comes in rather than a root path, so the rule tests with plain\n * objects like the descriptor rules above it. `config.base` is not walked, so the base app\n * can never be reported.\n */\nexport function checkProject(args: ProjectCheckArgs): Finding[] {\n const { config, manifest } = args;\n const owners = new Set(\n Object.values(manifest.managed).map((entry) => entry.module)\n );\n // `managed` is the file ledger, and the criterion is about files. A module owning a\n // patch or a link but no file is still a module that owns no files.\n return config.installed\n .filter((name) => !owners.has(name))\n .map((name) =>\n finding(\n name,\n \"/installed\",\n `installed but owns no files in .saasaloy/manifest.json — run \\`saasaloy remove ${name}\\` to drop it.`\n )\n );\n}\n\nexport interface DoctorTarget {\n /** The registry directory the modules sit in. */\n registryDir: string;\n /** Module folder names to check — one, or every module the registry offers. */\n names: string[];\n}\n\n/**\n * Read `path` as either one module folder or a registry of them. A folder carrying a\n * `registry-item.json` is the module; anything else is a directory of module folders.\n */\nexport async function resolveDoctorTarget(path: string): Promise<DoctorTarget> {\n const dir = resolve(path);\n if (await pathExists(join(dir, \"registry-item.json\"))) {\n return { names: [basename(dir)], registryDir: dirname(dir) };\n }\n return { names: await registryModuleNames(dir), registryDir: dir };\n}\n\nexport interface ProjectState {\n /** The project's `.saasaloy/manifest.json`, as `loadManifest` returns it. */\n manifest: Manifest;\n /** `saasaloy.json`'s `installed` list. */\n installed: string[];\n}\n\n/**\n * The inverse of `checkProject`'s rule (#49): a module the manifest tracks work for,\n * absent from `installed`, is a partial install.\n *\n * `add` leaves that state on purpose. It never rolls back, so a run that failed mid-apply\n * — or one whose file drifted between the plan and the write — keeps the files it wrote\n * and withholds the install. The bookkeeping is honest and the project is unfinished, and\n * only a re-run finishes it, which is what this says out loud. Two files, or twenty, are\n * one finding: the module is the unit the user acts on.\n */\nexport function checkPartialInstalls(state: ProjectState): Finding[] {\n const installed = new Set(state.installed);\n const tracked = new Set<string>();\n for (const entry of Object.values(state.manifest.managed)) {\n tracked.add(entry.module);\n }\n // A patch counts as tracked work too. A module whose files all held as conflicts can\n // still have landed a dependency in a package.json, and that is on disk either way.\n for (const patch of state.manifest.patches) {\n tracked.add(patch.module);\n }\n // The base records its files under a reserved name and is never in `installed` (#120).\n return [...tracked]\n .filter((name) => !installed.has(name) && name !== BASE_MODULE)\n .toSorted()\n .map((name) =>\n finding(\n name,\n `/installed/${name}`,\n `partial install — re-run \\`saasaloy add ${name}\\``\n )\n );\n}\n\n/** Check every module named by `target`, in name order. */\nexport async function checkTarget(\n target: DoctorTarget\n): Promise<ModuleReport[]> {\n const siblings = await registryModuleNames(target.registryDir);\n const aliases = await collectAliases(target.registryDir, siblings);\n const reports: ModuleReport[] = [];\n for (const name of target.names) {\n reports.push(\n await checkModule({\n aliases,\n dir: join(target.registryDir, name),\n siblings,\n })\n );\n }\n return reports;\n}\n\nexport interface BaseCheckArgs {\n root: string;\n lock: Lockfile;\n manifest: Manifest;\n /** The seed paths the running template declares — listed, never hashed. */\n seedFiles: string[];\n}\n\nexport interface BaseReport {\n /** `untracked` when the project carries no usable record; `update` writes one. */\n status: \"tracked\" | \"untracked\";\n record?: LockBase;\n /** Managed base files whose bytes no longer match their recorded hash. */\n drifted: string[];\n /** Managed base files the record names that are not on disk. */\n missing: string[];\n /** How many managed base files still match their recorded hash. */\n matching: number;\n /** Seed files — declared by the template, or recorded as such — reported but not checked. */\n seed: string[];\n}\n\n/**\n * The base half of `doctor <project>` (#120): is there a record, which CLI wrote it, and\n * which managed base files moved since. A drifted file is information, not a problem —\n * the owner is meant to edit the base — so this never contributes a `Finding`, and it\n * never writes: an untracked base is reported and left for `update` to adopt.\n */\nexport async function checkBase(args: BaseCheckArgs): Promise<BaseReport> {\n const { root, lock, manifest } = args;\n const seedSet = new Set(args.seedFiles);\n if (!isBaseTracked(lock, manifest) || !lock.base) {\n return {\n status: \"untracked\",\n drifted: [],\n missing: [],\n matching: 0,\n seed: args.seedFiles.toSorted(),\n };\n }\n const drifted: string[] = [];\n const missing: string[] = [];\n const seed: string[] = [];\n let matching = 0;\n for (const [target, entry] of Object.entries(baseEntries(manifest))) {\n if (seedSet.has(target)) {\n seed.push(target);\n continue;\n }\n const mine = await readIfPresent(resolveWithinRoot(root, target));\n if (mine === undefined) {\n missing.push(target);\n } else if (hashContent(mine) === entry.hash) {\n matching++;\n } else {\n drifted.push(target);\n }\n }\n return {\n status: \"tracked\",\n record: lock.base,\n drifted: drifted.toSorted(),\n missing: missing.toSorted(),\n matching,\n seed: seed.toSorted(),\n };\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport {\n cancel,\n intro,\n isCancel,\n log,\n note,\n outro,\n select,\n text,\n} from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { parseDevVars } from \"../lib/dev-vars.js\";\nimport {\n apiWorkspace,\n appendVars,\n discoverWorkspaces,\n findWranglerWorkspaces,\n isSet,\n productionSecretCommands,\n routeVariable,\n targetFileName,\n workspaceForPath,\n workspacesByModule,\n} from \"../lib/env-vars.js\";\nimport type { Declaration } from \"../lib/env-vars.js\";\nimport {\n EXIT_OK,\n EXIT_REFUSED,\n exitCodeFor,\n formatFailure,\n} from \"../lib/exit.js\";\nimport {\n assertNoSymlinkPath,\n pathExists,\n resolveWithinRoot,\n} from \"../lib/fs-utils.js\";\nimport { isPathIgnored } from \"../lib/gitignore.js\";\nimport { loadLock } from \"../lib/lock.js\";\nimport type { Lockfile } from \"../lib/lock.js\";\nimport { loadManifest } from \"../lib/manifest.js\";\nimport { findProjectRoot } from \"../lib/project.js\";\nimport {\n LocalRegistrySource,\n REGISTRY_ENV,\n RemoteRegistrySource,\n} from \"../lib/registry.js\";\nimport type { RegistrySource } from \"../lib/registry.js\";\nimport { loadConfig } from \"../lib/saasaloy-config.js\";\nimport type { LoadedConfig } from \"../lib/saasaloy-config.js\";\nimport { isInteractive, wrapForNote } from \"../lib/tui.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\n\n// `saasaloy env` — fill in the variables the installed modules declare (#50).\n//\n// Before this, `add` rendered `apps/api/.dev.vars.example` and stopped there: the project\n// knew what it needed and no file on disk had it. A `PUBLIC_*` value was worse off still,\n// because it belongs in the frontend's `.env`, which the example file never described.\n//\n// The command fills blanks and nothing else. A value already typed is never rewritten,\n// whatever the descriptor's `devVars` suggests, because the person who typed it knew\n// something the descriptor did not. It refuses to write at all unless git ignores the\n// target, and it prints the production commands rather than running them: putting a\n// secret into a live account is a deploy, and this is a scaffolding tool.\n\nexport interface Options {\n /** `--check`: report what is missing and exit non-zero, prompting for nothing. */\n check: boolean;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\"--check\", \"--help\", \"-h\"]);\nconst USAGE = \"saasaloy env [--check]\";\nconst HELP: CommandHelp = {\n name: \"env\",\n describe: DESCRIPTIONS.env,\n usage: USAGE,\n flags: {\n \"--check\": \"report missing variables and exit non-zero, without prompting\",\n },\n};\n\nexport function parseArgs(argv: string[]): Options {\n const unknown: string[] = [];\n for (const arg of argv) {\n // Every declared variable is covered; there is no single-variable form to name.\n if (!arg.startsWith(\"-\") || !KNOWN_FLAGS.has(arg)) {\n unknown.push(arg);\n }\n }\n return { check: argv.includes(\"--check\"), unknown };\n}\n\n/** A variable that still needs an answer, and where the answer goes. */\nexport interface Pending {\n declaration: Declaration;\n /** Project-relative workspace root, or undefined when routing found no target. */\n workspace?: string;\n /** Project-relative target file, or undefined alongside an undefined workspace. */\n file?: string;\n /** Set when routing found several plausible workspaces and a person must pick. */\n choices?: string[];\n}\n\n/**\n * One report line per pending variable: the name, the module that declared it, and\n * either the file it will land in or why no file could be chosen.\n *\n * Pure and exported so `--check`'s output is tested without a project on disk.\n */\nexport function renderPending(pending: Pending[]): string[] {\n return pending.map((entry) => {\n const head = `${pc.cyan(entry.declaration.name)} ${pc.dim(`— declared by ${entry.declaration.module}`)}`;\n if (entry.file) {\n return `${head}\\n ${pc.dim(`→ ${entry.file}`)}`;\n }\n const why = entry.choices\n ? `several workspaces fit (${entry.choices.join(\", \")}) — run \\`saasaloy env\\` to pick one`\n : \"no target workspace found — this module wrote no files under a known alias\";\n return `${head}\\n ${pc.yellow(`→ ${why}`)}`;\n });\n}\n\n/**\n * Read every installed module's descriptor and flatten its `envVars` into declarations.\n *\n * The lock records provenance but not `envVars`, so the descriptions have to come from\n * the registry — the same path `update` takes. Each module is pinned to the SHA the lock\n * resolved, so the wording matches the code actually on disk rather than whatever `main`\n * says today.\n *\n * A module that cannot be read is reported and skipped. One unreachable repo must not\n * hide every other module's variables, and `--check` still gates on the ones it saw.\n */\nasync function loadDeclarations(\n config: LoadedConfig,\n lock: Lockfile,\n sources: RegistrySource[],\n onSkip: (message: string) => void\n): Promise<Declaration[]> {\n const override = process.env[REGISTRY_ENV];\n const local: RegistrySource | undefined = override\n ? new LocalRegistrySource(\n isAbsolute(override) ? override : resolve(process.cwd(), override)\n )\n : undefined;\n const resolvers = new Map<string, RegistrySource>();\n\n const declarations: Declaration[] = [];\n for (const name of config.installed) {\n const entry = lock.modules[name];\n let source: RegistrySource | undefined = local;\n if (source === undefined) {\n if (!entry) {\n onSkip(\n `${name} has no lock entry — nothing to read its variables from.`\n );\n continue;\n }\n if (entry.source === \"local\") {\n onSkip(\n `${name} was installed from a local checkout — set ${REGISTRY_ENV} to read its variables.`\n );\n continue;\n }\n const [owner, repo] = entry.source.split(\"/\");\n if (!owner || !repo) {\n onSkip(\n `${name}'s lock source \"${entry.source}\" isn't an owner/repo coordinate.`\n );\n continue;\n }\n // Pin to the resolved SHA, not the ref: the descriptor has to describe the bytes\n // this project installed, and the ref has moved on by definition when it moved.\n const ref = entry.resolved === \"local\" ? entry.ref : entry.resolved;\n const key = `${entry.source}@${ref}`;\n let cached = resolvers.get(key);\n if (!cached) {\n cached = new RemoteRegistrySource(owner, repo, ref);\n resolvers.set(key, cached);\n sources.push(cached);\n }\n source = cached;\n }\n\n try {\n const { item } = await source.readModule(name);\n for (const [variable, description] of Object.entries(\n item.envVars ?? {}\n )) {\n declarations.push({\n description,\n module: name,\n name: variable,\n ...(item.devVars?.[variable] === undefined\n ? {}\n : { devValue: item.devVars[variable] }),\n });\n }\n } catch (error) {\n onSkip(\n `${name}'s descriptor could not be read — ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n // Two modules can declare the same variable (`PUBLIC_API_URL` comes from both `admin`\n // and `waitlist`). First declaration wins, so the prompt is asked once.\n const seen = new Set<string>();\n return declarations.filter((declaration) => {\n if (seen.has(declaration.name)) {\n return false;\n }\n seen.add(declaration.name);\n return true;\n });\n}\n\n/** The workspace an alias sits in, used to find the base app for a `PUBLIC_*` fallback. */\nfunction workspaceForAlias(\n aliases: Record<string, string>,\n alias: string,\n workspaces: string[]\n): string | undefined {\n const value = aliases[alias];\n if (!value) {\n return undefined;\n }\n return workspaceForPath(\n value.endsWith(\"/\") ? value : `${value}/`,\n workspaces\n );\n}\n\n/**\n * Write the answers, one file at a time, and report `[file, count]` per file touched.\n *\n * `contents` carries what each file held when the run read it, so the append is built\n * from the same bytes the \"already set\" decision was made against. Exported because it\n * is the half of `env` that touches disk: the tests drive it directly, since the prompt\n * loop above it needs a terminal that no test process has.\n */\nexport async function writeAnswers(\n root: string,\n answers: Map<string, [string, string][]>,\n contents: Map<string, string | undefined>\n): Promise<[string, number][]> {\n const written: [string, number][] = [];\n for (const [file, additions] of [...answers].toSorted(([a], [b]) =>\n a.localeCompare(b)\n )) {\n if (additions.length === 0) {\n continue;\n }\n const abs = resolveWithinRoot(root, file);\n await assertNoSymlinkPath(root, abs);\n await mkdir(dirname(abs), { recursive: true });\n await writeFile(abs, appendVars(contents.get(file), additions), \"utf-8\");\n written.push([file, additions.length]);\n }\n return written;\n}\n\nasync function readTarget(\n root: string,\n file: string\n): Promise<string | undefined> {\n const abs = resolveWithinRoot(root, file);\n // Same guard `dev-vars` takes: the lexical check proves the path is inside the project,\n // and the read that follows would still traverse a planted symlink out of it.\n await assertNoSymlinkPath(root, abs);\n return (await pathExists(abs)) ? readFile(abs, \"utf-8\") : undefined;\n}\n\nexport async function runEnv(argv: string[]): Promise<number> {\n // Parse before help answers, so a typo'd flag alongside `--help` still reports the typo.\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n intro(pc.bgCyan(pc.black(\" saasaloy env \")));\n\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n\n const sources: RegistrySource[] = [];\n try {\n const root = await findProjectRoot();\n const config = await loadConfig(root);\n const manifest = await loadManifest(root);\n const lock = await loadLock(root);\n\n const declarations = await loadDeclarations(\n config,\n lock,\n sources,\n (msg) => {\n log.warn(msg);\n }\n );\n if (declarations.length === 0) {\n outro(pc.dim(\"No module declares an environment variable.\"));\n return EXIT_OK;\n }\n\n const workspaces = await discoverWorkspaces(root, config.aliases);\n const wranglerWorkspaces = await findWranglerWorkspaces(root, workspaces);\n const byModule = workspacesByModule(manifest, workspaces);\n const api = apiWorkspace(config.aliases);\n const base = workspaceForAlias(\n config.aliases,\n `@${config.base ?? \"web\"}`,\n workspaces\n );\n\n // One read per target file, reused for every variable that routes to it, so two\n // variables landing in the same `.dev.vars` see the same \"already set\" answer.\n const contents = new Map<string, string | undefined>();\n const readOnce = async (file: string): Promise<string | undefined> => {\n if (!contents.has(file)) {\n contents.set(file, await readTarget(root, file));\n }\n return contents.get(file);\n };\n\n const pending: Pending[] = [];\n const routed: { name: string; workspace: string }[] = [];\n for (const declaration of declarations) {\n const route = routeVariable({\n candidates: byModule.get(declaration.module) ?? [],\n name: declaration.name,\n wranglerWorkspaces,\n ...(api ? { apiWorkspace: api } : {}),\n ...(base ? { baseWorkspace: base } : {}),\n });\n if (route.kind === \"resolved\") {\n const file = `${route.workspace}/${targetFileName(declaration.name)}`;\n routed.push({ name: declaration.name, workspace: route.workspace });\n const values = parseDevVars((await readOnce(file)) ?? \"\");\n if (isSet(values, declaration.name)) {\n continue;\n }\n pending.push({ declaration, file, workspace: route.workspace });\n continue;\n }\n pending.push({\n declaration,\n ...(route.kind === \"ambiguous\" ? { choices: route.choices } : {}),\n });\n }\n\n // The production block is a report, so it prints on every run — a `--check` in CI is\n // exactly where someone wants the deploy commands to hand to an operator.\n const secretCommands = productionSecretCommands(routed);\n const printSecrets = (): void => {\n if (secretCommands.length > 0) {\n note(\n wrapForNote(\n `${secretCommands.join(\"\\n\")}\\n\\n${pc.dim(\"Printed, never run — copy these when you deploy.\")}`\n ),\n \"Production secrets\"\n );\n }\n };\n\n if (pending.length === 0) {\n printSecrets();\n outro(pc.green(\"Every declared variable is set.\"));\n return EXIT_OK;\n }\n\n // Prove every file is ignored before asking for a single secret. Typing an API key\n // into a prompt that then refuses to write it wastes the key as much as the typing —\n // and the same refusal is the right answer under `--check`, where it says the deploy\n // gate would have written a secret into a tracked file.\n const files = [\n ...new Set(pending.flatMap((entry) => (entry.file ? [entry.file] : []))),\n ].toSorted();\n for (const file of files) {\n if (!(await isPathIgnored(root, file))) {\n cancel(\n `${pc.cyan(file)} isn't gitignored — refusing to write a secret into a tracked file. ` +\n `Add it to .gitignore (the base template already does) and run again.`\n );\n return EXIT_REFUSED;\n }\n }\n\n // `--check` and a session with no terminal take the same path. A prompt nobody can\n // answer is a hang, and a hang in CI is worse than a refusal.\n if (opts.check || !isInteractive()) {\n note(wrapForNote(renderPending(pending).join(\"\\n\")), \"Missing\");\n printSecrets();\n const count = pending.length;\n cancel(\n `${count} variable${count === 1 ? \"\" : \"s\"} still unset — run \\`saasaloy env\\` to fill ${count === 1 ? \"it\" : \"them\"} in.`\n );\n return EXIT_REFUSED;\n }\n\n const answers = new Map<string, [string, string][]>();\n for (const entry of pending) {\n let file = entry.file;\n if (!file) {\n if (!entry.choices) {\n log.warn(\n `${pc.cyan(entry.declaration.name)} — no target workspace found; set it by hand.`\n );\n continue;\n }\n const picked = await select({\n message: `Which workspace reads ${entry.declaration.name}?`,\n options: entry.choices.map((workspace) => ({\n label: workspace,\n value: workspace,\n })),\n });\n if (isCancel(picked)) {\n cancel(\"Cancelled — nothing written.\");\n return EXIT_REFUSED;\n }\n file = `${picked}/${targetFileName(entry.declaration.name)}`;\n if (!(await isPathIgnored(root, file))) {\n cancel(\n `${pc.cyan(file)} isn't gitignored — refusing to write a secret into a tracked file.`\n );\n return EXIT_REFUSED;\n }\n const values = parseDevVars((await readOnce(file)) ?? \"\");\n if (isSet(values, entry.declaration.name)) {\n continue;\n }\n }\n\n // The message is the descriptor's own wording. `env` does not paraphrase it: the\n // module author wrote the sentence that explains what the value is for, and a\n // second wording here would drift from the one in `.dev.vars.example`.\n const answer = await text({\n defaultValue: entry.declaration.devValue ?? \"\",\n message: `${pc.cyan(entry.declaration.name)} ${pc.dim(`→ ${file}`)}\\n${entry.declaration.description}`,\n ...(entry.declaration.devValue === undefined\n ? {}\n : { initialValue: entry.declaration.devValue }),\n });\n if (isCancel(answer)) {\n cancel(\"Cancelled — nothing written.\");\n return EXIT_REFUSED;\n }\n answers.set(file, [\n ...(answers.get(file) ?? []),\n [entry.declaration.name, answer],\n ]);\n }\n\n const written = await writeAnswers(root, answers, contents);\n if (written.length > 0) {\n note(\n wrapForNote(\n written\n .map(\n ([file, count]) =>\n `${pc.cyan(file)} ${pc.dim(`— ${count} added`)}`\n )\n .join(\"\\n\")\n ),\n \"Written\"\n );\n }\n printSecrets();\n outro(pc.green(\"Environment filled in.\"));\n return EXIT_OK;\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n } finally {\n for (const source of sources) {\n await source.cleanup?.();\n }\n }\n}\n","import { posix } from \"node:path\";\nimport { pathExists, resolveWithinRoot } from \"./fs-utils.js\";\nimport type { Manifest } from \"./manifest.js\";\n\n// The routing half of `saasaloy env` (#50): which file does a declared variable belong\n// in, and is it already set?\n//\n// Two facts settle every case. A `PUBLIC_*` variable is a build-time value the frontend\n// bundles, so it belongs in that app's `.env`, which Astro and Vite read. Everything else\n// is a secret the Worker reads at runtime, so it belongs in `.dev.vars`, which is\n// wrangler's file and nothing else's. There is no `secret: true` flag on the descriptor\n// and there is not going to be one: the prefix already says it, and a second way to say\n// the same thing is a second way to disagree with it.\n//\n// *Which* app is the part that has to be inferred, because `envVars` is a flat\n// `NAME → description` map with no app scope. The manifest knows: it records every file\n// a module wrote, keyed by project-relative path, so the workspaces a module touched are\n// recoverable from it. This file turns that into a target and says plainly when it\n// cannot.\n\nexport const ENV_FILE = \".env\";\nexport const DEV_VARS_FILE = \".dev.vars\";\n\n/** The literal, case-sensitive prefix that marks a build-time public value. */\nexport const PUBLIC_PREFIX = \"PUBLIC_\";\n\n/** Where an app workspace lives by convention — the half of the repo that is not a lib. */\nconst APPS_DIR = \"apps/\";\n\nconst WRANGLER_CONFIGS = [\"wrangler.jsonc\", \"wrangler.json\", \"wrangler.toml\"];\n\nexport function isPublicVar(name: string): boolean {\n return name.startsWith(PUBLIC_PREFIX);\n}\n\n/** `.env` for a public build-time value, `.dev.vars` for a secret. */\nexport function targetFileName(name: string): string {\n return isPublicVar(name) ? ENV_FILE : DEV_VARS_FILE;\n}\n\n/** One variable a module declares, carrying the description the prompt will read out. */\nexport interface Declaration {\n name: string;\n /** The declaring module's own wording — `env` never paraphrases it. */\n description: string;\n /** The module that declared it. */\n module: string;\n /** The local-dev value the descriptor supplies, if any. */\n devValue?: string;\n}\n\nexport type Route =\n | { kind: \"resolved\"; workspace: string }\n | { kind: \"ambiguous\"; choices: string[] }\n | { kind: \"unknown\" };\n\nexport interface RouteArgs {\n name: string;\n /** Workspaces the declaring module wrote files into, project-relative. */\n candidates: string[];\n /** Of those, the ones holding a wrangler config. */\n wranglerWorkspaces: string[];\n /** The project's Worker workspace, from the `@api` alias. */\n apiWorkspace?: string;\n /** The base app's workspace — where a `PUBLIC_*` value lands when nothing else fits. */\n baseWorkspace?: string;\n}\n\n/** A route to a workspace, or nothing when the caller had no workspace to offer. */\nfunction resolved(workspace: string | undefined): Route | undefined {\n return workspace ? { kind: \"resolved\", workspace } : undefined;\n}\n\n/**\n * The workspace whose `.env` or `.dev.vars` a variable belongs in.\n *\n * A `PUBLIC_*` value is bundled by exactly one frontend, so it looks for an app the\n * declaring module wrote into, falling back to the project's base app — `waitlist` ships\n * a component into `apps/web` and its `PUBLIC_API_URL` is what that component reads.\n *\n * A secret is read by whatever runs it, which for `.dev.vars` means a wrangler process.\n * So it prefers a candidate holding a wrangler config, breaks a tie towards the api\n * workspace, and falls back to the api workspace outright: `packages/email` declares\n * `PLUNK_API_KEY` and writes no app file at all, but the Worker importing that package\n * is what needs the key on disk.\n *\n * Pure, and it says `ambiguous` rather than guessing — the caller prompts.\n */\nexport function routeVariable(args: RouteArgs): Route {\n const { candidates, wranglerWorkspaces } = args;\n\n if (isPublicVar(args.name)) {\n const apps = candidates.filter(\n (w) => w.startsWith(APPS_DIR) && w !== args.apiWorkspace\n );\n if (apps.length === 1) {\n return { kind: \"resolved\", workspace: apps[0]! };\n }\n if (apps.length > 1) {\n return { choices: apps.toSorted(), kind: \"ambiguous\" };\n }\n return resolved(args.baseWorkspace) ?? fromCandidatesAlone(candidates);\n }\n\n const wrangler = candidates.filter((w) => wranglerWorkspaces.includes(w));\n if (wrangler.length === 1) {\n return { kind: \"resolved\", workspace: wrangler[0]! };\n }\n if (wrangler.length > 1) {\n return (\n (args.apiWorkspace && wrangler.includes(args.apiWorkspace)\n ? resolved(args.apiWorkspace)\n : undefined) ?? { choices: wrangler.toSorted(), kind: \"ambiguous\" }\n );\n }\n return resolved(args.apiWorkspace) ?? fromCandidatesAlone(candidates);\n}\n\nfunction fromCandidatesAlone(candidates: string[]): Route {\n if (candidates.length === 1) {\n return { kind: \"resolved\", workspace: candidates[0]! };\n }\n return candidates.length === 0\n ? { kind: \"unknown\" }\n : { choices: candidates.toSorted(), kind: \"ambiguous\" };\n}\n\n/**\n * The workspace roots this project has, derived from the alias map: an alias points at a\n * source directory (`packages/db/src`), and the workspace is the nearest ancestor that\n * carries a `package.json`. Walking up rather than assuming `dirname` keeps a nested\n * alias (`@ui` → `packages/ui/src/components`) pointing at the right workspace.\n *\n * The project root is deliberately excluded. It has a `package.json` too, and a variable\n * routed there would write `.dev.vars` beside `pnpm-workspace.yaml`, where no wrangler\n * process ever looks.\n */\nexport async function discoverWorkspaces(\n root: string,\n aliases: Record<string, string>\n): Promise<string[]> {\n const found = new Set<string>();\n for (const value of Object.values(aliases)) {\n const workspace = await walkUpToWorkspace(root, value);\n if (workspace) {\n found.add(workspace);\n }\n }\n return [...found].toSorted();\n}\n\nasync function walkUpToWorkspace(\n root: string,\n relPosixPath: string\n): Promise<string | undefined> {\n let current = relPosixPath.replace(/\\/+$/, \"\");\n while (current && current !== \".\" && current !== \"/\") {\n if (await pathExists(resolveWithinRoot(root, `${current}/package.json`))) {\n return current;\n }\n const parent = posix.dirname(current);\n if (parent === current) {\n return undefined;\n }\n current = parent;\n }\n return undefined;\n}\n\n/** Of `workspaces`, those holding a wrangler config — the ones that read `.dev.vars`. */\nexport async function findWranglerWorkspaces(\n root: string,\n workspaces: string[]\n): Promise<string[]> {\n const found: string[] = [];\n for (const workspace of workspaces) {\n for (const config of WRANGLER_CONFIGS) {\n if (await pathExists(resolveWithinRoot(root, `${workspace}/${config}`))) {\n found.push(workspace);\n break;\n }\n }\n }\n return found;\n}\n\n/**\n * Which workspaces each module wrote into, read back from the manifest's `managed` map.\n * The key is the project-relative path the file landed on, so the longest workspace that\n * prefixes it is the workspace that owns it. A file under no known workspace (a root\n * `README`, an `infra/` scaffold with no alias) contributes nothing rather than a guess.\n */\nexport function workspacesByModule(\n manifest: Manifest,\n workspaces: string[]\n): Map<string, string[]> {\n const byModule = new Map<string, Set<string>>();\n for (const [path, entry] of Object.entries(manifest.managed)) {\n const workspace = workspaceForPath(path, workspaces);\n if (!workspace) {\n continue;\n }\n const set = byModule.get(entry.module) ?? new Set<string>();\n set.add(workspace);\n byModule.set(entry.module, set);\n }\n return new Map(\n [...byModule].map(([module, set]) => [module, [...set].toSorted()])\n );\n}\n\n/** The longest workspace that contains `path`, or undefined when none does. */\nexport function workspaceForPath(\n path: string,\n workspaces: string[]\n): string | undefined {\n let best: string | undefined;\n for (const workspace of workspaces) {\n if (\n path.startsWith(`${workspace}/`) &&\n (best === undefined || workspace.length > best.length)\n ) {\n best = workspace;\n }\n }\n return best;\n}\n\n/** The api workspace, re-exported so `env` reads one name for the Worker's root. */\nexport { apiWorkspace } from \"./dev-vars.js\";\n\n/**\n * Is this variable already answered? A key present with an empty value is a placeholder\n * someone left behind, not an answer, so `env` offers to fill it. Anything else is a\n * value a person typed, and `env` never rewrites one.\n */\nexport function isSet(values: Record<string, string>, name: string): boolean {\n return (values[name] ?? \"\").trim() !== \"\";\n}\n\n/**\n * Is this line the empty placeholder for `name` — `KEY=`, with nothing but spaces after\n * the `=`? A commented-out line is not one: `# KEY=` has a key of `# KEY`.\n */\nfunction isPlaceholderFor(line: string, name: string): boolean {\n const equals = line.indexOf(\"=\");\n return (\n equals !== -1 &&\n line.slice(0, equals).trim() === name &&\n line.slice(equals + 1).trim() === \"\"\n );\n}\n\n/**\n * The file's new content, with `additions` written in.\n *\n * Every existing line survives byte for byte, comments and ordering included. This is a\n * file a person edits by hand, unlike `.dev.vars.example`, which is regenerated from the\n * descriptors — re-rendering it here would throw away their formatting to say the same\n * thing.\n *\n * One exception, and it is the reason this is not a plain append: a `.dev.vars` copied\n * from `.dev.vars.example` holds `KEY=` for every variable, and `isSet` calls those\n * unset. Appending would leave the file carrying two lines for one key, the first of them\n * a lie. So a placeholder is filled where it stands, and only a key the file has never\n * heard of goes on the end.\n */\nexport function appendVars(\n existing: string | undefined,\n additions: [string, string][]\n): string {\n if (additions.length === 0) {\n return existing ?? \"\";\n }\n if (!existing || existing.trim() === \"\") {\n return `${additions.map(([name, value]) => `${name}=${value}`).join(\"\\n\")}\\n`;\n }\n\n const lines = existing.split(\"\\n\");\n const appended: string[] = [];\n for (const [name, value] of additions) {\n const line = `${name}=${value}`;\n const index = lines.findIndex((existingLine) =>\n isPlaceholderFor(existingLine, name)\n );\n if (index === -1) {\n appended.push(line);\n } else {\n lines[index] = line;\n }\n }\n\n const kept = lines.join(\"\\n\");\n const head = kept.endsWith(\"\\n\") ? kept : `${kept}\\n`;\n return appended.length === 0 ? head : `${head}${appended.join(\"\\n\")}\\n`;\n}\n\n/**\n * The production block: one `wrangler secret put` per secret, grouped by the workspace\n * you have to run it from.\n *\n * Printed, never run. Putting a secret into a live Cloudflare account is a deploy, and a\n * scaffolding tool that deploys on your behalf is a scaffolding tool you cannot trust\n * with a token. The lines are here to be copied.\n */\nexport function productionSecretCommands(\n targets: { name: string; workspace: string }[]\n): string[] {\n const byWorkspace = new Map<string, string[]>();\n for (const { name, workspace } of targets) {\n if (isPublicVar(name)) {\n continue;\n }\n byWorkspace.set(workspace, [...(byWorkspace.get(workspace) ?? []), name]);\n }\n const lines: string[] = [];\n for (const workspace of [...byWorkspace.keys()].toSorted()) {\n lines.push(`# from ${workspace}`);\n for (const name of (byWorkspace.get(workspace) ?? []).toSorted()) {\n lines.push(`wrangler secret put ${name}`);\n }\n }\n return lines;\n}\n","import { readFile } from \"node:fs/promises\";\nimport { dirname, join, relative, resolve, sep } from \"node:path\";\nimport { pathExists } from \"./fs-utils.js\";\n\n// Does git ignore this path? `saasaloy env` writes real secrets into `.dev.vars` and\n// `.env`, so it has to prove the file will not be committed before it writes a single\n// byte (#50). The base template's `_gitignore` already carves both out, but a project is\n// a person's to edit, and an `env` that wrote a live API key into a tracked file would be\n// the worst bug this CLI could ship.\n//\n// The check is done here, in process, rather than by shelling out to `git check-ignore`.\n// `env`'s whole contract is that it prints deployment commands and never runs anything,\n// and that promise is only checkable if the command path starts no child process at all. So this\n// file implements the subset of gitignore's pattern language the question needs:\n// comments, negation, anchoring, directory-only patterns, `*`, `?`, and `**`.\n//\n// Four deliberate gaps, none of which changes the answer for an env file: character\n// classes (`[a-z]`) are matched literally, a backslash escape (`\\#.env`, a trailing\n// `.env\\ `) is matched literally rather than unescaped, `.git/info/exclude` and the\n// global excludes file are not read, and a `.gitignore` inside an already-ignored\n// directory is still read. Each can only make the check stricter (report \"not ignored\"\n// for something git would ignore), which refuses rather than leaks.\n\ninterface Rule {\n /** Tested against the path relative to the `.gitignore`'s own directory. */\n test: RegExp;\n /** A leading `!` — re-includes what an earlier rule excluded. */\n negated: boolean;\n /** A trailing `/` — matches a directory and never a file. */\n dirOnly: boolean;\n}\n\ninterface Layer {\n /** Project-relative POSIX directory the `.gitignore` sits in; `\"\"` at the root. */\n base: string;\n rules: Rule[];\n}\n\nconst REGEX_SPECIAL = /[.+^${}()|[\\]\\\\]/g;\n\n/** Translate a gitignore glob body into a regex source, `/` never matched by `*`. */\nfunction globToRegex(glob: string): string {\n let out = \"\";\n let index = 0;\n while (index < glob.length) {\n const char = glob[index]!;\n if (char === \"*\" && glob[index + 1] === \"*\") {\n // `**/` crosses directory boundaries; a bare `**` matches the rest of the path.\n index += 2;\n if (glob[index] === \"/\") {\n index += 1;\n out += \"(?:.*/)?\";\n } else {\n out += \".*\";\n }\n continue;\n }\n if (char === \"*\") {\n out += \"[^/]*\";\n } else if (char === \"?\") {\n out += \"[^/]\";\n } else {\n out += char.replace(REGEX_SPECIAL, \"\\\\$&\");\n }\n index += 1;\n }\n return out;\n}\n\n/**\n * One `.gitignore` line as a rule, or undefined for a blank line or a comment.\n *\n * Exported for the tests: the anchoring rule (a pattern with an interior `/` is relative\n * to the `.gitignore`, one without matches a basename at any depth) is the part most\n * likely to be got wrong, and it is worth pinning directly.\n */\nexport function compilePattern(pattern: string): Rule | undefined {\n const trimmed = pattern.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) {\n return undefined;\n }\n const negated = trimmed.startsWith(\"!\");\n let body = negated ? trimmed.slice(1) : trimmed;\n const dirOnly = body.endsWith(\"/\");\n if (dirOnly) {\n body = body.slice(0, -1);\n }\n if (!body) {\n return undefined;\n }\n // A `/` anywhere but the very end anchors the pattern to this `.gitignore`'s directory.\n const anchored = body.includes(\"/\");\n if (body.startsWith(\"/\")) {\n body = body.slice(1);\n }\n const prefix = anchored ? \"\" : \"(?:.*/)?\";\n return {\n dirOnly,\n negated,\n test: new RegExp(`^${prefix}${globToRegex(body)}$`),\n };\n}\n\nexport function parseGitignore(text: string): Rule[] {\n const rules: Rule[] = [];\n for (const line of text.split(\"\\n\")) {\n const rule = compilePattern(line);\n if (rule) {\n rules.push(rule);\n }\n }\n return rules;\n}\n\n/**\n * Whether the layers exclude one path. Shallow `.gitignore`s are applied before deep\n * ones and, within a file, the last matching line wins — git's own precedence, which is\n * what makes `!.env.example` after `.env.*` mean anything.\n */\nfunction excludes(layers: Layer[], candidate: string, isDir: boolean): boolean {\n let ignored = false;\n for (const layer of layers) {\n if (layer.base && !candidate.startsWith(`${layer.base}/`)) {\n continue;\n }\n const rel = layer.base ? candidate.slice(layer.base.length + 1) : candidate;\n for (const rule of layer.rules) {\n if (rule.dirOnly && !isDir) {\n continue;\n }\n if (rule.test.test(rel)) {\n ignored = !rule.negated;\n }\n }\n }\n return ignored;\n}\n\n/**\n * The repository the project sits in, or undefined when it sits in none.\n *\n * The `.git` marker is not always at the project root. `saasaloy init .` inside an\n * existing repository deliberately does not nest a second one, and the CLI's own\n * `.dev/playground` is exactly that shape — so anchoring at the project root would report\n * every file as unignored and refuse every write.\n */\nexport async function findRepoRoot(start: string): Promise<string | undefined> {\n let current = resolve(start);\n for (;;) {\n if (await pathExists(join(current, \".git\"))) {\n return current;\n }\n const parent = dirname(current);\n if (parent === current) {\n return undefined;\n }\n current = parent;\n }\n}\n\n/**\n * Would git ignore `relPosixPath` (project-relative, need not exist yet)?\n *\n * A path under no repository at all answers false. That is the conservative reading:\n * `env` uses this to decide whether writing a secret is safe, and \"there is no repository\n * here yet\" is not proof that the file will stay out of one. Refusing says so; a person\n * who wants the file anyway can `git init` or write it by hand.\n */\nexport async function isPathIgnored(\n root: string,\n relPosixPath: string\n): Promise<boolean> {\n const repoRoot = await findRepoRoot(root);\n if (!repoRoot) {\n return false;\n }\n // Everything below is measured from the repository root, so a `.gitignore` above the\n // project is applied — and a project inside an already-ignored directory answers yes,\n // which is what git itself says.\n const fromRepo = relative(\n repoRoot,\n join(resolve(root), ...relPosixPath.split(\"/\"))\n );\n const segments = fromRepo.split(sep).filter(Boolean);\n if (segments.length === 0 || segments.includes(\"..\")) {\n return false;\n }\n\n const layers: Layer[] = [];\n for (let depth = 0; depth < segments.length; depth += 1) {\n const parts = segments.slice(0, depth);\n const file = join(repoRoot, ...parts, \".gitignore\");\n if (await pathExists(file)) {\n layers.push({\n base: parts.join(\"/\"),\n rules: parseGitignore(await readFile(file, \"utf-8\")),\n });\n }\n }\n\n // Top down: git cannot re-include a path inside an excluded directory, so the first\n // ancestor that matches settles it for everything below.\n for (let depth = 1; depth <= segments.length; depth += 1) {\n const candidate = segments.slice(0, depth).join(\"/\");\n if (excludes(layers, candidate, depth < segments.length)) {\n return true;\n }\n }\n return false;\n}\n","import { spawn } from \"node:child_process\";\nimport { readdir, readFile } from \"node:fs/promises\";\nimport { basename, join, resolve } from \"node:path\";\nimport {\n cancel,\n intro,\n isCancel,\n note,\n outro,\n select,\n spinner,\n text,\n} from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport type { LinkState } from \"../lib/fs-utils.js\";\nimport {\n classifyLink,\n createDirLink,\n hashContent,\n pathExists,\n readDirNames,\n} from \"../lib/fs-utils.js\";\nimport { CONFIG_FILE, loadConfig, saveConfig } from \"../lib/saasaloy-config.js\";\nimport { EXIT_FAILURE, EXIT_OK, EXIT_REFUSED } from \"../lib/exit.js\";\nimport { baseRecord, recordBaseFiles, templateHash } from \"../lib/base.js\";\nimport { loadLock, saveLock } from \"../lib/lock.js\";\nimport { logger } from \"../lib/logger.js\";\nimport { loadManifest, saveManifest } from \"../lib/manifest.js\";\nimport {\n baseTemplateDir,\n copyTemplate,\n templateVars,\n} from \"../lib/scaffold.js\";\nimport { stripAnsi, wrapForNote } from \"../lib/tui.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { readVersion } from \"../version.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\n\n// `saasaloy init <name>` — scaffold the near-inert base (Astro landing + @repo/ui\n// + @repo/tsconfig) and print next steps. The base ships committed AGENTS.md/CLAUDE.md\n// (fixed common rules); nothing is generated. Two things are written rather than copied:\n// the `.claude/skills/*` link for the skills the base carries — a per-machine symlink, not\n// a file (see linkAgentSkills) — and the base's provenance, one manifest entry per rendered\n// file plus the lock's `base` record, so `outdated`/`update`/`doctor` can speak about the\n// base afterwards (#120, ADR 0032). Churny modules (api, database, auth, admin, features)\n// are added later via `saasaloy add`, which copies their own skills in.\n\n// wrangler and npm package names share this constraint.\nconst NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;\n\nexport interface InitOptions {\n name?: string;\n force: boolean;\n noInstall: boolean;\n noGit: boolean;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\n \"--force\",\n \"--no-install\",\n \"--no-git\",\n \"--help\",\n \"-h\",\n]);\nconst USAGE = \"saasaloy init [<name>] [--force] [--no-install] [--no-git]\";\nconst HELP: CommandHelp = {\n name: \"init\",\n describe: DESCRIPTIONS.init,\n usage: USAGE,\n flags: {\n \"--force\": \"scaffold into a directory that is not empty\",\n \"--no-install\": \"never run pnpm install, and never ask\",\n \"--no-git\": \"do not run git init in the new project\",\n },\n};\n\n/** Split `init`'s argv into options. Exported for its own tests, like `add`'s. */\nexport function parseArgs(argv: string[]): InitOptions {\n const positional: string[] = [];\n const unknown: string[] = [];\n for (const arg of argv) {\n if (!arg.startsWith(\"-\")) {\n positional.push(arg);\n } else if (!KNOWN_FLAGS.has(arg)) {\n unknown.push(arg);\n }\n }\n unknown.push(...positional.slice(1));\n return {\n force: argv.includes(\"--force\"),\n name: positional[0],\n // Skip the install prompt entirely and never run pnpm install — for scripted/CI\n // scaffolds (e.g. `pnpm play:init`) that manage installs themselves.\n noInstall: argv.includes(\"--no-install\"),\n noGit: argv.includes(\"--no-git\"),\n unknown,\n };\n}\n\ninterface RunResult {\n ok: boolean;\n message?: string;\n}\n\n// Run one child process in the scaffolded project. Output is buffered (not streamed)\n// so only the caller's spinner shows; both streams are captured so a failure can\n// report *why*. pnpm writes its `ERR_PNPM_*` diagnostics to stdout, not stderr,\n// so we keep both. Never throws — failures come back as { ok: false } so init\n// can carry on regardless.\nfunction run(command: string, args: string[], cwd: string): Promise<RunResult> {\n return new Promise((resolvePromise) => {\n // On Windows pnpm is `pnpm.cmd`, which bare spawn won't resolve — go via the shell there.\n const child = spawn(command, args, {\n cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n shell: process.platform === \"win32\",\n });\n let stdout = \"\";\n let stderr = \"\";\n child.stdout?.on(\"data\", (chunk) => {\n stdout += chunk.toString();\n });\n child.stderr?.on(\"data\", (chunk) => {\n stderr += chunk.toString();\n });\n child.on(\"error\", (err) => {\n // e.g. pnpm not on PATH.\n resolvePromise({ ok: false, message: err.message });\n });\n child.on(\"close\", (code) => {\n if (code === 0) {\n resolvePromise({ ok: true });\n } else {\n // Prefer pnpm's own diagnostics (stderr, then stdout); fall back to the code.\n const details = [stderr.trim(), stdout.trim()]\n .filter(Boolean)\n .join(\"\\n\");\n resolvePromise({\n ok: false,\n message:\n details ||\n `${command} ${args.join(\" \")} exited with code ${code ?? \"unknown\"}`,\n });\n }\n });\n });\n}\n\nfunction runPnpmInstall(cwd: string): Promise<RunResult> {\n return run(\"pnpm\", [\"install\"], cwd);\n}\n\n// Make the scaffold a git repository. ADR 0024, CONTRIBUTING and the QA plans all say\n// `init` does this, and until #98 none of them was true: husky's `prepare` hook has no\n// repository to install into, and turbo falls back to a global cache key that can serve\n// a stale build of a previous template. Skipped when the target already sits inside a\n// working tree — `saasaloy init .` inside an existing repo must not nest a second one.\nasync function initGitRepo(\n cwd: string\n): Promise<{ created: boolean; message?: string }> {\n const inside = await run(\"git\", [\"rev-parse\", \"--is-inside-work-tree\"], cwd);\n if (inside.ok) {\n return { created: false };\n }\n const result = await run(\"git\", [\"init\"], cwd);\n if (!result.ok) {\n return { created: false, message: result.message ?? \"git init failed\" };\n }\n return { created: true };\n}\n\ninterface SkillLinkResult {\n linked: string[];\n conflicts: string[];\n failures: { name: string; message: string }[];\n // `.agents/skills` itself couldn't be read, so no per-skill result is meaningful.\n // A *missing* directory isn't this: that's the ordinary \"template ships no skills\" case\n // and comes back as an empty result.\n unreadable?: string;\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n// Some of the base's skills run in an order — saasaloy-setup writes the brief that\n// saasaloy-landing-copy reads — and readdir's alphabetical order happens to invert exactly\n// that pair. There is no manifest to read a dependency out of, and inventing one for two\n// skills would cost more than it explains, so the order is listed here. Anything not named\n// keeps its directory order, after these.\nconst SKILL_ORDER = [\"saasaloy-setup\"];\n\nfunction bySkillOrder(a: string, b: string): number {\n const rank = (name: string) => {\n const at = SKILL_ORDER.indexOf(name);\n return at === -1 ? SKILL_ORDER.length : at;\n };\n return rank(a) - rank(b);\n}\n\n// Point `.claude/skills/<name>` at each skill the base ships, so Claude Code discovers it\n// the moment scaffolding finishes rather than after the owner reads AGENTS.md.\n//\n// The real files travel in `.agents/skills/` — readable by every agent, and committed,\n// because the template's .gitignore ignores `.claude/skills/` (the link is a per-machine\n// artefact, and a skill shipped there would land ignored and never reach the owner's repo).\n// ADR 0015 is the same arrangement `saasaloy add` uses for module skills; `init` calls the\n// two helpers directly rather than going through the applier, because the base has no\n// descriptor to plan from — its files are recorded straight off the render (ADR 0032).\n//\n// Never throws, and never fails `init`: a link is a convenience, and losing it costs\n// discovery, not files. A path already occupied by something that isn't ours is reported\n// and left exactly as it was.\nasync function linkAgentSkills(target: string): Promise<SkillLinkResult> {\n const result: SkillLinkResult = { linked: [], conflicts: [], failures: [] };\n const skillsDir = join(target, \".agents\", \"skills\");\n let names: string[];\n try {\n names = await readDirNames(skillsDir);\n } catch (error) {\n // readDirNames returns [] when the directory is simply absent, so reaching here means\n // it exists and we couldn't read it. Say so: the alternative is init printing no links\n // and no reason, which reads exactly like a template that ships no skills.\n result.unreadable = errorMessage(error);\n return result;\n }\n for (const name of names.toSorted(bySkillOrder)) {\n const linkAbs = join(target, \".claude\", \"skills\", name);\n const targetAbs = join(skillsDir, name);\n // classifyLink can reject after its lstat succeeds (the readlink behind it), so it\n // belongs inside the non-fatal path too — a link is a convenience, and no failure\n // here may take `init` down after the scaffold already landed.\n let state: LinkState;\n try {\n state = await classifyLink(linkAbs, targetAbs);\n } catch (error) {\n result.failures.push({ name, message: errorMessage(error) });\n continue;\n }\n if (state === \"conflict\") {\n result.conflicts.push(name);\n continue;\n }\n if (state === \"correct\") {\n result.linked.push(name);\n continue;\n }\n try {\n await createDirLink(linkAbs, targetAbs);\n result.linked.push(name);\n } catch (error) {\n result.failures.push({ name, message: errorMessage(error) });\n }\n }\n return result;\n}\n\nexport async function runInit(argv: string[]): Promise<number> {\n // Parse before help answers, so a typo'd flag alongside `--help` still reports the typo.\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n const { force, noInstall, noGit } = opts;\n let nameArg = opts.name;\n\n intro(pc.bgCyan(pc.black(\" saasaloy init \")));\n\n // `init` ignored every flag it did not know, while `add`/`remove`/`update` rejected\n // theirs — so `saasaloy init app --forse` scaffolded into a non-empty directory and\n // said nothing. One rule now (#98).\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n\n // No name given — ask for it rather than erroring out.\n if (!nameArg) {\n const answer = await text({\n message: \"Project name?\",\n placeholder: \"my-app (use `.` for the current directory)\",\n validate: (value) => {\n const trimmed = value?.trim() ?? \"\";\n if (!trimmed) {\n return \"Enter a project name (or `.` for the current directory).\";\n }\n // Mirror the arg path: name is the basename of the resolved target.\n const name = basename(resolve(process.cwd(), trimmed));\n if (!NAME_PATTERN.test(name)) {\n return \"Use lowercase letters, digits, and hyphens (e.g. my-app).\";\n }\n // clack accepts a value only on an explicit `undefined`, and consistent-return\n // rejects a bare `return`.\n // oxlint-disable-next-line unicorn/no-useless-undefined\n return undefined;\n },\n });\n if (isCancel(answer)) {\n cancel(\"init cancelled\");\n return EXIT_FAILURE;\n }\n nameArg = answer.trim();\n }\n\n // nameArg may be a bare name (`my-app`), `.`, or a path (`./apps/my-app`).\n const target = resolve(process.cwd(), nameArg);\n const projectName = basename(target);\n\n if (!NAME_PATTERN.test(projectName)) {\n cancel(\n `Invalid project name \"${projectName}\". Use lowercase letters, digits, and hyphens (e.g. my-app).`\n );\n return EXIT_REFUSED;\n }\n\n if (await pathExists(target)) {\n const entries = (await readdir(target)).filter((e) => e !== \".git\");\n if (entries.length > 0 && !force) {\n cancel(\n `Directory ${nameArg} is not empty. Re-run with --force to scaffold into it anyway.`\n );\n return EXIT_REFUSED;\n }\n }\n\n const s = spinner();\n s.start(`Scaffolding ${pc.cyan(projectName)}`);\n const templateDir = await baseTemplateDir();\n const written = await copyTemplate(\n templateDir,\n target,\n templateVars(projectName)\n );\n s.stop(\n `Scaffolded ${pc.cyan(projectName)} ${pc.dim(\"(apps/web · packages/ui · packages/tsconfig)\")}`\n );\n\n // Record what was just rendered: every file at the hash of its rendered bytes, and the\n // template's own hash beside the CLI version. These ledgers decide whether later\n // updates may overwrite files, so init must not report success if either write fails.\n // The project's own name, recorded once. `update` re-renders this template to compare\n // against and needs the same `{{PROJECT_NAME}}` it was rendered with here; without this\n // it fell back to the directory name, and a git worktree or a CI checkout path then\n // rewrote `package.json` `name`, `wrangler.jsonc` `name` and `siteName` (#144). The\n // template ships the file without a name so the file it ships stays schema-valid.\n try {\n const config = await loadConfig(target);\n await saveConfig(target, { ...config, name: projectName });\n const entry = written.find((file) => file.target === CONFIG_FILE);\n if (entry) {\n entry.hash = hashContent(await readFile(entry.path, \"utf-8\"));\n }\n } catch (error) {\n cancel(`Couldn't record the project name: ${errorMessage(error)}`);\n return EXIT_FAILURE;\n }\n\n try {\n const manifest = await loadManifest(target);\n const lock = await loadLock(target);\n recordBaseFiles(manifest, written);\n lock.base = baseRecord(\n await readVersion(),\n await templateHash(templateDir)\n );\n await saveManifest(target, manifest);\n await saveLock(target, lock);\n } catch (error) {\n cancel(\n `Couldn't record the base template: ${errorMessage(error)}\\n` +\n `The scaffold files remain, but init cannot safely complete without its project state.`\n );\n return EXIT_FAILURE;\n }\n\n // Before the install: husky's `prepare` script runs during `pnpm install` and needs a\n // repository to install its hooks into.\n if (!noGit) {\n const git = await initGitRepo(target);\n if (git.created) {\n logger.step(`Initialized a git repository ${pc.dim(\"(git init)\")}.`);\n } else if (git.message) {\n logger.warn(\n `Couldn't run ${pc.cyan(\"git init\")}: ${git.message} ` +\n `${pc.dim(\"(the scaffold is fine — run it yourself)\")}.`\n );\n }\n }\n\n const skills = await linkAgentSkills(target);\n if (skills.linked.length > 0) {\n const lines = skills.linked.map(\n (name) =>\n `${pc.cyan(`.claude/skills/${name}`)} ${pc.dim(`→ .agents/skills/${name}`)}`\n );\n note(\n wrapForNote(\n `${lines.join(\"\\n\")}\\n\\n${pc.dim(\"Symlinked for Claude Code — the skill files live in `.agents/skills/`.\")}`\n ),\n \"Skill links\"\n );\n }\n if (skills.unreadable) {\n logger.warn(\n `Couldn't read ${pc.cyan(\".agents/skills\")}: ${skills.unreadable} ` +\n `${pc.dim(\"(the skill files are still there — fix the permissions and re-run init to link them)\")}.`\n );\n }\n for (const name of skills.conflicts) {\n logger.warn(\n `Skill link ${pc.cyan(`.claude/skills/${name}`)} already exists and isn't ours — ` +\n `left untouched ${pc.dim(\"(remove it, then re-run to link the skill)\")}.`\n );\n }\n for (const failure of skills.failures) {\n logger.warn(\n `Couldn't link skill ${pc.cyan(failure.name)}: ${failure.message} ` +\n `${pc.dim(\"(the skill files are still in `.agents/skills/`)\")}.`\n );\n }\n\n // Offer to install now; on decline (or cancel) fall back to the printed steps.\n // `select` (not `confirm`) so each choice renders on its own line.\n let installed = false;\n const wantsInstall = noInstall\n ? false\n : await select({\n message: \"Install dependencies now?\",\n options: [\n { value: true, label: `Yes, run ${pc.cyan(\"pnpm install\")}` },\n { value: false, label: \"No, I'll run it later\" },\n ],\n initialValue: true,\n });\n if (!isCancel(wantsInstall) && wantsInstall) {\n const install = spinner();\n install.start(`Installing dependencies ${pc.dim(\"(pnpm install)\")}`);\n const result = await runPnpmInstall(target);\n if (result.ok) {\n installed = true;\n install.stop(`Installed dependencies ${pc.dim(\"(pnpm install)\")}`);\n } else {\n // Don't break the flow — report and let the user finish it by hand.\n install.stop(pc.yellow(\"pnpm install did not finish\"));\n logger.warn(\n `Couldn't install dependencies automatically — run ${pc.cyan(\"pnpm install\")} yourself.`\n );\n if (result.message) {\n // Show pnpm's own diagnostics inside a box, tail-trimmed and soft-wrapped to\n // the terminal width so a long line (e.g. a registry URL) can't break the rail.\n const lines = stripAnsi(result.message)\n .split(\"\\n\")\n .filter((line) => line.trim() !== \"\");\n // Color each line individually: clack's `note` splits on \\n and prefixes each\n // line, so a single wrapping color would only tint the first one.\n const body = wrapForNote(lines.slice(-12).join(\"\\n\"))\n .split(\"\\n\")\n .map((line) => pc.dim(pc.red(line)))\n .join(\"\\n\");\n note(body, \"pnpm install output\");\n }\n }\n }\n\n const steps = [\n nameArg === \".\" ? null : `cd ${nameArg}`,\n installed ? null : \"pnpm install\",\n `pnpm dev ${pc.dim(\"# run dev servers\")}`,\n // The skills the base ships are the fastest way to make the scaffold yours, and an\n // owner who never opens AGENTS.md would otherwise not know they exist.\n // Pad to the same column the commands above comment at (`pnpm dev` + 21 spaces).\n ...skills.linked.map(\n (name) => `${`/${name}`.padEnd(29)}${pc.dim(\"# in Claude Code\")}`\n ),\n ]\n .filter((line): line is string => line !== null)\n .map((line) => pc.cyan(line))\n .join(\"\\n\");\n\n note(steps, \"Next steps\");\n outro(pc.green(`🎉 Created ${projectName} successfully.`));\n return EXIT_OK;\n}\n","// Console reporter, now grafted onto @clack/prompts' `log.*` (connected-rail\n// output) with picocolors for small accents. The call sites are unchanged from\n// the original dependency-free shim — same `info/step/success/warn/error` shape.\n\nimport { log } from \"@clack/prompts\";\nimport pc from \"picocolors\";\n\nexport const logger = {\n error(message: string): void {\n log.error(message);\n },\n info(message: string): void {\n log.info(message);\n },\n step(message: string): void {\n // clack's `log.step` prefixes a green rail marker; keep the copy dim so\n // these read as sub-lines under a heading, matching the old two-space indent.\n log.step(pc.dim(message));\n },\n success(message: string): void {\n log.success(message);\n },\n warn(message: string): void {\n log.warn(message);\n },\n};\n","import { cancel, intro, note, outro } from \"@clack/prompts\";\nimport { join } from \"node:path\";\nimport pc from \"picocolors\";\nimport {\n EXIT_OK,\n EXIT_REFUSED,\n exitCodeFor,\n formatFailure,\n} from \"../lib/exit.js\";\nimport { pathExists } from \"../lib/fs-utils.js\";\nimport { findProjectRoot } from \"../lib/project.js\";\nimport {\n createRegistrySource,\n parseCoordinate,\n REGISTRY_ENV,\n} from \"../lib/registry.js\";\nimport type { RegistrySource } from \"../lib/registry.js\";\nimport { CONFIG_FILE, loadConfig } from \"../lib/saasaloy-config.js\";\nimport { wrapForNote } from \"../lib/tui.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\n\n// `saasaloy list [owner/repo[@ref]]` — enumerate the modules a registry offers, the same\n// seam `add`'s picker reads from. With no argument it lists the default repo (or the local\n// modules/ checkout when SAASALOY_REGISTRY_DIR is set); an `owner/repo` coordinate lists a\n// third-party registry. Names only — matching the picker — so it stays a cheap, one-call\n// lookup (no per-module descriptor fetch).\n//\n// Run inside a project, each name is marked installed or not, and anything installed the\n// registry no longer offers is listed under its own heading. Before #98 the output was a\n// flat list a user had to diff against saasaloy.json by hand.\n\ninterface Options {\n source?: string;\n /** Show only what this project has installed. */\n installed: boolean;\n /** Show only what the registry offers and this project has not installed. */\n available: boolean;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\"--installed\", \"--available\", \"--help\", \"-h\"]);\nconst USAGE = \"saasaloy list [<owner/repo[@ref]>] [--installed] [--available]\";\nconst HELP: CommandHelp = {\n name: \"list\",\n describe: DESCRIPTIONS.list,\n usage: USAGE,\n flags: {\n \"--installed\": \"list only the modules this project has installed\",\n \"--available\": \"list only the modules this project has not installed\",\n },\n};\n\nfunction parseArgs(argv: string[]): Options {\n const positional: string[] = [];\n const unknown: string[] = [];\n for (const arg of argv) {\n if (!arg.startsWith(\"-\")) {\n positional.push(arg);\n } else if (!KNOWN_FLAGS.has(arg)) {\n unknown.push(arg);\n }\n }\n unknown.push(...positional.slice(1));\n return {\n available: argv.includes(\"--available\"),\n installed: argv.includes(\"--installed\"),\n source: positional[0],\n unknown,\n };\n}\n\ninterface LocalProject {\n /** Modules `saasaloy add` applied here. */\n installed: Set<string>;\n /** The base app `init` scaffolded, which is not a module and is in no registry. */\n base?: string;\n}\n\n/**\n * What this project has, or nothing when `list` is run outside one. Listing a registry\n * from anywhere is the point of the command, so a missing `saasaloy.json` is not an error\n * here — it only means nothing can be marked.\n *\n * Test for the file rather than catching whatever `loadConfig` throws. A bare catch also\n * swallowed a corrupt or schema-invalid `saasaloy.json`, so a project whose config would\n * stop every other command read here as \"not in a project\" and quietly lost its installed\n * marks. Only absence is tolerated; a file that is there and will not load is reported\n * through the caller's own error path (#98).\n */\nasync function localProject(): Promise<LocalProject> {\n const root = await findProjectRoot();\n if (!(await pathExists(join(root, CONFIG_FILE)))) {\n return { installed: new Set() };\n }\n const config = await loadConfig(root);\n return {\n installed: new Set(config.installed),\n ...(config.base === undefined ? {} : { base: config.base }),\n };\n}\n\nexport async function runList(argv: string[]): Promise<number> {\n // Parse before help answers, so a typo'd flag alongside `--help` still reports the typo.\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n intro(pc.bgCyan(pc.black(\" saasaloy list \")));\n\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n if (opts.installed && opts.available) {\n cancel(\n `\\`--installed\\` and \\`--available\\` exclude each other — pass neither to see both.`\n );\n return EXIT_REFUSED;\n }\n\n let source: RegistrySource | undefined;\n try {\n const coord = parseCoordinate(opts.source);\n source = createRegistrySource(coord);\n if (process.env[REGISTRY_ENV] && (coord.owner || coord.repo)) {\n note(\n `Ignoring source \"${coord.owner}/${coord.repo}\" — ${REGISTRY_ENV} override is set.`,\n pc.yellow(\"Warning\")\n );\n }\n\n const project = await localProject();\n const installed = project.installed;\n const modules = await source.listModules();\n if (modules.length === 0) {\n note(`No modules found in ${source.label}.`, \"Registry\");\n outro(pc.dim(\"0 modules\"));\n return EXIT_OK;\n }\n\n const offeredAndInstalled = modules.filter((name) => installed.has(name));\n const offeredOnly = modules.filter((name) => !installed.has(name));\n // Installed here but absent from this registry: a module from another registry, or\n // one renamed upstream. Say so rather than omitting it. The base app is not in this\n // list — it is not a module, so no registry ever offers it.\n const elsewhere = [...installed]\n .filter((name) => !modules.includes(name))\n .toSorted();\n\n const shown: string[] = [];\n if (!opts.available) {\n shown.push(...offeredAndInstalled);\n }\n if (!opts.installed) {\n shown.push(...offeredOnly);\n }\n\n const lines = shown.map((name) =>\n installed.has(name)\n ? `${pc.green(\"✔\")} ${pc.cyan(name)} ${pc.dim(\"installed\")}`\n : `${pc.dim(\"·\")} ${pc.cyan(name)}`\n );\n if (lines.length === 0) {\n lines.push(\n pc.dim(\n opts.installed\n ? \"nothing from this registry is installed here\"\n : \"every module this registry offers is already installed\"\n )\n );\n }\n if (elsewhere.length > 0 && !opts.available) {\n lines.push(\n \"\",\n pc.dim(`installed but not in this registry: ${elsewhere.join(\", \")}`)\n );\n }\n if (project.base && !opts.available && !opts.installed) {\n lines.push(\n \"\",\n pc.dim(\n `base app: ${project.base} — scaffolded by \\`saasaloy init\\`, not a module`\n )\n );\n }\n\n note(\n wrapForNote(lines.join(\"\\n\")),\n `Modules ${pc.dim(`(${source.label})`)}`\n );\n const total = shown.length;\n outro(\n pc.dim(\n `${total} module${total === 1 ? \"\" : \"s\"}${\n opts.installed || opts.available\n ? \"\"\n : ` · ${offeredAndInstalled.length} installed`\n }`\n )\n );\n return EXIT_OK;\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n } finally {\n await source?.cleanup?.();\n }\n}\n","import { join, relative, resolve } from \"node:path\";\nimport {\n cancel,\n intro,\n isCancel,\n note,\n outro,\n select,\n text,\n} from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { checkTarget, resolveDoctorTarget } from \"../lib/doctor.js\";\nimport type { Finding } from \"../lib/doctor.js\";\nimport {\n EXIT_OK,\n EXIT_REFUSED,\n exitCodeFor,\n formatFailure,\n} from \"../lib/exit.js\";\nimport { pathExists } from \"../lib/fs-utils.js\";\nimport {\n isModuleType,\n MODULE_TYPES,\n nameProblem,\n parseDependsOn,\n REGISTRY_DIR,\n requiresRange,\n TYPE_HINTS,\n writeModule,\n} from \"../lib/new-module.js\";\nimport type { ModuleSpec, ModuleType } from \"../lib/new-module.js\";\nimport { findProjectRoot } from \"../lib/project.js\";\nimport { CONFIG_FILE } from \"../lib/saasaloy-config.js\";\nimport { isInteractive, wrapForNote } from \"../lib/tui.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { readVersion } from \"../version.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\n\n// `saasaloy new module <name>` — the registry author's scaffold (#50).\n//\n// Before this the only way to start a module was the `create-module` skill, which made\n// authoring a Claude Code feature rather than a CLI one. That is the wrong shape for a\n// registry meant to take third-party modules: the skeleton is mechanical, so the tool\n// writes it and the skill keeps the judgment about tiers, conventions and slices.\n//\n// It refuses inside a generated project. `modules/` in a consumer's repo means nothing —\n// a project installs modules, it does not host them — and a scaffold landing there is a\n// folder the user finds later with no idea what put it there.\n//\n// The noun is a positional rather than a second registry key, so `COMMANDS` stays the flat\n// map `cli.ts` renders help and the picker from. `module` is the only noun today.\n\n/** The nouns `new` knows. Named in the refusal, so adding one updates the message too. */\nconst NOUNS = [\"module\"] as const;\n\nexport interface Options {\n /** The noun — `module`. Absent when the picker handed off with an empty argv. */\n noun?: string;\n name?: string;\n /** `--type <tier>`: skips the two-tier prompt, for a non-interactive run. */\n type?: string;\n /** `--depends-on <a,b>`: skips the dependency prompt. */\n dependsOn?: string;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\"--help\", \"-h\"]);\nconst VALUE_FLAGS = new Set([\"--type\", \"--depends-on\"]);\nconst USAGE = \"saasaloy new module <name> [--type <tier>] [--depends-on <a,b>]\";\nconst HELP: CommandHelp = {\n name: \"new\",\n describe: DESCRIPTIONS.new,\n usage: USAGE,\n flags: {\n \"--type <tier>\": `${MODULE_TYPES.join(\" | \")} — skips the prompt`,\n \"--depends-on <a,b>\":\n \"comma-separated capabilities this module needs — skips the prompt\",\n },\n};\n\nexport function parseArgs(argv: string[]): Options {\n const positional: string[] = [];\n const unknown: string[] = [];\n const values: Record<string, string> = {};\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!;\n if (!arg.startsWith(\"-\")) {\n positional.push(arg);\n continue;\n }\n // `--type feature` and `--type=feature` both work, and a value flag with nothing\n // usable after it is a usage error rather than a silently empty tier — otherwise\n // `--type --depends-on api` scaffolds a module whose type is \"--depends-on\".\n const eq = arg.indexOf(\"=\");\n const flag = eq === -1 ? arg : arg.slice(0, eq);\n if (VALUE_FLAGS.has(flag)) {\n const next = eq === -1 ? argv[i + 1] : arg.slice(eq + 1);\n if (!next || (eq === -1 && next.startsWith(\"-\"))) {\n unknown.push(`${flag} (missing value)`);\n continue;\n }\n if (eq === -1) {\n i++;\n }\n values[flag] = next;\n continue;\n }\n if (!KNOWN_FLAGS.has(arg)) {\n unknown.push(arg);\n }\n }\n // Two positionals are the whole grammar: the noun and the name.\n unknown.push(...positional.slice(2));\n\n return {\n ...(positional[0] === undefined ? {} : { noun: positional[0] }),\n ...(positional[1] === undefined ? {} : { name: positional[1] }),\n ...(values[\"--type\"] === undefined ? {} : { type: values[\"--type\"] }),\n ...(values[\"--depends-on\"] === undefined\n ? {}\n : { dependsOn: values[\"--depends-on\"] }),\n unknown,\n };\n}\n\n/** One doctor finding as a line, in the shape `saasaloy doctor` prints. */\nexport function renderFindings(findings: Finding[]): string[] {\n return findings.map(\n (found) => `${pc.yellow(found.where)} ${pc.dim(found.message)}`\n );\n}\n\nexport async function runNew(argv: string[]): Promise<number> {\n // Parse before help answers, so a typo'd flag alongside `--help` still reports the typo.\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n intro(pc.bgCyan(pc.black(\" saasaloy new \")));\n\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n\n // The picker hands off an empty argv, and `module` is the only noun there is, so an\n // absent noun is the one the user meant rather than a question worth asking.\n const noun = opts.noun ?? NOUNS[0];\n if (!(NOUNS as readonly string[]).includes(noun)) {\n cancel(\n `\\`saasaloy new ${noun}\\` isn't a thing — the noun is one of: ${NOUNS.join(\", \")}.`\n );\n return EXIT_REFUSED;\n }\n\n try {\n // A generated project carries `saasaloy.json`, at its root or above the current\n // directory. `new module` authors a *registry* module, so landing one in a consumer's\n // repo would write a folder nothing there reads.\n const projectRoot = await findProjectRoot();\n if (await pathExists(join(projectRoot, CONFIG_FILE))) {\n cancel(\n `${CONFIG_FILE} is present at ${pc.cyan(projectRoot)} — this is a generated project, and ` +\n `\\`new module\\` authors a module for a registry. Run it in the registry repo instead. Nothing was written.`\n );\n return EXIT_REFUSED;\n }\n\n let name = opts.name;\n if (name === undefined) {\n if (!isInteractive()) {\n cancel(`No module name — usage: \\`${USAGE}\\`.`);\n return EXIT_REFUSED;\n }\n const answer = await text({\n message: \"Module name (lowercase, digits, hyphens)\",\n validate: (value) => nameProblem((value ?? \"\").trim()),\n });\n if (isCancel(answer)) {\n cancel(\"Cancelled — nothing written.\");\n return EXIT_REFUSED;\n }\n name = answer.trim();\n }\n\n const problem = nameProblem(name);\n if (problem) {\n cancel(`${problem} Nothing was written.`);\n return EXIT_REFUSED;\n }\n\n // Prove the folder is free before asking anything: a scaffold that merges into a\n // descriptor somebody wrote would overwrite it, and answering three prompts first\n // only to be refused wastes them.\n const registryDir = resolve(process.cwd(), REGISTRY_DIR);\n const dir = join(registryDir, name);\n if (await pathExists(dir)) {\n cancel(\n `${pc.cyan(`${REGISTRY_DIR}/${name}`)} already exists — pick another name, or edit it by hand. Nothing was written.`\n );\n return EXIT_REFUSED;\n }\n\n let type: ModuleType;\n if (opts.type !== undefined) {\n if (!isModuleType(opts.type)) {\n cancel(\n `\\`--type ${opts.type}\\` isn't a tier — use ${MODULE_TYPES.join(\" or \")}.`\n );\n return EXIT_REFUSED;\n }\n type = opts.type;\n } else if (isInteractive()) {\n const picked = await select({\n message: \"Which tier is this?\",\n options: MODULE_TYPES.map((value) => ({\n hint: TYPE_HINTS[value],\n label: value,\n value,\n })),\n });\n if (isCancel(picked)) {\n cancel(\"Cancelled — nothing written.\");\n return EXIT_REFUSED;\n }\n type = picked;\n } else {\n // No terminal to ask on: a prompt nobody can answer is a hang, and a hang in CI is\n // worse than a refusal that names the flag carrying the same answer.\n cancel(\n `No tier — pass ${MODULE_TYPES.map((tier) => `\\`--type ${tier}\\``).join(\" or \")} when there is no terminal to ask.`\n );\n return EXIT_REFUSED;\n }\n\n let dependsOn: string[] = [];\n if (opts.dependsOn !== undefined) {\n dependsOn = parseDependsOn(opts.dependsOn);\n } else if (isInteractive()) {\n const answer = await text({\n defaultValue: \"\",\n message: `Capabilities it depends on ${pc.dim(\"(comma-separated, blank for none)\")}`,\n placeholder: \"api, database\",\n });\n if (isCancel(answer)) {\n cancel(\"Cancelled — nothing written.\");\n return EXIT_REFUSED;\n }\n dependsOn = parseDependsOn(answer);\n }\n\n const requires = requiresRange(await readVersion());\n const spec: ModuleSpec = {\n dependsOn,\n name,\n type,\n ...(requires === undefined ? {} : { requires }),\n };\n const written = await writeModule(dir, spec);\n\n const shown = relative(process.cwd(), dir) || dir;\n note(\n wrapForNote(\n written\n .map((path) => `${pc.green(\"create\")} ${shown}/${path}`)\n .join(\"\\n\")\n ),\n \"Written\"\n );\n\n // Run the same checks `saasaloy doctor` runs, in-process. Shelling out to the binary\n // would need one installed, and the scaffold's whole promise is that what it writes\n // passes — proving it here means a change to either side is caught by the other.\n const reports = await checkTarget(await resolveDoctorTarget(dir));\n const findings = reports.flatMap((report) => report.findings);\n if (findings.length > 0) {\n // Reported, not refused: the files are on disk and correct enough to edit, and the\n // usual cause is a `dependsOn` naming a module this registry does not offer — the\n // author's own input, which they fix in the descriptor rather than by rerunning.\n note(wrapForNote(renderFindings(findings).join(\"\\n\")), \"Doctor\");\n outro(\n pc.yellow(\n `Scaffolded with ${findings.length} finding${findings.length === 1 ? \"\" : \"s\"} — fix ${shown}/registry-item.json, then \\`saasaloy doctor ${shown}\\`.`\n )\n );\n return EXIT_OK;\n }\n\n outro(\n pc.green(\n `${name} is ready — put its payload in ${shown}/files/ and write its runbook in ${shown}/skills/.`\n )\n );\n return EXIT_OK;\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n }\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { UNKNOWN_VERSION } from \"./cli-requires.js\";\nimport { parseVersion } from \"./semver.js\";\n\n// The scaffold behind `saasaloy new module <name>` (#50).\n//\n// Authoring a module was Claude-Code-only before this: the `create-module` skill knew the\n// shape of a descriptor and nothing else did, which locked out every contributor not\n// running that agent. The skeleton is mechanical — a descriptor, a `files/` folder, a\n// prefixed skill — so the CLI writes it and the skill keeps the part that is judgment.\n//\n// One scaffolder, not two. `create-module` now runs this command for the skeleton, so a\n// change to the shape lands here and both paths get it.\n//\n// Everything is pure up to `writeModule`, so the descriptor and the skill are asserted as\n// strings in a test with no directory to inspect afterwards.\n\n/** The two tiers a descriptor's `type` may name, in the order the picker offers them. */\nexport const MODULE_TYPES = [\n \"saasaloy:capability\",\n \"saasaloy:feature\",\n] as const;\n\nexport type ModuleType = (typeof MODULE_TYPES)[number];\n\n/** What each tier is, for the picker's hint and for the refusal that lists them. */\nexport const TYPE_HINTS: Record<ModuleType, string> = {\n \"saasaloy:capability\":\n \"scaffolds an app or package and establishes an extension point\",\n \"saasaloy:feature\":\n \"drops files into the conventions existing capabilities already establish\",\n};\n\nexport function isModuleType(value: string): value is ModuleType {\n return (MODULE_TYPES as readonly string[]).includes(value);\n}\n\n// The same pattern `registry-item.schema.json` puts on `name`. Checked before anything is\n// written, so a rejected name costs no directory to clean up.\nconst NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;\n\nexport function isValidModuleName(name: string): boolean {\n return NAME_PATTERN.test(name);\n}\n\n/** Why a name cannot be used, as a sentence, or `undefined` when it can. */\nexport function nameProblem(name: string): string | undefined {\n return isValidModuleName(name)\n ? undefined\n : `\"${name}\" isn't a module name — use lowercase letters, digits and hyphens, starting with a letter or digit.`;\n}\n\n/** Every module skill folder is prefixed, so two registries' skills never collide (ADR 0014). */\nexport const SKILL_PREFIX = \"saasaloy-\";\n\n/** The registry folder a module is written into, relative to the current directory. */\nexport const REGISTRY_DIR = \"modules\";\n\n/**\n * The `requires.saasaloy` range a freshly scaffolded descriptor declares, or `undefined`\n * when it should declare none.\n *\n * The floor is the running CLI's own minor, because that is the only version the author\n * has actually run the module against. A wider range would be a claim nobody checked.\n *\n * Two versions produce no range at all. `\"unknown\"` means the CLI could not read its own\n * package.json, and a floor invented from nothing would refuse every consumer. `0.0.x` is\n * the unpublished placeholder this repo still carries, and `>=0.0` constrains nothing, so\n * the field is left out rather than written as decoration.\n */\nexport function requiresRange(cliVersion: string): string | undefined {\n if (cliVersion === UNKNOWN_VERSION) {\n return undefined;\n }\n const parsed = parseVersion(cliVersion);\n if (!parsed || (parsed.major === 0 && parsed.minor === 0)) {\n return undefined;\n }\n return `>=${parsed.major}.${parsed.minor}`;\n}\n\nexport interface ModuleSpec {\n name: string;\n type: ModuleType;\n /** Capabilities this module needs first. Empty means the field is left out entirely. */\n dependsOn: string[];\n /** The `requires.saasaloy` range, from `requiresRange`. Omitted when undefined. */\n requires?: string;\n}\n\n/**\n * The descriptor as JSON text.\n *\n * Every optional field the scaffold has nothing to say about is left out rather than\n * written empty. `additionalProperties: false` means a placeholder is a lie the schema\n * cannot catch, and an author deleting `\"envVars\": {}` is work the scaffold created.\n * Key order follows the schema's own: identity, then requirements, then contents.\n */\nexport function renderDescriptor(spec: ModuleSpec): string {\n const item: Record<string, unknown> = {\n $schema: \"https://saasaloy.dev/schemas/registry-item.schema.json\",\n name: spec.name,\n type: spec.type,\n };\n if (spec.requires) {\n item.requires = { saasaloy: spec.requires };\n }\n if (spec.dependsOn.length > 0) {\n item.dependsOn = spec.dependsOn;\n }\n // The skill folder is scaffolded alongside, so the descriptor points at it from the\n // start — a module that ships a runbook nothing references is the easy thing to forget.\n item.agent = { skills: [`skills/${SKILL_PREFIX}${spec.name}`] };\n return `${JSON.stringify(item, null, 2)}\\n`;\n}\n\nconst TIER_WORD: Record<ModuleType, string> = {\n \"saasaloy:capability\": \"capability\",\n \"saasaloy:feature\": \"feature\",\n};\n\n/**\n * The skill stub, in the frontmatter shape every module's `SKILL.md` already uses: a\n * `name` identical to the folder, and a `description` saying when to load it. The body is\n * a checklist of the sections a module runbook owes its reader, because a stub with\n * headings and no prose is easier to finish than a blank page.\n */\nexport function renderSkill(spec: ModuleSpec): string {\n const tier = TIER_WORD[spec.type];\n const depends =\n spec.dependsOn.length > 0\n ? `It depends on ${spec.dependsOn.join(\", \")}, which \\`add\\` resolves and installs first.`\n : `It declares no \\`dependsOn\\` yet — add every capability it needs before publishing.`;\n return `---\nname: ${SKILL_PREFIX}${spec.name}\ndescription: Runbook for the ${spec.name} module. Use when adding, changing, or debugging ${spec.name} in a Saasaloy project. TODO — describe what this module does and name the files and settings it owns, so the agent loads this on the right task.\n---\n\n# ${spec.name} — TODO one-line summary\n\n\\`${spec.name}\\` is a **${tier} module** (\\`${spec.type}\\`). ${depends}\n\nTODO — say in two sentences what a consumer gets from installing it.\n\n## What it installs\n\nTODO — list each file this module writes and where it lands. Keep it in step with\n\\`registry-item.json\\`'s \\`files[]\\`; this table is what a reader checks before editing one.\n\n| File | Lands at |\n| --- | --- |\n| TODO | TODO |\n\n## Wire-up\n\nRequired only when this module targets \\`@ui/blocks/\\`, because \\`add\\` writes the block and\nnever edits a page. Name four things: the file to edit, the import line verbatim, the tag\nwith its client directive, and a suggested anchor marked as a suggestion. Delete this\nsection when the module ships no block.\n\n## Settings\n\nTODO — every key in \\`envVars\\`, what it is for, and what \\`saasaloy env\\` does with it.\nDelete this section when the module declares none.\n\n## What \\`remove\\` leaves behind\n\nTODO — anything the user has to undo by hand, such as an import they added during wire-up.\n`;\n}\n\n/** One file the scaffold writes: a module-relative POSIX path and its content. */\nexport interface ScaffoldedFile {\n path: string;\n content: string;\n}\n\n/**\n * Every file the scaffold writes, in the order it writes them. Exported apart from the\n * write so the layout is asserted without a temp directory.\n *\n * `files/.gitkeep` is deliberate: the folder is where the payload goes, and git will not\n * carry an empty directory, so the author would otherwise have to create it themselves\n * before the first file.\n */\nexport function moduleFiles(spec: ModuleSpec): ScaffoldedFile[] {\n return [\n { content: renderDescriptor(spec), path: \"registry-item.json\" },\n { content: \"\", path: \"files/.gitkeep\" },\n {\n content: renderSkill(spec),\n path: `skills/${SKILL_PREFIX}${spec.name}/SKILL.md`,\n },\n ];\n}\n\n/**\n * Write the scaffold under `dir` and return the paths written, module-relative.\n *\n * The caller proves `dir` does not exist first: this refuses to merge into a module folder\n * that is already there, since overwriting a descriptor somebody wrote is not a scaffold.\n */\nexport async function writeModule(\n dir: string,\n spec: ModuleSpec\n): Promise<string[]> {\n const files = moduleFiles(spec);\n for (const file of files) {\n const abs = join(dir, ...file.path.split(\"/\"));\n await mkdir(dirname(abs), { recursive: true });\n await writeFile(abs, file.content, \"utf-8\");\n }\n return files.map((file) => file.path);\n}\n\n/**\n * Split a `--depends-on` value into module names. Both `a,b` and `a, b` are accepted, and\n * an empty segment is dropped rather than becoming a `dependsOn` entry named `\"\"` that\n * doctor then reports as a module the registry does not offer.\n */\nexport function parseDependsOn(raw: string): string[] {\n return raw\n .split(\",\")\n .map((name) => name.trim())\n .filter((name) => name !== \"\");\n}\n","import { cancel, intro, log, note, outro } from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport {\n EXIT_OK,\n EXIT_REFUSED,\n exitCodeFor,\n formatFailure,\n} from \"../lib/exit.js\";\nimport { BASE_MODULE, missingBaseTargets, templateHash } from \"../lib/base.js\";\nimport { loadLock } from \"../lib/lock.js\";\nimport { loadManifest } from \"../lib/manifest.js\";\nimport { findProjectRoot } from \"../lib/project.js\";\nimport { REGISTRY_ENV, RemoteRegistrySource } from \"../lib/registry.js\";\nimport type { RegistrySource } from \"../lib/registry.js\";\nimport { loadConfig } from \"../lib/saasaloy-config.js\";\nimport { baseTemplateDir } from \"../lib/scaffold.js\";\nimport { wrapForNote } from \"../lib/tui.js\";\nimport { compareBase, compareInstalled } from \"../lib/updater.js\";\nimport type { ModuleComparison, UpdateStatus } from \"../lib/updater.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { readVersion } from \"../version.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\n\n// `saasaloy outdated` — \"has anything moved?\", answered without touching a file (#50).\n//\n// The lock already records the exact SHA every installed module resolved to, so the\n// answer is one `compareInstalled` call away — the same call `update` opens with. Before\n// this command there was no way to ask, which made `update` a thing you ran speculatively\n// and then read a merge plan to find out it had nothing to do.\n//\n// It is a report, so a bare run exits 0 whatever it finds: \"three modules moved\" is news,\n// not a failure. `--check` is the CI form — it exits 2 when anything is outdated, so a\n// pipeline can gate on drift without parsing the table.\n//\n// An unreachable source is a row, never a throw. One dead repo must not hide the state of\n// every other module, and it does not fail `--check` either: a network blip is not drift.\n//\n// The base template is the first row (#120): the lock's `templateHash` against the one\n// the running CLI ships, with the two CLI versions as labels. An `untracked` base — a\n// project scaffolded before the record existed — is news, not drift, and `update` is\n// what records it; this command never writes.\n\nexport interface Options {\n /** `--check`: exit non-zero when any module has moved, for CI. */\n check: boolean;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\"--check\", \"--help\", \"-h\"]);\nconst USAGE = \"saasaloy outdated [--check]\";\nconst HELP: CommandHelp = {\n name: \"outdated\",\n describe: DESCRIPTIONS.outdated,\n usage: USAGE,\n flags: {\n \"--check\":\n \"exit non-zero when the base template or any module has moved (for CI)\",\n },\n};\n\nexport function parseArgs(argv: string[]): Options {\n const unknown: string[] = [];\n for (const arg of argv) {\n // The report covers every installed module; there is no single-module form to name.\n if (!arg.startsWith(\"-\") || !KNOWN_FLAGS.has(arg)) {\n unknown.push(arg);\n }\n }\n return { check: argv.includes(\"--check\"), unknown };\n}\n\nconst SHA_PATTERN = /^[0-9a-f]{40}$/i;\n\n/** A 40-hex value is a commit; anything else (`local`, `unknown`) is printed as it stands. */\nfunction short(value: string): string {\n return SHA_PATTERN.test(value) ? value.slice(0, 7) : value;\n}\n\n// Colour carries the same meaning it does in `update`'s summary: green is settled, yellow\n// is work waiting, red is a question the tool could not answer, dim is deliberately frozen.\nconst STATUS_COLOR: Record<UpdateStatus, (text: string) => string> = {\n current: pc.green,\n outdated: pc.yellow,\n pinned: pc.dim,\n local: pc.dim,\n unresolvable: pc.red,\n untracked: pc.yellow,\n};\n\nconst HEADERS = [\"MODULE\", \"STATUS\", \"REF\", \"CURRENT\", \"LATEST\"] as const;\n\nfunction cells(comparison: ModuleComparison): string[] {\n return [\n comparison.name,\n comparison.status,\n short(comparison.ref),\n short(comparison.current),\n short(comparison.latest),\n ];\n}\n\n/**\n * The table, one row per module, plus an indented note under every row carrying a\n * `detail` — which is every status but `current` and `outdated`, so a `local` entry says\n * it was skipped and an unreachable one says why.\n *\n * Pure and exported so the renderer is tested against a stubbed comparison covering all\n * five statuses, with no network and no project on disk. Alignment is measured on the\n * plain strings and colour applied after, or the escape codes would count as width.\n */\nexport function renderComparisons(comparisons: ModuleComparison[]): string[] {\n if (comparisons.length === 0) {\n return [pc.dim(\"No modules installed — `saasaloy add <module>` first.\")];\n }\n\n const rows = comparisons.map(cells);\n const widths = HEADERS.map((header, column) =>\n Math.max(header.length, ...rows.map((row) => row[column]?.length ?? 0))\n );\n const pad = (text: string, column: number): string =>\n text.padEnd(widths[column] ?? 0);\n\n const lines = [\n pc.dim(\n HEADERS.map((header, column) => pad(header, column))\n .join(\" \")\n .trimEnd()\n ),\n ];\n for (const [index, row] of rows.entries()) {\n const comparison = comparisons[index]!;\n const color = STATUS_COLOR[comparison.status];\n lines.push(\n [\n pc.cyan(pad(row[0]!, 0)),\n color(pad(row[1]!, 1)),\n pc.dim(pad(row[2]!, 2)),\n pad(row[3]!, 3),\n comparison.status === \"outdated\"\n ? pc.yellow(pad(row[4]!, 4))\n : pad(row[4]!, 4),\n ]\n .join(\" \")\n .trimEnd()\n );\n if (comparison.detail) {\n // A leading-space indent would not survive `wrapForNote`, which splits on spaces\n // and drops the empty leading words. A glyph carries the \"belongs to the row above\"\n // relationship through the wrap.\n lines.push(pc.dim(` ↳ ${comparison.detail}`));\n }\n }\n return lines;\n}\n\n/**\n * How many rows moved, the base included. Only `outdated` counts: a pinned module is\n * frozen on purpose, a local one has no commit to compare, an unresolvable one is a\n * failure to answer rather than an answer, and an untracked base has nothing to compare\n * yet — gating CI on any of the four would fail builds for no drift.\n */\nexport function countDrift(comparisons: ModuleComparison[]): number {\n return comparisons.filter((c) => c.status === \"outdated\").length;\n}\n\n/** The closing line for a report with drift: which of the base and the modules moved. */\nexport function describeDrift(comparisons: ModuleComparison[]): string {\n const baseMoved = comparisons.some(\n (c) => c.name === BASE_MODULE && c.status === \"outdated\"\n );\n const modules = comparisons.filter(\n (c) => c.name !== BASE_MODULE && c.status === \"outdated\"\n ).length;\n const moduleText = `${modules} module${modules === 1 ? \"\" : \"s\"}`;\n const subject = baseMoved\n ? modules > 0\n ? `The base template and ${moduleText}`\n : \"The base template\"\n : moduleText;\n return `${subject} moved — run \\`saasaloy update\\` to apply.`;\n}\n\n/**\n * Rewrite the rows a registry override produced so they read `local`.\n *\n * `compareInstalled` marks every override row `outdated`, because that is what the word\n * means to `update`: re-apply the module from the checkout. `outdated` asks a different\n * question, and a working copy has no commit to compare against, so the honest answer is\n * `local` — the same answer a locally installed module gets without the override. Left as\n * `outdated`, every row counted as drift and `--check` exited 2 with nothing moved.\n *\n * Rows the override never touched (a module with no lock entry stays `unresolvable`) are\n * returned as they came.\n */\nexport function asLocalRows(\n comparisons: ModuleComparison[]\n): ModuleComparison[] {\n return comparisons.map((comparison) =>\n comparison.status === \"outdated\"\n ? { ...comparison, status: \"local\" as const }\n : comparison\n );\n}\n\nfunction splitSlug(slug: string): [string, string] | undefined {\n const parts = slug.split(\"/\");\n return parts.length === 2 && parts[0] && parts[1]\n ? [parts[0], parts[1]]\n : undefined;\n}\n\nexport async function runOutdated(argv: string[]): Promise<number> {\n // Parse before help answers, so a typo'd flag alongside `--help` still reports the typo.\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n intro(pc.bgCyan(pc.black(\" saasaloy outdated \")));\n\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n\n const sources: RegistrySource[] = [];\n try {\n const root = await findProjectRoot();\n const config = await loadConfig(root);\n const lock = await loadLock(root);\n const manifest = await loadManifest(root);\n const registryOverride = !!process.env[REGISTRY_ENV];\n if (registryOverride && config.installed.length > 0) {\n log.warn(\n `${REGISTRY_ENV} is set, so every module reads as local — unset it to compare against the registry.`\n );\n }\n\n // The base first: it ships with the CLI, so its comparison needs no network and no\n // registry, and it is the one row every project has.\n const templateDir = await baseTemplateDir();\n const baseRow = compareBase({\n lock,\n manifest,\n runningHash: await templateHash(templateDir),\n runningVersion: await readVersion(),\n missingTargets: await missingBaseTargets(root, manifest, templateDir),\n });\n\n // One source per (repo, ref), the same cache `update` keeps, so a bare run resolves\n // each ref once however many modules share it.\n const resolvers = new Map<string, RemoteRegistrySource>();\n const remote = (slug: string, ref: string): RemoteRegistrySource => {\n const key = `${slug}@${ref}`;\n const cached = resolvers.get(key);\n if (cached) {\n return cached;\n }\n const parts = splitSlug(slug);\n if (!parts) {\n throw new Error(\n `Lock entry source \"${slug}\" isn't an owner/repo coordinate.`\n );\n }\n const source = new RemoteRegistrySource(parts[0], parts[1], ref);\n resolvers.set(key, source);\n sources.push(source);\n return source;\n };\n\n let moduleRows: ModuleComparison[] = [];\n if (config.installed.length > 0) {\n const compared = await compareInstalled({\n installed: config.installed,\n lock,\n registryOverride,\n resolveRef: (_name, entry, ref) =>\n remote(entry.source, ref).resolveSha(),\n });\n moduleRows = registryOverride ? asLocalRows(compared) : compared;\n }\n\n note(wrapForNote(renderComparisons([baseRow]).join(\"\\n\")), \"Base\");\n note(wrapForNote(renderComparisons(moduleRows).join(\"\\n\")), \"Modules\");\n\n const comparisons = [baseRow, ...moduleRows];\n const drift = countDrift(comparisons);\n if (drift === 0) {\n if (registryOverride && moduleRows.length > 0) {\n // Nothing was compared, so there is nothing to gate on — `--check` exits 0 too.\n outro(pc.dim(\"Nothing to compare — every module reads as local.\"));\n } else if (baseRow.status === \"untracked\") {\n outro(\n pc.yellow(\n \"Modules are up to date; the base is untracked — run `saasaloy update` to record it.\"\n )\n );\n } else {\n outro(pc.green(\"Everything is up to date.\"));\n }\n return EXIT_OK;\n }\n\n const summary = describeDrift(comparisons);\n // `--check` is the only thing that turns news into a non-zero exit. A bare run is a\n // report, and a report that failed the shell would be unusable interactively.\n if (opts.check) {\n cancel(summary);\n return EXIT_REFUSED;\n }\n outro(pc.yellow(summary));\n return EXIT_OK;\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n } finally {\n for (const source of sources) {\n await source.cleanup?.();\n }\n }\n}\n","import { mkdir, readFile, rm as rmPath, writeFile } from \"node:fs/promises\";\nimport { dirname, posix } from \"node:path\";\nimport {\n buildPlan,\n executePlan,\n listModuleFiles,\n previewPatches,\n} from \"./applier.js\";\nimport type { LinkAction, Plan, PlannedLink, PlannedPatch } from \"./applier.js\";\nimport {\n classifyLink,\n createDirLink,\n hashContent,\n pathExists,\n readIfPresent,\n resolveWithinRoot,\n} from \"./fs-utils.js\";\nimport { BASE_MODULE, isBaseTracked } from \"./base.js\";\nimport type { LockBase, Lockfile, LockModule } from \"./lock.js\";\nimport { samePatchEntry } from \"./manifest.js\";\nimport type { Manifest, ManifestPatch } from \"./manifest.js\";\nimport { applyPatch } from \"./patch/index.js\";\nimport { parseDep, readRootPackageJson, writeDeps } from \"./pkg-json.js\";\nimport type { DepChange, PackageJson } from \"./pkg-json.js\";\nimport type { LoadedModule } from \"./registry.js\";\nimport { classifyTrackedFile } from \"./remover.js\";\nimport type { FileRemoveAction } from \"./remover.js\";\nimport type { RegistryPatch, SaasaloyConfig } from \"./schema.js\";\n\n// The deterministic core of `saasaloy update` (issue #48), mirroring the buildPlan/\n// executePlan split of applier.ts and remover.ts. Two things live here:\n//\n// compareInstalled — \"did anything move?\", derived from the lockfile alone. Exported\n// on its own because `saasaloy outdated` (#50) is exactly this and nothing else.\n// buildUpdatePlan / executeUpdatePlan — the three-way classification the merge plan\n// is built from: *base* (the module at the lock's old SHA), *theirs* (at the new\n// SHA) and *mine* (what's on disk). The lock's `resolved` SHA is what makes a real\n// merge base reachable at all (ADR 0006, ADR 0012).\n//\n// Nothing here fetches: the command hands over already-loaded module folders, which is\n// what keeps this whole file testable offline.\n\n/** The command named in the summary and the merge plan's Verification section — never run (decision 16). */\nexport const VERIFY_COMMAND = \"pnpm typecheck\";\n\n/** Surfaced when the update moved a db schema file — never run, never applied (decision 12). */\nexport const MIGRATION_COMMAND = \"pnpm --filter @repo/db db:generate\";\n\n/** The capability that owns migrations; without it there is nothing to regenerate. */\nconst DATABASE_MODULE = \"database\";\nconst DB_ALIAS = \"@db\";\nconst DB_SCHEMA_FALLBACK = \"packages/db/src/schema/\";\n\n// --- Phase 1: did anything move? -------------------------------------------------\n\n/**\n * Where an installed module stands against its registry:\n * current — the ref still resolves to the SHA the lock recorded\n * outdated — the ref moved; there is an update to apply\n * pinned — the lock's `ref` is itself a SHA, so it is frozen by definition\n * local — installed from a working copy; nothing to re-resolve\n * unresolvable — no lock entry, or the source couldn't be reached\n * untracked — the base only: no record to compare, `update` adopts it (#120)\n */\nexport type UpdateStatus =\n \"current\" | \"outdated\" | \"pinned\" | \"local\" | \"unresolvable\" | \"untracked\";\n\nexport interface ModuleComparison {\n name: string;\n /** `owner/repo`, or `local`. */\n source: string;\n /** The ref that was resolved — the lock's, or `--ref`'s override. */\n ref: string;\n /** The SHA the lock records today. */\n current: string;\n /** The SHA the ref resolves to now; equal to `current` when nothing moved. */\n latest: string;\n status: UpdateStatus;\n /** Why, for everything that isn't a plain current/outdated — shown in the summary. */\n detail?: string;\n /**\n * `--ref` named a ref the lock isn't tracking, but it resolves to the SHA already\n * recorded — so there are no files to update and the lock's `ref` still has to move.\n * Without this a `--ref v2` onto a pin whose tag hasn't moved yet would report\n * \"already at <sha7>\" and silently leave the module pinned forever (criterion 3).\n */\n refRewrite?: boolean;\n}\n\nexport interface CompareInstalledArgs {\n /** Modules to compare — `saasaloy.json`'s `installed`, or the one module named. */\n installed: string[];\n lock: Lockfile;\n /** Resolve a module's ref to a commit SHA. Rejecting marks the module unresolvable. */\n resolveRef: (name: string, entry: LockModule, ref: string) => Promise<string>;\n /** `--ref <ref>`: the explicit unpin. Only ever set alongside a single named module. */\n overrideRef?: string;\n /** True when SAASALOY_REGISTRY_DIR is set — a working copy replaces the registry. */\n registryOverride?: boolean;\n}\n\nconst SHA_PATTERN = /^[0-9a-f]{40}$/i;\n\n/** A 40-hex `ref` is a pin, not a moving target — the lock records where it points forever. */\nfunction isSha(ref: string): boolean {\n return SHA_PATTERN.test(ref);\n}\n\nfunction short(sha: string): string {\n return isSha(sha) ? sha.slice(0, 7) : sha;\n}\n\n/**\n * Compare every installed module's lock entry against what its ref resolves to now.\n * Pure but for the injected `resolveRef`, so `outdated` (#50) can call it with the same\n * resolver and render a report instead of applying anything. Never throws: an\n * unreachable source becomes an `unresolvable` row so one dead repo can't block the rest\n * (decision 14).\n */\nexport async function compareInstalled(\n args: CompareInstalledArgs\n): Promise<ModuleComparison[]> {\n const { installed, lock, resolveRef, overrideRef, registryOverride } = args;\n const out: ModuleComparison[] = [];\n\n for (const name of installed) {\n const entry = lock.modules[name];\n if (!entry) {\n out.push({\n name,\n source: \"unknown\",\n ref: \"unknown\",\n current: \"unknown\",\n latest: \"unknown\",\n status: \"unresolvable\",\n detail:\n \"no lock entry — reinstall it with `saasaloy add` to record its provenance\",\n });\n continue;\n }\n\n // A working copy has no commit identity, so there is no base and nothing to\n // re-resolve — the update re-applies whatever the checkout holds (decision 3).\n if (registryOverride) {\n out.push({\n name,\n source: \"local\",\n ref: \"local\",\n current: entry.resolved,\n latest: \"local\",\n status: \"outdated\",\n detail: \"local install — no merge base\",\n });\n continue;\n }\n\n if (entry.source === \"local\" || entry.resolved === \"local\") {\n out.push({\n name,\n source: entry.source,\n ref: entry.ref,\n current: entry.resolved,\n latest: entry.resolved,\n status: \"local\",\n detail:\n \"installed from a working copy — set SAASALOY_REGISTRY_DIR to update it\",\n });\n continue;\n }\n\n // Pinned by definition: the ref *is* the SHA, so re-resolving it can only return\n // itself. `--ref <branch|tag>` is the sanctioned way off it (decision 10).\n if (isSha(entry.ref) && !overrideRef) {\n out.push({\n name,\n source: entry.source,\n ref: entry.ref,\n current: entry.resolved,\n latest: entry.resolved,\n status: \"pinned\",\n detail: `pinned at ${short(entry.ref)} — nothing to update (use \\`--ref <branch|tag>\\` to move it)`,\n });\n continue;\n }\n\n const ref = overrideRef ?? entry.ref;\n try {\n const latest = await resolveRef(name, entry, ref);\n const status: UpdateStatus =\n latest === entry.resolved ? \"current\" : \"outdated\";\n out.push({\n name,\n source: entry.source,\n ref,\n current: entry.resolved,\n latest,\n status,\n // An `outdated` module rewrites `ref` as part of moving `resolved`; only a\n // `current` one needs the ref move called out on its own.\n ...(status === \"current\" && ref !== entry.ref\n ? { refRewrite: true }\n : {}),\n });\n } catch (error) {\n out.push({\n name,\n source: entry.source,\n ref,\n current: entry.resolved,\n latest: entry.resolved,\n status: \"unresolvable\",\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n return out;\n}\n\n/** What the base row prints where a module prints its `owner/repo` and its ref. */\nexport const BASE_SOURCE = \"bundled template\";\nexport const BASE_REF = \"template\";\n\nexport interface CompareBaseArgs {\n lock: Lockfile;\n manifest: Manifest;\n /** `templateHash()` of the template the running CLI ships. */\n runningHash: string;\n /** `readVersion()` of the running CLI. */\n runningVersion: string;\n /**\n * Tracked base targets that are not on disk — `missingBaseTargets()`. They make the base\n * `outdated` on their own, because the template hash cannot see them (#120).\n */\n missingTargets?: readonly string[];\n}\n\n/** `0.2.0 (bbbbbbb)` — the version is the label, the hash is the verdict (#120). */\nfunction baseLabel(version: string, hash: string): string {\n return `${version} (${hash.slice(0, 7)})`;\n}\n\n/**\n * The base's row beside `compareInstalled`'s (#120). The base ships inside the CLI, so\n * its comparison is the lock's `templateHash` against the running CLI's, with the two\n * `cliVersion`s as labels: `current` when the hashes agree, `outdated` when they differ,\n * `untracked` when the project carries no usable record. Never `local`: the template has\n * no working-copy override, and a registry override says nothing about it.\n *\n * A tracked file missing from disk also makes the base `outdated`, at an unchanged hash.\n * `adoptBase` records an absent template file so the next update restores it, and the hash\n * alone would report `current` on the same CLI and never restore anything.\n */\nexport function compareBase(args: CompareBaseArgs): ModuleComparison {\n const { lock, manifest, runningHash, runningVersion } = args;\n const missing = args.missingTargets ?? [];\n const latest = baseLabel(runningVersion, runningHash);\n if (!isBaseTracked(lock, manifest) || !lock.base) {\n return {\n name: BASE_MODULE,\n source: BASE_SOURCE,\n ref: BASE_REF,\n current: \"untracked\",\n latest,\n status: \"untracked\",\n detail:\n \"not recorded — run `saasaloy update` to adopt the base at this CLI\",\n };\n }\n const moved = lock.base.templateHash !== runningHash;\n return {\n name: BASE_MODULE,\n source: BASE_SOURCE,\n ref: BASE_REF,\n current: baseLabel(lock.base.cliVersion, lock.base.templateHash),\n latest,\n status: moved || missing.length > 0 ? \"outdated\" : \"current\",\n // Say why, or an unmoved hash beside `outdated` reads as a bug.\n ...(!moved && missing.length > 0\n ? {\n detail: `${missing.length} tracked file${missing.length === 1 ? \" is\" : \"s are\"} missing from disk`,\n }\n : {}),\n };\n}\n\n/**\n * Move the lock's `ref` for modules whose ref changed but whose SHA didn't — the\n * `--ref <branch|tag>` unpin of a module the tag already points at. There is nothing to\n * apply, so no update plan is ever built for these; without this the unpin would be\n * dropped on the floor and the module would stay pinned (criterion 3). `resolved` is\n * untouched by construction: it already holds the SHA the new ref resolves to.\n * Returns the names actually rewritten.\n */\nexport function recordRefRewrites(\n lock: Lockfile,\n comparisons: ModuleComparison[]\n): string[] {\n const rewritten: string[] = [];\n for (const comparison of comparisons) {\n if (!comparison.refRewrite) {\n continue;\n }\n const entry = lock.modules[comparison.name];\n // Same guard executeUpdatePlan uses: a comparison against a different source isn't\n // describing this entry's ref.\n if (\n !entry ||\n entry.ref === comparison.ref ||\n entry.source !== comparison.source\n ) {\n continue;\n }\n entry.ref = comparison.ref;\n rewritten.push(comparison.name);\n }\n return rewritten;\n}\n\n// --- Phase 2: classify every managed file three ways ------------------------------\n\n/**\n * How a module file at the new SHA relates to base and to disk:\n * skip — base === theirs: the module didn't touch it, so neither do we,\n * drift or not (the single most important rule here)\n * create — the new version adds it and nothing occupies the path\n * overwrite — tracked and untouched since it was applied → safe deterministic write\n * restore — tracked but missing from disk → put it back (decision 6)\n * unchanged — disk already holds exactly what the new version ships\n * drift — tracked and hand-edited → merge plan, never written\n * conflict — the new version adds a file that already exists untracked → merge\n * plan, rendered two-way (decision 13)\n * delete — the new version dropped it and disk still matches → remove it\n * delete-drift — dropped upstream but hand-edited → keep it, say so (decision 5)\n * delete-missing — dropped upstream and already gone → just untrack\n */\nexport type UpdateFileAction =\n | \"skip\"\n | \"create\"\n | \"overwrite\"\n | \"restore\"\n | \"unchanged\"\n | \"drift\"\n | \"conflict\"\n | \"delete\"\n | \"delete-drift\"\n | \"delete-missing\";\n\n/** `remove`'s verdicts for a tracked file, said in `update`'s vocabulary (same classifier). */\nconst DELETE_ACTION: Record<FileRemoveAction, UpdateFileAction> = {\n delete: \"delete\",\n drift: \"delete-drift\",\n missing: \"delete-missing\",\n};\n\n/** Actions safe to write without a human in the loop (the clean path, spec §2.13). */\nconst WRITABLE: ReadonlySet<UpdateFileAction> = new Set<UpdateFileAction>([\n \"create\",\n \"overwrite\",\n \"restore\",\n \"unchanged\",\n]);\n\n/** Actions that route a file into the merge plan rather than to disk. */\nconst NEEDS_MERGE: ReadonlySet<UpdateFileAction> = new Set<UpdateFileAction>([\n \"drift\",\n \"conflict\",\n \"delete-drift\",\n]);\n\nexport interface PlannedUpdateFile {\n module: string;\n /** Module-relative POSIX source path — the manifest's `from`. */\n from: string;\n /** Project-relative POSIX path (manifest key + display). */\n target: string;\n /** Absolute destination path, validated to sit inside the project root. */\n targetAbs: string;\n action: UpdateFileAction;\n /** Content at the lock's old SHA; absent when the module didn't ship it then, or there's no base. */\n base?: string;\n /** Content at the new SHA; absent when the new version dropped the file. */\n theirs?: string;\n /** Content on disk right now; absent when the file is missing. */\n mine?: string;\n /** sha256 of `theirs` — what the manifest records once written. */\n newHash?: string;\n /** The template hands this file over on first write; it is the project's afterwards. */\n projectOwned?: boolean;\n /** The manifest hash was adopted off disk, so it does not prove the file is untouched. */\n adopted?: boolean;\n /**\n * Modules whose config patches also wrote to this file. A patched file's manifest\n * hash is deliberately not re-recorded (the patch belongs to another module, see\n * applier.ts), so it classifies as drift on the next update — this says so, rather\n * than letting the merge plan imply a human edited it. `remove` reverses those patches\n * from the manifest record itself (#36); `update` still has no hash to compare against.\n */\n patchedBy?: string[];\n}\n\n/**\n * A config patch bound to a concrete target file, previewed. Identical to `add`'s\n * `PlannedPatch` because one helper builds both (#98): `apply` writes it, `unchanged` is\n * a no-op, `missing` has no target file, and `matched` is the collision that routes it\n * into the merge plan (decision 1).\n */\nexport type PlannedUpdatePatch = PlannedPatch;\n\n/** A pin the module owns that moved between the two descriptor revisions (decision 8). */\nexport interface DepBump {\n name: string;\n from: string;\n to: string;\n /** True when the pin lives in package.json's `devDependencies` — the bucket it is rewritten in. */\n dev: boolean;\n}\n\nexport interface ModuleUpdatePlan {\n name: string;\n comparison: ModuleComparison;\n /** Why there is no merge base, when there isn't one — stamped on the merge plan (decision 3). */\n noMergeBase?: string;\n /** Commit subjects touching the module between the two SHAs (decision 4); `[]` when unavailable. */\n intent: string[];\n /** Every file the new version ships, classified. */\n files: PlannedUpdateFile[];\n /** Every file the new version dropped, classified. */\n removals: PlannedUpdateFile[];\n links: PlannedLink[];\n patches: PlannedUpdatePatch[];\n depAdds: DepChange[];\n devDepAdds: DepChange[];\n depBumps: DepBump[];\n /** Human-readable version disagreements, in `planDeps`'s existing phrasing. */\n depConflicts: string[];\n /** New `dependsOn` prerequisites this version introduces (decision 11). */\n prereqNames: string[];\n /** An ordinary `add` plan for those prerequisites, executed under the same confirmation. */\n prereqPlan?: Plan;\n /** Each prerequisite's own `dependsOn`, so its lock entry stays self-describing. */\n prereqDependsOn: Record<string, string[]>;\n /** Each prerequisite's own `conflictsWith`, for the same reason. */\n prereqConflictsWith: Record<string, string[]>;\n /** The new version's own `dependsOn`, written into the lock entry. */\n dependsOn?: string[];\n /**\n * The new version's own `conflictsWith`, written into the lock entry. The descriptor is\n * gone once the update lands, so the lock is the only offline record of what this\n * module refuses to sit beside — drop it here and the next `add` stops refusing a\n * second driver (#98).\n */\n conflictsWith?: string[];\n /** Removal warnings from the new descriptor, persisted only after a clean update. */\n removeWarnings: string[];\n /**\n * Env vars the new version requires that the old one did not — named before the\n * confirmation, or a version that starts requiring a secret updates in silence. With no\n * merge base there is nothing to diff against, so the whole set is reported.\n */\n newEnvVars: Record<string, string>;\n /** True when anything about this module routes to the merge plan. */\n needsMerge: boolean;\n /**\n * Set on the base's plan only (#120): the lock `base` record a clean run writes in place\n * of a `modules` entry. Its presence is also what the merge plan reads to render the\n * section as the template rather than a registry module.\n */\n baseRecord?: LockBase;\n}\n\nexport interface UpdatePlan {\n modules: ModuleUpdatePlan[];\n /** Comparisons that won't be applied (current/pinned/local/unresolvable) — for the summary. */\n skipped: ModuleComparison[];\n /** Installed modules with no lock entry — reported, never fatal (mirrors remover.ts). */\n missingLockEntries: string[];\n /** Set when a db schema file moved and `database` is installed (decision 12). */\n migrationCommand?: string;\n /** Always `pnpm typecheck` — named, never executed (decision 16). */\n verifyCommand: string;\n /** True when any module needs a merge — i.e. a merge plan document is worth emitting. */\n needsMerge: boolean;\n}\n\n/** One module's two revisions plus everything the command learned while fetching them. */\nexport interface ModuleUpdateInput {\n comparison: ModuleComparison;\n /** The module as it exists at the new SHA. */\n theirs: LoadedModule;\n /** The module at the lock's old SHA; absent when the base can't be reached. */\n base?: LoadedModule;\n /** Why `base` is absent — `local install`, `force-pushed branch`, … (decision 3). */\n noMergeBase?: string;\n /** Commit subjects touching `modules/<name>/` between the two SHAs. */\n intent?: string[];\n /** Descriptors for prerequisites the new version introduces, topologically ordered. */\n prereqs?: { order: string[]; modules: Map<string, LoadedModule> };\n /**\n * Manifest entries this module owns that the run must leave alone — the base's seed\n * files (#120). They are neither classified nor treated as dropped when `theirs` no\n * longer ships them.\n */\n ignoreTargets?: ReadonlySet<string>;\n /**\n * Whether a target is source the project owns once it has been written — the base's\n * blocks, components, `globals.css` and favicon (`ownedFiles` in `_saasaloy-base.json`).\n * An owned file that is absent is still created or restored, because a project that\n * never had it is not a project that edited it. An owned file that exists is never\n * written: it goes to the merge plan whatever its hash says.\n */\n isOwned?: (target: string) => boolean;\n /**\n * Recorded patches other modules applied to files `theirs` ships, re-applied after an\n * overwrite through the same idempotent loop as the module's own (#120). A patch whose\n * identity now holds a different value demotes its file to `drift`.\n */\n reapplyPatches?: ManifestPatch[];\n /** The lock `base` record to write on a clean run — the base only (#120). */\n baseRecord?: LockBase;\n}\n\nexport interface BuildUpdatePlanArgs {\n root: string;\n config: SaasaloyConfig;\n manifest: Manifest;\n lock: Lockfile;\n inputs: ModuleUpdateInput[];\n /** Modules this run considered; defaults to everything installed. Scopes `missingLockEntries`. */\n considered?: string[];\n /** Comparisons the command decided not to update — carried through for one summary. */\n skipped?: ModuleComparison[];\n /** The project root package.json, for dependency-pin bumping. */\n pkg?: PackageJson | null;\n}\n\n/** Modules that have applied a config patch to this file, per the manifest's record. */\nfunction patchersOf(manifest: Manifest, target: string): string[] {\n return [\n ...new Set(\n manifest.patches.filter((p) => p.file === target).map((p) => p.module)\n ),\n ];\n}\n\n/** Aliases a module's scaffolds register — needed to resolve its own file targets. */\nfunction scaffoldAliases(\n mod: LoadedModule | undefined\n): Record<string, string> {\n const out: Record<string, string> = {};\n for (const scaffold of mod?.item.scaffolds ?? []) {\n for (const [alias, prefix] of Object.entries(scaffold.aliases ?? {})) {\n out[alias] = prefix;\n }\n }\n return out;\n}\n\nexport async function buildUpdatePlan(\n args: BuildUpdatePlanArgs\n): Promise<UpdatePlan> {\n const { root, config, manifest, lock, inputs, pkg } = args;\n\n // Scoped to what this run looked at, so `update email` doesn't editorialize about an\n // unrelated module the base template registered without a lock entry.\n const missingLockEntries = (args.considered ?? config.installed).filter(\n (name) => !lock.modules[name]\n );\n const modules: ModuleUpdatePlan[] = [];\n const touched: string[] = [];\n\n // Both records of what this project holds, unioned: `saasaloy.json` is the live list and\n // the lock is what `add` actually resolved, and a project can be mid-way between them.\n const installed: ReadonlySet<string> = new Set([\n ...config.installed,\n ...Object.keys(lock.modules),\n ]);\n\n for (const item of inputs) {\n const plan = await planOneModule({\n root,\n config,\n manifest,\n input: item,\n pkg: pkg ?? null,\n installed,\n });\n modules.push(plan);\n for (const file of plan.files) {\n if (file.action !== \"skip\" && file.action !== \"unchanged\") {\n touched.push(file.target);\n }\n }\n for (const file of plan.removals) {\n if (file.action !== \"delete-missing\") {\n touched.push(file.target);\n }\n }\n }\n\n // Phase 5: name the migration command when a schema file moved. The `@db` alias is\n // where the database capability's scaffold actually put the schema; the literal is\n // only a fallback for a project whose alias map predates it.\n const schemaPrefix = config.aliases[DB_ALIAS]\n ? `${config.aliases[DB_ALIAS].replace(/\\/$/, \"\")}/schema/`\n : DB_SCHEMA_FALLBACK;\n const migrationCommand =\n config.installed.includes(DATABASE_MODULE) &&\n touched.some((t) => t.startsWith(schemaPrefix))\n ? MIGRATION_COMMAND\n : undefined;\n\n return {\n modules,\n skipped: args.skipped ?? [],\n missingLockEntries,\n ...(migrationCommand ? { migrationCommand } : {}),\n verifyCommand: VERIFY_COMMAND,\n needsMerge: modules.some((m) => m.needsMerge),\n };\n}\n\ninterface PlanOneArgs {\n root: string;\n config: SaasaloyConfig;\n manifest: Manifest;\n input: ModuleUpdateInput;\n pkg: PackageJson | null;\n /** Modules this project holds — what an `onlyWith` condition is tested against (#99). */\n installed: ReadonlySet<string>;\n}\n\nasync function planOneModule(args: PlanOneArgs): Promise<ModuleUpdatePlan> {\n const { root, config, manifest, input, pkg } = args;\n const { theirs, base } = input;\n const name = theirs.item.name;\n\n // Resolve targets against the aliases that will exist once this run lands: the\n // project's own, plus any scaffold alias either revision (or a new prerequisite)\n // declares — otherwise a brand-new capability's files can't be placed.\n const prereqAliases: Record<string, string> = {};\n for (const prereqName of input.prereqs?.order ?? []) {\n Object.assign(\n prereqAliases,\n scaffoldAliases(input.prereqs?.modules.get(prereqName))\n );\n }\n const aliasView = {\n ...config.aliases,\n ...scaffoldAliases(base),\n ...prereqAliases,\n ...scaffoldAliases(theirs),\n };\n\n // The same install set both revisions are filtered against (#99): what the project\n // holds, plus any prerequisite this update introduces. Filtering base and theirs alike\n // is what keeps the three-way merge honest — a variant this project never held is not\n // a file the new revision \"dropped\", and the one it does hold is the one to diff.\n const installedSet = new Set([\n ...args.installed,\n ...(input.prereqs?.order ?? []),\n name,\n ]);\n\n const theirsFiles = await listModuleFiles(theirs, aliasView, installedSet);\n const baseFiles = base\n ? await listModuleFiles(base, aliasView, installedSet)\n : undefined;\n\n const files: PlannedUpdateFile[] = [];\n for (const ref of theirsFiles.values()) {\n const theirsContent = await readFile(ref.abs, \"utf-8\");\n const baseRef = baseFiles?.get(ref.target);\n const baseContent = baseRef\n ? await readFile(baseRef.abs, \"utf-8\")\n : undefined;\n const targetAbs = resolveWithinRoot(root, ref.target);\n const managed = manifest.managed[ref.target];\n const owned = managed?.module === name ? managed : undefined;\n const mine = await readIfPresent(targetAbs);\n\n const projectOwned = input.isOwned?.(ref.target) ?? false;\n const action = classifyUpdate(\n baseContent,\n theirsContent,\n mine,\n owned?.hash,\n owned?.adopted ?? false,\n projectOwned\n );\n const patchedBy = patchersOf(manifest, ref.target);\n files.push({\n module: name,\n from: ref.from,\n target: ref.target,\n targetAbs,\n action,\n ...(projectOwned ? { projectOwned: true } : {}),\n ...(owned?.adopted ? { adopted: true } : {}),\n base: baseContent,\n theirs: theirsContent,\n mine,\n newHash: hashContent(theirsContent),\n ...(patchedBy.length > 0 ? { patchedBy } : {}),\n });\n }\n\n // Anything this module owns that the new version no longer ships. Derived from the\n // manifest rather than from base, so it stays right even with no merge base — the\n // manifest is the record of what we actually put there.\n const removals: PlannedUpdateFile[] = [];\n for (const [target, entry] of Object.entries(manifest.managed)) {\n if (\n entry.module !== name ||\n theirsFiles.has(target) ||\n input.ignoreTargets?.has(target)\n ) {\n continue;\n }\n const targetAbs = resolveWithinRoot(root, target);\n const mine = await readIfPresent(targetAbs);\n const baseRef = baseFiles?.get(target);\n // An adopted or project-owned file that is still on disk is never deleted for the\n // same reason it is never overwritten: nothing here knows whose bytes those are.\n const keep =\n mine !== undefined &&\n ((entry.adopted ?? false) || (input.isOwned?.(target) ?? false));\n removals.push({\n module: name,\n from: entry.from ?? baseRef?.from ?? target,\n target,\n targetAbs,\n action: keep\n ? \"delete-drift\"\n : DELETE_ACTION[classifyTrackedFile(mine, entry.hash)],\n base: baseRef ? await readFile(baseRef.abs, \"utf-8\") : undefined,\n mine,\n });\n }\n\n const links = await planLinks(root, theirs);\n const patches = await planPatches(\n root,\n name,\n theirs.item.patches ?? [],\n files\n );\n await reapplyRecordedPatches(\n root,\n input.reapplyPatches ?? [],\n files,\n patches\n );\n const deps = planDepChanges(base, theirs, pkg);\n\n // A `dependsOn` the new version introduces is folded into this same plan rather than\n // left for the user to install by hand (decision 11).\n let prereqPlan: Plan | undefined;\n const prereqDependsOn: Record<string, string[]> = {};\n const prereqConflictsWith: Record<string, string[]> = {};\n const prereqNames = (input.prereqs?.order ?? []).filter(\n (n) => !config.installed.includes(n)\n );\n if (prereqNames.length > 0 && input.prereqs) {\n prereqPlan = await buildPlan({\n root,\n install: prereqNames,\n alreadyInstalled: [],\n modules: input.prereqs.modules,\n config,\n manifest,\n });\n for (const prereq of prereqNames) {\n const prereqItem = input.prereqs.modules.get(prereq)?.item;\n const dependsOn = prereqItem?.dependsOn;\n if (dependsOn && dependsOn.length > 0) {\n prereqDependsOn[prereq] = dependsOn;\n }\n const conflictsWith = prereqItem?.conflictsWith;\n if (conflictsWith && conflictsWith.length > 0) {\n prereqConflictsWith[prereq] = conflictsWith;\n }\n }\n }\n\n const needsMerge =\n files.some((f) => NEEDS_MERGE.has(f.action)) ||\n removals.some((f) => NEEDS_MERGE.has(f.action)) ||\n patches.some((p) => p.matched !== undefined);\n\n return {\n name,\n comparison: input.comparison,\n ...(input.noMergeBase ? { noMergeBase: input.noMergeBase } : {}),\n ...(input.baseRecord ? { baseRecord: input.baseRecord } : {}),\n intent: input.intent ?? [],\n files,\n removals,\n links,\n patches,\n ...deps,\n prereqNames,\n ...(prereqPlan ? { prereqPlan } : {}),\n prereqDependsOn,\n prereqConflictsWith,\n ...(theirs.item.dependsOn && theirs.item.dependsOn.length > 0\n ? { dependsOn: theirs.item.dependsOn }\n : {}),\n ...(theirs.item.conflictsWith && theirs.item.conflictsWith.length > 0\n ? { conflictsWith: theirs.item.conflictsWith }\n : {}),\n removeWarnings: [...(theirs.item.removeWarnings ?? [])],\n newEnvVars: newEnvVars(base, theirs),\n needsMerge,\n };\n}\n\n/**\n * Patches other modules recorded against files this revision ships (#120): the base's\n * `apps/web/package.json` after `waitlist` added a script to it. `add` never re-records a\n * file's hash after a patch, so on disk it reads as drift; three verdicts fix that:\n *\n * - a file that holds exactly the new render plus its patches is `unchanged`, since the\n * patched bytes are the bytes this run would leave behind;\n * - a file this run overwrites gets every recorded patch previewed against the new\n * render, so the execute loop lands them again after the write;\n * - a preview that comes back `matched` — the identity is present with a value the\n * template now ships — demotes the file to `drift`, and `patchedBy` names the patcher\n * in the plan.\n */\nasync function reapplyRecordedPatches(\n root: string,\n recorded: ManifestPatch[],\n files: PlannedUpdateFile[],\n patches: PlannedUpdatePatch[]\n): Promise<void> {\n if (recorded.length === 0) {\n return;\n }\n const byTarget = new Map(files.map((f) => [f.target, f]));\n\n for (const file of files) {\n const ops = recorded.filter((p) => p.file === file.target);\n if (\n file.action !== \"drift\" ||\n ops.length === 0 ||\n file.theirs === undefined\n ) {\n continue;\n }\n let expected = file.theirs;\n for (const op of ops) {\n expected = applyPatch(expected, op.patch, op.file).content;\n }\n if (expected === file.mine) {\n file.action = \"unchanged\";\n }\n }\n\n for (const entry of recorded) {\n const file = byTarget.get(entry.file);\n if (!file) {\n continue;\n }\n const previews = await previewPatches({\n root,\n module: entry.module,\n ops: [entry.patch],\n planned: new Map([\n [\n file.target,\n { content: file.theirs, landsOnDisk: WRITABLE.has(file.action) },\n ],\n ]),\n });\n for (const preview of previews) {\n if (preview.matched !== undefined && file.action === \"overwrite\") {\n file.action = \"drift\";\n }\n patches.push(preview);\n }\n }\n}\n\n/**\n * Env vars `theirs` requires that `base` did not. `base` absent means the run has no\n * merge base at all (a local install, a force-pushed branch), so nothing can be called\n * old — the whole set is returned rather than staying quiet about a secret the project\n * may now need.\n */\nfunction newEnvVars(\n base: LoadedModule | undefined,\n theirs: LoadedModule\n): Record<string, string> {\n const known = new Set(Object.keys(base?.item.envVars ?? {}));\n const out: Record<string, string> = {};\n for (const [key, description] of Object.entries(theirs.item.envVars ?? {})) {\n if (base && known.has(key)) {\n continue;\n }\n out[key] = description;\n }\n return out;\n}\n\n/**\n * The three-way verdict for one file. `base === theirs` short-circuits everything: the\n * module didn't touch this file, so whatever the user did to it is none of our business\n * (the rule that keeps `update` from re-proposing every hand-edit forever).\n *\n * Two rules stand between a recorded hash and a write. `adopted` says the hash came off\n * disk rather than out of a render, so it proves nothing about who wrote those bytes.\n * `projectOwned` says the template hands this file over once and never comes back for it.\n * Either one sends an existing file to the merge plan; neither blocks creating or\n * restoring a file that is not there, since an absent file carries no work to lose.\n */\nfunction classifyUpdate(\n base: string | undefined,\n theirs: string,\n mine: string | undefined,\n managedHash: string | undefined,\n adopted: boolean,\n projectOwned: boolean\n): UpdateFileAction {\n if (base !== undefined && base === theirs) {\n return \"skip\";\n }\n if (mine === undefined) {\n return managedHash === undefined ? \"create\" : \"restore\";\n }\n if (mine === theirs) {\n return \"unchanged\";\n }\n if (managedHash === undefined) {\n return \"conflict\";\n }\n if (adopted || projectOwned) {\n return \"drift\";\n }\n return hashContent(mine) === managedHash ? \"overwrite\" : \"drift\";\n}\n\nasync function planLinks(\n root: string,\n theirs: LoadedModule\n): Promise<PlannedLink[]> {\n const links: PlannedLink[] = [];\n for (const skillRel of theirs.item.agent?.skills ?? []) {\n const folderName = posix.basename(skillRel);\n const path = posix.join(\".claude/skills\", folderName);\n const target = posix.join(\".agents/skills\", folderName);\n const pathAbs = resolveWithinRoot(root, path);\n const targetAbs = resolveWithinRoot(root, target);\n const state = await classifyLink(pathAbs, targetAbs);\n const action: LinkAction =\n state === \"missing\"\n ? \"create\"\n : state === \"correct\"\n ? \"exists\"\n : \"conflict\";\n links.push({\n module: theirs.item.name,\n path,\n pathAbs,\n target,\n targetAbs,\n action,\n });\n }\n return links;\n}\n\n/**\n * Re-apply each of the new version's patches, idempotently, against the content this run\n * will leave behind — so a patch aimed at a file the same update rewrites reads the new\n * bytes. `previewPatches` is the same helper `buildPlan` uses (#98). The interesting\n * outcome is `matched`: the patch is a no-op *because* its identity already holds a\n * different value — the user edited what we would have written — which routes it to the\n * merge plan as prose rather than silently doing nothing (decision 1).\n */\nfunction planPatches(\n root: string,\n module: string,\n ops: RegistryPatch[],\n files: PlannedUpdateFile[]\n): Promise<PlannedUpdatePatch[]> {\n return previewPatches({\n root,\n module,\n ops,\n planned: new Map(\n files.map((f) => [\n f.target,\n { content: f.theirs, landsOnDisk: WRITABLE.has(f.action) },\n ])\n ),\n });\n}\n\ninterface PlannedDeps {\n depAdds: DepChange[];\n devDepAdds: DepChange[];\n depBumps: DepBump[];\n depConflicts: string[];\n}\n\n/** `name -> version` for one descriptor revision, per bucket. */\nfunction pinsOf(\n mod: LoadedModule | undefined,\n dev: boolean\n): Map<string, string> {\n const specs =\n (dev ? mod?.item.devDependencies : mod?.item.dependencies) ?? [];\n return new Map(\n specs.map((spec) => [parseDep(spec).name, parseDep(spec).version])\n );\n}\n\n/**\n * A pin moves only when the module owns it *and* the user hasn't overridden it: base's\n * descriptor says which pins the module moved, and the current package.json still\n * matching base's value proves nobody edited it since (decision 8). Anything else keeps\n * `planDeps`'s existing conflict warning rather than clobbering a deliberate override.\n */\nfunction planDepChanges(\n base: LoadedModule | undefined,\n theirs: LoadedModule,\n pkg: PackageJson | null\n): PlannedDeps {\n const depAdds: DepChange[] = [];\n const devDepAdds: DepChange[] = [];\n const depBumps: DepBump[] = [];\n const depConflicts: string[] = [];\n if (!pkg) {\n return { depAdds, devDepAdds, depBumps, depConflicts };\n }\n\n for (const dev of [false, true]) {\n const bucket = dev ? (pkg.devDependencies ?? {}) : (pkg.dependencies ?? {});\n const otherBucket = dev\n ? (pkg.dependencies ?? {})\n : (pkg.devDependencies ?? {});\n const basePins = pinsOf(base, dev);\n\n for (const [name, wanted] of pinsOf(theirs, dev)) {\n // The pin may sit in the *other* bucket — the user moved it, or an earlier version\n // of the descriptor declared it there. Bump it where it actually lives: writing it\n // into the bucket this descriptor declares would leave the stale pin behind and\n // package.json would carry the same package at two versions.\n const inDeclaredBucket = bucket[name] !== undefined;\n const current = inDeclaredBucket ? bucket[name] : otherBucket[name];\n if (current === undefined) {\n (dev ? devDepAdds : depAdds).push({ name, version: wanted });\n continue;\n }\n if (current === wanted) {\n continue;\n }\n\n const basePin = basePins.get(name);\n if (basePin !== undefined && basePin !== wanted && current === basePin) {\n depBumps.push({\n name,\n from: current,\n to: wanted,\n dev: inDeclaredBucket ? dev : !dev,\n });\n } else {\n depConflicts.push(\n `${name}: package.json already has ${current}, ignoring ${wanted}`\n );\n }\n }\n }\n return { depAdds, devDepAdds, depBumps, depConflicts };\n}\n\n// --- Phase 3: apply the clean path ------------------------------------------------\n\nexport interface ExecuteUpdateArgs {\n root: string;\n config: SaasaloyConfig;\n manifest: Manifest;\n lock: Lockfile;\n /** The root package.json, when dependency pins are being written. */\n pkg?: PackageJson | null;\n}\n\nexport interface UpdateResult {\n /** Files actually written (create/overwrite/restore). */\n written: PlannedUpdateFile[];\n /** Files already byte-identical to the new version — manifest refreshed, disk untouched. */\n refreshed: PlannedUpdateFile[];\n /** Planned-writable files edited between planning and the write — left alone. */\n lateDrift: PlannedUpdateFile[];\n /** Files deleted because the new version dropped them and disk still matched. */\n deleted: PlannedUpdateFile[];\n /** Dropped-upstream files kept because they were hand-edited — now the user's. */\n driftSurvivors: PlannedUpdateFile[];\n /** Manifest entries dropped for files that were already gone. */\n untracked: PlannedUpdateFile[];\n patched: PlannedUpdatePatch[];\n /** Patches whose target file was absent — reported, not applied. */\n patchConflicts: PlannedUpdatePatch[];\n links: PlannedLink[];\n linkConflicts: PlannedLink[];\n /** Prerequisites installed as part of the same confirmed plan. */\n prereqsInstalled: string[];\n /** Modules whose lock entry moved to the new SHA (those that fully applied). */\n lockMoved: string[];\n /** Modules whose lock `ref` was rewritten by `--ref` while `resolved` stayed put. */\n refsRecorded: string[];\n /** Dependency pins actually written into package.json. */\n depsWritten: DepChange[];\n}\n\n/**\n * Whether a planned file still looks the way it did when the plan was built. The\n * confirmation prompt (and, for a bare `update`, a long fetch) sits between the two, so\n * the verdict is re-earned immediately before the write — the same reason remover.ts\n * re-checks before deleting.\n */\nasync function stillMatches(file: PlannedUpdateFile): Promise<boolean> {\n const now = await readIfPresent(file.targetAbs);\n return now === file.mine;\n}\n\nexport async function executeUpdatePlan(\n plan: UpdatePlan,\n args: ExecuteUpdateArgs\n): Promise<UpdateResult> {\n const { root, config, manifest, lock } = args;\n const result: UpdateResult = {\n written: [],\n refreshed: [],\n lateDrift: [],\n deleted: [],\n driftSurvivors: [],\n untracked: [],\n patched: [],\n patchConflicts: [],\n links: [],\n linkConflicts: [],\n prereqsInstalled: [],\n lockMoved: [],\n refsRecorded: [],\n depsWritten: [],\n };\n\n const bumpsAsDeps: DepChange[] = [];\n const devBumpsAsDeps: DepChange[] = [];\n\n for (const mod of plan.modules) {\n // Prerequisites first: a new capability may scaffold the very alias this module's\n // files resolve against, and `executePlan` registers those aliases as it goes.\n if (mod.prereqPlan) {\n await executePlan(mod.prereqPlan, root, config, manifest);\n result.prereqsInstalled.push(...mod.prereqPlan.install);\n }\n\n let clean = !mod.needsMerge;\n\n for (const file of mod.files) {\n if (!WRITABLE.has(file.action) || file.theirs === undefined) {\n continue;\n }\n if (!(await stillMatches(file))) {\n result.lateDrift.push(file);\n clean = false;\n continue;\n }\n // `unchanged` means disk already holds these exact bytes, so rewriting it would\n // churn the file's mtime for nothing. Its manifest entry is still refreshed —\n // that is how a file installed before `from` existed acquires one.\n if (file.action === \"unchanged\") {\n result.refreshed.push(file);\n } else {\n await mkdir(dirname(file.targetAbs), { recursive: true });\n await writeFile(file.targetAbs, file.theirs, \"utf-8\");\n result.written.push(file);\n }\n manifest.managed[file.target] = {\n module: file.module,\n hash: file.newHash ?? hashContent(file.theirs),\n from: file.from,\n };\n }\n\n for (const file of mod.removals) {\n if (file.action === \"delete\") {\n if (await stillMatches(file)) {\n await rmPath(file.targetAbs, { force: true });\n result.deleted.push(file);\n } else {\n // Edited while the confirmation was up — the delete was authorized for\n // different bytes, so it doesn't authorize removing these.\n result.driftSurvivors.push(file);\n clean = false;\n }\n } else if (file.action === \"delete-drift\") {\n result.driftSurvivors.push(file);\n } else {\n result.untracked.push(file);\n }\n // Untrack in every case: the new version no longer ships this file, so a survivor\n // becomes plain user-owned content rather than something `update` keeps offering\n // to delete on every run.\n delete manifest.managed[file.target];\n }\n\n for (const p of mod.patches) {\n if (!(await pathExists(p.fileAbs))) {\n result.patchConflicts.push(p);\n clean = false;\n continue;\n }\n // Re-apply against fresh disk state, never the preview — the engine is idempotent,\n // so a patch that already landed is a clean no-op.\n const source = await readFile(p.fileAbs, \"utf-8\");\n const { content, changed } = applyPatch(source, p.patch, p.file);\n if (!changed) {\n continue;\n }\n await writeFile(p.fileAbs, content, \"utf-8\");\n result.patched.push(p);\n const entry: ManifestPatch = {\n module: p.module,\n file: p.file,\n patch: p.patch,\n };\n if (\n !manifest.patches.some((existing) => samePatchEntry(existing, entry))\n ) {\n manifest.patches.push(entry);\n }\n }\n\n for (const link of mod.links) {\n if (link.action === \"conflict\") {\n result.linkConflicts.push(link);\n continue;\n }\n if (link.action === \"create\") {\n await createDirLink(link.pathAbs, link.targetAbs);\n }\n manifest.links[link.target] = link.path;\n result.links.push(link);\n }\n\n for (const bump of mod.depBumps) {\n (bump.dev ? devBumpsAsDeps : bumpsAsDeps).push({\n name: bump.name,\n version: bump.to,\n });\n }\n\n if (clean) {\n if (mod.removeWarnings.length > 0) {\n manifest.removeWarnings[mod.name] = [...mod.removeWarnings];\n } else {\n delete manifest.removeWarnings[mod.name];\n }\n }\n\n // The base has no `modules` entry: its record is the lock's `base` object, and it moves\n // under the same rule as a module's `resolved` — only once everything landed (#120).\n if (mod.baseRecord) {\n if (clean && mod.comparison.latest !== mod.comparison.current) {\n lock.base = mod.baseRecord;\n result.lockMoved.push(mod.name);\n }\n continue;\n }\n\n // Move `resolved` only for a module that fully landed. While anything still needs\n // merging, the *old* SHA is the only merge base a re-run has — advancing it would\n // strand the drifted files with nothing to diff against (decision 15's boundary).\n if (clean && mod.comparison.latest !== mod.comparison.current) {\n lock.modules[mod.name] = {\n source: mod.comparison.source,\n ref: mod.comparison.ref,\n resolved: mod.comparison.latest,\n ...(mod.dependsOn ? { dependsOn: mod.dependsOn } : {}),\n ...(mod.conflictsWith ? { conflictsWith: mod.conflictsWith } : {}),\n };\n result.lockMoved.push(mod.name);\n } else {\n // `ref` is intent (\"track this branch\") and `resolved` is fact (\"these bytes are\n // on disk\"), so an explicit `--ref` unpin is recorded even when files are still\n // out for merge — otherwise the user would have to repeat `--ref` on the re-run\n // the merge plan tells them to do (decision 10).\n const entry = lock.modules[mod.name];\n if (\n entry &&\n entry.ref !== mod.comparison.ref &&\n mod.comparison.source === entry.source\n ) {\n entry.ref = mod.comparison.ref;\n result.refsRecorded.push(mod.name);\n }\n }\n // A prerequisite installed here is pinned to the same SHA as the module that needs it.\n for (const prereq of mod.prereqPlan?.install ?? []) {\n const dependsOn = mod.prereqDependsOn[prereq];\n const conflictsWith = mod.prereqConflictsWith[prereq];\n lock.modules[prereq] = {\n source: mod.comparison.source,\n ref: mod.comparison.ref,\n resolved: mod.comparison.latest,\n ...(dependsOn && dependsOn.length > 0 ? { dependsOn } : {}),\n ...(conflictsWith && conflictsWith.length > 0 ? { conflictsWith } : {}),\n };\n }\n }\n\n // The root package.json is a base file, so a run that overwrote it above must not\n // then write dependency pins into the copy read before the run started — that copy\n // would clobber the new render (#120).\n let pkg = args.pkg;\n if (pkg && result.written.some((file) => file.target === \"package.json\")) {\n pkg = await readRootPackageJson(root);\n }\n const allAdds = plan.modules.flatMap((m) => m.depAdds);\n const allDevAdds = plan.modules.flatMap((m) => m.devDepAdds);\n if (\n pkg &&\n (allAdds.length > 0 ||\n allDevAdds.length > 0 ||\n bumpsAsDeps.length > 0 ||\n devBumpsAsDeps.length > 0)\n ) {\n const added = [...allAdds, ...bumpsAsDeps];\n const devAdded = [...allDevAdds, ...devBumpsAsDeps];\n // `writeDeps` merges by key, so an existing pin is rewritten in place — which is\n // exactly what a bump is.\n await writeDeps(root, pkg, added, devAdded);\n result.depsWritten = [...added, ...devAdded];\n }\n\n return result;\n}\n","import { readdir, readFile, rm as rmPath, writeFile } from \"node:fs/promises\";\nimport { dirname, join, relative, sep } from \"node:path\";\nimport {\n assertNoSymlinkPath,\n classifyLink,\n hashContent,\n pathExists,\n resolveWithinRoot,\n} from \"./fs-utils.js\";\nimport type { Lockfile } from \"./lock.js\";\nimport { samePatchEntry } from \"./manifest.js\";\nimport type { Manifest, ManifestPatch } from \"./manifest.js\";\nimport { isReversibleKind, reversePatch } from \"./patch/index.js\";\nimport type { PatchResult } from \"./patch/index.js\";\nimport type { SaasaloyConfig } from \"./schema.js\";\n\n// The deterministic core of `saasaloy remove` — mirrors applier.ts's buildPlan/\n// executePlan split, but for undoing a module's application (issue #27). Fully\n// offline: the module descriptor isn't available at remove time (a remote add's\n// temp dir is long gone), so everything here derives from the three local state\n// files — manifest.managed/links/patches, saasaloy.json's installed[]/aliases, and\n// the lock's dependsOn.\n\n// How an owned managed file relates to what's actually on disk right now:\n// delete — on-disk content matches the manifest hash → safe to remove\n// drift — exists but hand-edited since (hash mismatch) → needs confirmation\n// missing — already gone → nothing to delete, just untrack\nexport type FileRemoveAction = \"delete\" | \"drift\" | \"missing\";\n\n/**\n * The verdict for one tracked file, from its on-disk content (`undefined` when the file\n * is gone) and the hash the manifest recorded when we wrote it. `update`'s delete side\n * asks exactly this question, so it calls this too and renames the answers to its own\n * `delete`/`delete-drift`/`delete-missing` — one definition of \"is this still the file\n * Saasaloy put there?\" for both commands.\n */\nexport function classifyTrackedFile(\n content: string | undefined,\n manifestHash: string\n): FileRemoveAction {\n if (content === undefined) {\n return \"missing\";\n }\n return hashContent(content) === manifestHash ? \"delete\" : \"drift\";\n}\n\nexport interface PlannedRemoveFile {\n module: string;\n /** Project-relative POSIX path (manifest key). */\n target: string;\n /** Absolute path on disk. */\n targetAbs: string;\n action: FileRemoveAction;\n /** On-disk content at plan time — undefined when `missing`. Used by `--diff`. */\n oldContent?: string;\n}\n\n// How a module-owned `.claude/skills/<name>` symlink relates to what's on disk:\n// remove — the correct link is there → safe to unlink\n// missing — nothing there → nothing to unlink\n// conflict — something else occupies the path → leave it, warn (symmetric with `add`)\nexport type LinkRemoveAction = \"remove\" | \"missing\" | \"conflict\";\n\nexport interface PlannedRemoveLink {\n module: string;\n /** manifest.links key — the real folder the link points at (e.g. .agents/skills/<name>). */\n target: string;\n targetAbs: string;\n /** manifest.links value — the symlink path (e.g. .claude/skills/<name>). */\n path: string;\n pathAbs: string;\n action: LinkRemoveAction;\n}\n\n// How a recorded config patch relates to what's on disk right now:\n// revert — a reversible kind with an edit still there → `diff` previews the undo\n// refused — the inverse declined it (the line is the user's now), or the file no longer\n// parses at all → `reason` says which, and names the file\n// gone — nothing left to undo (target file absent, or already hand-reverted)\n// drop — a kind with no inverse (the two `package.json` ones) → record goes, edit stays\nexport type PatchRemoveAction = \"revert\" | \"refused\" | \"gone\" | \"drop\";\n\nexport interface PlannedRemovePatch {\n /** The manifest entry this untracks. Dropped on execute whatever the action. */\n entry: ManifestPatch;\n action: PatchRemoveAction;\n /** Unified diff of the would-be reversal; `\"\"` for every action but `revert`. */\n diff: string;\n /** Why the inverse refused — set only when `action` is `refused`. */\n reason?: string;\n}\n\nexport interface RemovePlan {\n module: string;\n /** Descriptor warnings persisted at add or clean update time. */\n warnings: string[];\n files: PlannedRemoveFile[];\n links: PlannedRemoveLink[];\n /** This module's entries in manifest.patches, each classified against fresh disk state.\n * Every entry is dropped on execute; an entry of a reversible kind is also *reversed*\n * on disk first (see `reversePatch`), and `diff` previews that reversal so\n * `--dry-run`/`--diff` show the edit rather than only labelling it. */\n patches: PlannedRemovePatch[];\n /** Installed modules whose lock `dependsOn` names this module — block removal\n * without `--force`. */\n dependents: string[];\n /** Installed modules with no lock entry at all — dependent detection is\n * incomplete for them (they might depend on this module; we can't tell). */\n missingLockEntries: string[];\n}\n\nexport interface BuildRemovePlanArgs {\n root: string;\n /** Module to remove. */\n name: string;\n config: SaasaloyConfig;\n manifest: Manifest;\n lock: Lockfile;\n}\n\n// The manifest attributes overwritten files to the last writer, so whichever module\n// owns a file directly under a link's target folder is the current owner of the\n// link too (the `saasaloy-<module>` folder convention makes collisions unlikely —\n// verified across modules/, same assumption the applier makes).\nfunction ownerOfLinkTarget(\n manifest: Manifest,\n linkTargetKey: string\n): string | undefined {\n const prefix = `${linkTargetKey}/`;\n for (const [target, entry] of Object.entries(manifest.managed)) {\n if (target.startsWith(prefix)) {\n return entry.module;\n }\n }\n return undefined;\n}\n\nexport async function buildRemovePlan(\n args: BuildRemovePlanArgs\n): Promise<RemovePlan> {\n const { root, name, config, manifest, lock } = args;\n\n const files: PlannedRemoveFile[] = [];\n for (const [target, entry] of Object.entries(manifest.managed)) {\n if (entry.module !== name) {\n continue;\n }\n const targetAbs = resolveWithinRoot(root, target);\n const oldContent = (await pathExists(targetAbs))\n ? await readFile(targetAbs, \"utf-8\")\n : undefined;\n files.push({\n module: name,\n target,\n targetAbs,\n action: classifyTrackedFile(oldContent, entry.hash),\n oldContent,\n });\n }\n\n const links: PlannedRemoveLink[] = [];\n for (const [target, path] of Object.entries(manifest.links)) {\n if (ownerOfLinkTarget(manifest, target) !== name) {\n continue;\n }\n const targetAbs = resolveWithinRoot(root, target);\n const pathAbs = resolveWithinRoot(root, path);\n const state = await classifyLink(pathAbs, targetAbs);\n links.push({\n module: name,\n target,\n targetAbs,\n path,\n pathAbs,\n action:\n state === \"missing\"\n ? \"missing\"\n : state === \"correct\"\n ? \"remove\"\n : \"conflict\",\n });\n }\n\n const patches: PlannedRemovePatch[] = [];\n for (const entry of manifest.patches) {\n if (entry.module !== name) {\n continue;\n }\n patches.push(await previewPatchRemoval(root, entry));\n }\n\n // A reverse-dependency map over the lock's `dependsOn`, restricted to what's\n // actually installed — an uninstalled module's stale dependsOn doesn't matter.\n const dependents: string[] = [];\n const missingLockEntries: string[] = [];\n for (const installedName of config.installed) {\n if (installedName === name) {\n continue;\n }\n const lockEntry = lock.modules[installedName];\n if (!lockEntry) {\n missingLockEntries.push(installedName);\n continue;\n }\n if (lockEntry.dependsOn?.includes(name)) {\n dependents.push(installedName);\n }\n }\n\n return {\n module: name,\n warnings: [...(manifest.removeWarnings[name] ?? [])],\n files,\n links,\n patches,\n dependents,\n missingLockEntries,\n };\n}\n\n/**\n * Classify one recorded patch against fresh disk state, and preview its reversal.\n *\n * `reversePatch` is pure, so the plan can carry the exact edit `--diff` promises without\n * writing anything — the repo requires every destructive operation to be previewable.\n * `executeRemovePlan` re-reads and recomputes rather than trusting this diff, because the\n * file can change between planning and executing.\n */\nasync function previewPatchRemoval(\n root: string,\n entry: ManifestPatch\n): Promise<PlannedRemovePatch> {\n if (!isReversibleKind(entry.patch.kind)) {\n return { entry, action: \"drop\", diff: \"\" };\n }\n\n const fileAbs = resolveWithinRoot(root, entry.file);\n if (!(await pathExists(fileAbs))) {\n return { entry, action: \"gone\", diff: \"\" };\n }\n await assertNoSymlinkPath(root, fileAbs);\n\n const source = await readFile(fileAbs, \"utf-8\");\n const attempt = tryReversePatch(source, entry);\n if (!attempt.ok) {\n return { entry, action: \"refused\", diff: \"\", reason: attempt.reason };\n }\n const result = attempt.result;\n if (result?.changed) {\n return { entry, action: \"revert\", diff: result.diff };\n }\n if (result?.reason !== undefined) {\n return { entry, action: \"refused\", diff: \"\", reason: result.reason };\n }\n return { entry, action: \"gone\", diff: \"\" };\n}\n\n/** Either the codemod's own verdict, or the parser's complaint about the target file. */\ntype ReversalAttempt =\n { ok: true; result: PatchResult | undefined } | { ok: false; reason: string };\n\n/**\n * `reversePatch` behind a guard. Every inverse parses the target file first, and both\n * parsers throw on syntax they can't read — a state only a hand edit produces, which the\n * remove contract routes to warn-and-skip, not to an aborted command. Turning the throw\n * into a refusal reason keeps the codemods pure (`string => string`, no error channel)\n * and names the file, so the warning says which of the workspace's files to open. Both\n * the plan and the execute pass call this: the plan so a broken target is previewed as a\n * refusal, the execute so a file broken *between* the two doesn't throw after files and\n * links are already deleted.\n */\nfunction tryReversePatch(\n source: string,\n entry: ManifestPatch\n): ReversalAttempt {\n try {\n return { ok: true, result: reversePatch(source, entry.patch, entry.file) };\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return {\n ok: false,\n reason: `${entry.file} does not parse (${detail}) — fix the file, then hand-revert this patch`,\n };\n }\n}\n\nexport interface ExecuteRemoveArgs {\n root: string;\n config: SaasaloyConfig;\n manifest: Manifest;\n lock: Lockfile;\n /**\n * Manifest targets classified `drift` that the caller confirmed to actually\n * delete (an interactive per-file `confirm`). Anything drift-classified but not\n * in this set survives on disk, untracked — the non-interactive `--yes` default\n * (drift never gets silently clobbered).\n */\n deleteDrifted?: ReadonlySet<string>;\n}\n\nexport interface RemoveResult {\n /** Files actually deleted from disk (hash-clean, or drift confirmed for deletion). */\n deleted: PlannedRemoveFile[];\n /** Drift files left on disk, untracked — the designed survivor outcome. */\n driftSurvivors: PlannedRemoveFile[];\n /** Files already gone from disk — just untracked, nothing to delete. */\n missingUntracked: PlannedRemoveFile[];\n /** `.claude/skills` symlinks actually removed. */\n linksRemoved: PlannedRemoveLink[];\n /** Symlinks left untouched because something else occupies their path. */\n linkConflicts: PlannedRemoveLink[];\n /** Patch entries actually undone on disk — a route link, a wrangler binding, a plugin\n * array element, a const array element, each with the import it needed. */\n patchesReversed: ManifestPatch[];\n /** Patch entries untracked without being undone: a kind with no inverse (the two\n * `package.json` ones), a target file that's gone, an edit already hand-reverted, or a\n * refusal. The command warns. */\n patchesDropped: ManifestPatch[];\n /** The subset of `patchesDropped` the inverse codemod actively declined, each with its\n * reason — a route the user repointed at their own handler, say. Separates \"there was\n * nothing left to undo\" from \"that line is yours now\" in what the command prints. */\n patchRefusals: RemovePatchRefusal[];\n /** Project-relative POSIX directories pruned because this run emptied them. */\n prunedDirs: string[];\n /** saasaloy.json alias names dropped because their prefix directory vanished. */\n droppedAliases: string[];\n}\n\n/** A patch entry left on disk on purpose, and the inverse codemod's account of why. */\nexport interface RemovePatchRefusal {\n patch: ManifestPatch;\n reason: string;\n}\n\nfunction toPosixRelative(root: string, abs: string): string {\n return relative(root, abs).split(sep).join(\"/\");\n}\n\n// Walk up from each deleted path's directory, removing directories that are now\n// empty, stopping at the first non-empty ancestor and never past `root`. Only\n// directories a deletion in *this run* could have emptied are ever candidates —\n// we start exclusively from paths this run actually removed.\nasync function pruneEmptyDirs(\n root: string,\n deletedAbsPaths: string[]\n): Promise<string[]> {\n const pruned: string[] = [];\n for (const abs of deletedAbsPaths) {\n let dir = dirname(abs);\n while (dir === root || dir.startsWith(root + sep)) {\n if (dir === root) {\n break;\n }\n if (!(await pathExists(dir))) {\n break;\n } // already pruned via another file's chain\n const entries = await readdir(dir);\n if (entries.length > 0) {\n break;\n } // first non-empty ancestor — stop climbing\n // `recursive` is required by fs.rm to remove a directory at all; safe here\n // since we just confirmed it's empty.\n await rmPath(dir, { recursive: true, force: true });\n pruned.push(toPosixRelative(root, dir));\n dir = dirname(dir);\n }\n }\n return pruned;\n}\n\n// A dangling alias would let a future `add` silently recreate a half-workspace;\n// dropping it makes `resolveTarget` fail loudly (\"Unknown alias\") instead.\nasync function dropDanglingAliases(\n root: string,\n config: SaasaloyConfig\n): Promise<string[]> {\n const dropped: string[] = [];\n for (const [alias, prefix] of Object.entries(config.aliases)) {\n const prefixAbs = join(root, ...prefix.split(\"/\"));\n if (!(await pathExists(prefixAbs))) {\n delete config.aliases[alias];\n dropped.push(alias);\n }\n }\n return dropped;\n}\n\n// Whether a planned file still looks the way it did when the plan was built:\n// unchanged — byte-identical to plan time → the plan's verdict still holds\n// changed — edited since → now user-owned content, must survive\n// gone — deleted since → nothing left to remove\ntype FileRecheck = \"unchanged\" | \"changed\" | \"gone\";\n\n// Arbitrary time passes between buildRemovePlan and here — the per-file drift\n// confirms are interactive and can sit open indefinitely. Re-reading immediately\n// before the delete is what keeps issue #27's \"drift is sacred\" true in the gap:\n// a file the user edited while a prompt was up is drift, whatever the plan said.\nasync function recheckFile(file: PlannedRemoveFile): Promise<FileRecheck> {\n if (!(await pathExists(file.targetAbs))) {\n return \"gone\";\n }\n if (file.oldContent === undefined) {\n return \"changed\";\n } // planned `missing`, something is there now\n const current = await readFile(file.targetAbs, \"utf-8\");\n return hashContent(current) === hashContent(file.oldContent)\n ? \"unchanged\"\n : \"changed\";\n}\n\nexport async function executeRemovePlan(\n plan: RemovePlan,\n args: ExecuteRemoveArgs\n): Promise<RemoveResult> {\n const { root, config, manifest, lock, deleteDrifted } = args;\n\n const deleted: PlannedRemoveFile[] = [];\n const driftSurvivors: PlannedRemoveFile[] = [];\n const missingUntracked: PlannedRemoveFile[] = [];\n\n for (const file of plan.files) {\n // Hash-clean, or drift the caller explicitly confirmed deleting — both are\n // \"delete this exact content\", so both have to prove the content is still that.\n const wantsDelete =\n file.action === \"delete\" ||\n (file.action === \"drift\" && !!deleteDrifted?.has(file.target));\n if (wantsDelete) {\n const state = await recheckFile(file);\n if (state === \"unchanged\") {\n await rmPath(file.targetAbs, { force: true });\n deleted.push(file);\n } else if (state === \"gone\") {\n missingUntracked.push(file);\n } else {\n // Edited between planning and now — the confirm the user gave was for\n // different bytes, so it doesn't authorize deleting these.\n driftSurvivors.push(file);\n }\n } else if (file.action === \"drift\") {\n driftSurvivors.push(file);\n } else {\n missingUntracked.push(file);\n }\n // Untrack in every case — a declined/survivor drift file becomes user-owned\n // (it classifies as a `conflict` on a future re-add, same as any untracked file).\n delete manifest.managed[file.target];\n }\n\n const linksRemoved: PlannedRemoveLink[] = [];\n const linkConflicts: PlannedRemoveLink[] = [];\n for (const link of plan.links) {\n if (link.action === \"remove\") {\n // Same staleness window as files: the link may have been replaced by a real\n // file or repointed since planning, and neither is ours to delete.\n const state = await classifyLink(link.pathAbs, link.targetAbs);\n if (state === \"correct\") {\n await rmPath(link.pathAbs, { force: true });\n linksRemoved.push(link);\n } else if (state === \"conflict\") {\n linkConflicts.push(link);\n }\n // \"missing\" — already gone, nothing to unlink.\n } else if (link.action === \"conflict\") {\n linkConflicts.push(link);\n }\n // Drop the links entry either way (ADR 0015 symmetry with `add`).\n delete manifest.links[link.target];\n }\n\n // Reverse what can be reversed, then untrack every entry either way. The four kinds\n // that edit a config file have an inverse (#83, #36); the two `package.json` kinds stay\n // report-only, and the command warns naming each file.\n //\n // Read fresh disk content rather than trusting the plan, mirroring what `executePlan`\n // does forward: the file may have been hand-edited since the plan was built. The\n // codemod is a no-op when the edit it recorded is already gone, so a hand-reverted\n // file is untracked and warned about instead of force-edited.\n const patchesReversed: ManifestPatch[] = [];\n const patchesDropped: ManifestPatch[] = [];\n const patchRefusals: RemovePatchRefusal[] = [];\n for (const planned of plan.patches) {\n const entry = planned.entry;\n const fileAbs = resolveWithinRoot(root, entry.file);\n let reversed = false;\n if (await pathExists(fileAbs)) {\n // `resolveWithinRoot` proves only that the recorded path is lexically inside the\n // project; the read and write below both follow symlinks. Refuse a linked component\n // rather than reverse a patch into a file outside the root.\n await assertNoSymlinkPath(root, fileAbs);\n const source = await readFile(fileAbs, \"utf-8\");\n const attempt = tryReversePatch(source, entry);\n if (!attempt.ok) {\n patchRefusals.push({ patch: entry, reason: attempt.reason });\n } else if (attempt.result?.changed) {\n await writeFile(fileAbs, attempt.result.content, \"utf-8\");\n reversed = true;\n } else if (attempt.result?.reason !== undefined) {\n patchRefusals.push({ patch: entry, reason: attempt.result.reason });\n }\n }\n (reversed ? patchesReversed : patchesDropped).push(entry);\n // Untrack this entry now, not after the loop. Reversing a patch is a disk write, and\n // `runRemove` saves the manifest from a `finally`: an entry still listed after its\n // file was already rewritten is a ledger that lies about disk. Anything this loop\n // never reached stays tracked, which is the point.\n manifest.patches = manifest.patches.filter(\n (p) => !samePatchEntry(p, entry)\n );\n }\n\n const prunedDirs = await pruneEmptyDirs(root, [\n ...deleted.map((f) => f.targetAbs),\n ...linksRemoved.map((l) => l.pathAbs),\n ]);\n const droppedAliases = await dropDanglingAliases(root, config);\n\n config.installed = config.installed.filter((m) => m !== plan.module);\n delete lock.modules[plan.module];\n delete manifest.removeWarnings[plan.module];\n\n return {\n deleted,\n driftSurvivors,\n missingUntracked,\n linksRemoved,\n linkConflicts,\n patchesReversed,\n patchesDropped,\n patchRefusals,\n prunedDirs,\n droppedAliases,\n };\n}\n","import {\n cancel,\n confirm,\n intro,\n isCancel,\n log,\n note,\n outro,\n select,\n} from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { lineDiff } from \"../lib/diff.js\";\nimport {\n EXIT_FAILURE,\n EXIT_OK,\n EXIT_REFUSED,\n exitCodeFor,\n formatFailure,\n} from \"../lib/exit.js\";\nimport { loadLock, saveLock } from \"../lib/lock.js\";\nimport { loadManifest, saveManifest } from \"../lib/manifest.js\";\nimport { isReversibleKind } from \"../lib/patch/index.js\";\nimport { findProjectRoot } from \"../lib/project.js\";\nimport { buildRemovePlan, executeRemovePlan } from \"../lib/remover.js\";\nimport type {\n FileRemoveAction,\n LinkRemoveAction,\n PatchRemoveAction,\n PlannedRemoveFile,\n RemovePlan,\n} from \"../lib/remover.js\";\nimport { loadConfig, saveConfig } from \"../lib/saasaloy-config.js\";\nimport { isInteractive, wrapForNote } from \"../lib/tui.js\";\nimport { uiBlockFiles } from \"../lib/ui-blocks.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\n\n// `saasaloy remove <module>` — the local undo, mirroring `add`'s clack UX and flag\n// surface. Fully offline: the plan derives entirely from manifest.json,\n// saasaloy.json and saasaloy-lock.json (issue #27). `--dry-run`/`--diff` preview\n// without mutating; a drifted managed file is never silently clobbered.\n\ninterface Options {\n name?: string;\n dryRun: boolean;\n diff: boolean;\n yes: boolean;\n force: boolean;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\n \"--dry-run\",\n \"--diff\",\n \"--yes\",\n \"-y\",\n \"--force\",\n \"--help\",\n \"-h\",\n]);\nconst USAGE =\n \"saasaloy remove [<module>] [--dry-run] [--diff] [--yes] [--force]\";\nconst HELP: CommandHelp = {\n name: \"remove\",\n describe: DESCRIPTIONS.remove,\n usage: USAGE,\n flags: {\n \"--dry-run\": \"show the plan and delete nothing\",\n \"--diff\": \"show what each deletion removes, and delete nothing\",\n \"-y, --yes\": \"skip the confirmation prompt (drifted files are kept)\",\n \"--force\": \"remove even while another module depends on it\",\n },\n};\n\nfunction parseArgs(argv: string[]): Options {\n const positional: string[] = [];\n const unknown: string[] = [];\n for (const arg of argv) {\n if (!arg.startsWith(\"-\")) {\n positional.push(arg);\n } else if (!KNOWN_FLAGS.has(arg)) {\n unknown.push(arg);\n }\n }\n unknown.push(...positional.slice(1));\n return {\n name: positional[0],\n dryRun: argv.includes(\"--dry-run\"),\n diff: argv.includes(\"--diff\"),\n yes: argv.includes(\"--yes\") || argv.includes(\"-y\"),\n force: argv.includes(\"--force\"),\n unknown,\n };\n}\n\nconst FILE_ACTION_LABEL: Record<FileRemoveAction, string> = {\n delete: pc.red(\"delete\"),\n drift: pc.yellow(\"drift → confirm\"),\n missing: pc.dim(\"missing → untrack\"),\n};\n\n// Under --yes the confirm loop never runs (see the `if (!opts.yes)` gate below), so a\n// drifted file always survives untracked rather than pausing for a prompt — label it\n// accordingly instead of implying a confirmation that won't happen.\nfunction fileActionLabel(action: FileRemoveAction, yes: boolean): string {\n if (action === \"drift\" && yes) {\n return pc.yellow(\"drift → kept (untracked)\");\n }\n return FILE_ACTION_LABEL[action];\n}\n\nconst LINK_ACTION_LABEL: Record<LinkRemoveAction, string> = {\n remove: pc.red(\"unlink\"),\n missing: pc.dim(\"missing\"),\n conflict: pc.yellow(\"conflict → left\"),\n};\n\nconst PATCH_ACTION_LABEL: Record<PatchRemoveAction, string> = {\n revert: pc.red(\"revert\"),\n refused: pc.yellow(\"drift → left\"),\n gone: pc.dim(\"already gone\"),\n drop: pc.dim(\"untrack\"),\n};\n\n// Cap a single file's deletion diff so a big generated file can't flood the terminal\n// (same cap as `add`'s renderDiff).\nconst MAX_DIFF_LINES = 60;\n\nfunction renderDeleteDiff(file: PlannedRemoveFile): string {\n const lines = lineDiff(file.oldContent ?? \"\", \"\");\n const shown = lines\n .slice(0, MAX_DIFF_LINES)\n .map((line) => pc.red(`- ${line.text}`));\n if (lines.length > MAX_DIFF_LINES) {\n shown.push(pc.dim(` … ${lines.length - MAX_DIFF_LINES} more lines`));\n }\n return shown.join(\"\\n\");\n}\n\nfunction summarizeRemovePlan(\n plan: RemovePlan,\n name: string,\n yes: boolean\n): void {\n const lines = [`will remove: ${pc.cyan(name)}`, \"\"];\n for (const file of plan.files) {\n lines.push(` ${fileActionLabel(file.action, yes)} ${file.target}`);\n }\n const toDelete = plan.files.filter((f) => f.action === \"delete\").length;\n const drifted = plan.files.filter((f) => f.action === \"drift\").length;\n const missing = plan.files.filter((f) => f.action === \"missing\").length;\n lines.push(\"\");\n const driftNote = yes ? \"kept untracked\" : \"need confirmation\";\n lines.push(\n pc.dim(\n `${toDelete} file(s) to delete, ${drifted} drifted (${driftNote}), ${missing} already missing`\n )\n );\n note(wrapForNote(lines.join(\"\\n\")), \"Plan\");\n\n const ownedLinks = plan.links.filter((l) => l.action !== \"missing\");\n if (ownedLinks.length > 0) {\n const linkLines = ownedLinks.map(\n (l) => `${LINK_ACTION_LABEL[l.action]} ${l.path}`\n );\n note(wrapForNote(linkLines.join(\"\\n\")), \"Skill links\");\n }\n // Conflicted skill links are warned about once, post-execute (see `result.linkConflicts`\n // below) — that reflects what actually happened, so we don't also warn here from the plan.\n\n // A dependents refusal (without --force) returns before this is ever called, so\n // reaching here with dependents means --force is overriding it.\n if (plan.dependents.length > 0) {\n log.warn(\n `${pc.cyan(name)} is still depended on by ${plan.dependents.join(\", \")} — proceeding anyway (--force).`\n );\n }\n for (const missingLock of plan.missingLockEntries) {\n log.warn(\n `Installed module ${pc.cyan(missingLock)} has no lock entry — dependent detection is incomplete for it.`\n );\n }\n\n // A kind with an inverse is listed as work rather than warned about; what actually\n // happened is reported post-execute (#83, #36).\n const reversible = plan.patches.filter((p) => p.action !== \"drop\");\n if (reversible.length > 0) {\n const patchLines = reversible.map(\n (p) =>\n ` ${PATCH_ACTION_LABEL[p.action]} ${p.entry.file} ${pc.dim(`(${p.entry.patch.kind})`)}${p.reason === undefined ? \"\" : `\\n ${pc.dim(p.reason)}`}`\n );\n note(wrapForNote(patchLines.join(\"\\n\")), \"Config patches\");\n }\n for (const p of plan.patches) {\n if (p.action !== \"drop\") {\n continue;\n }\n log.warn(\n `Config patch on ${pc.cyan(p.entry.file)} ${pc.dim(`(${p.entry.patch.kind})`)} is not reversed by \\`remove\\` — hand-revert it if needed.`\n );\n }\n}\n\nexport async function runRemove(argv: string[]): Promise<number> {\n // Parse before help answers, so a typo'd flag alongside `--help` still reports the typo.\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n intro(pc.bgCyan(pc.black(\" saasaloy remove \")));\n\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n\n let root: string;\n let config: Awaited<ReturnType<typeof loadConfig>>;\n try {\n root = await findProjectRoot();\n config = await loadConfig(root);\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n }\n\n try {\n let name = opts.name;\n if (!name) {\n if (config.installed.length === 0) {\n note(\"Nothing installed.\", \"Nothing to do\");\n outro(pc.dim(\"0 modules\"));\n return EXIT_OK;\n }\n // Same hazard as `add`: without a terminal this prompt can never be answered, so\n // it would hang rather than fail.\n if (!isInteractive()) {\n cancel(\n `No module named and no terminal to pick one in — usage: \\`${USAGE}\\`.`\n );\n return EXIT_REFUSED;\n }\n const picked = await select({\n message: \"Pick a module to remove\",\n options: config.installed.map((n) => ({ value: n, label: n })),\n });\n if (isCancel(picked)) {\n cancel(\"remove cancelled\");\n return EXIT_FAILURE;\n }\n name = picked;\n }\n\n if (!config.installed.includes(name)) {\n cancel(`${pc.cyan(name)} isn't installed — nothing to remove.`);\n return EXIT_REFUSED;\n }\n\n const manifest = await loadManifest(root);\n const lock = await loadLock(root);\n const plan = await buildRemovePlan({ root, name, config, manifest, lock });\n\n if (plan.dependents.length > 0 && !opts.force) {\n cancel(\n `${pc.cyan(name)} is still depended on by ${plan.dependents.join(\", \")} — refusing ` +\n `${pc.dim(\"(use --force to remove it anyway)\")}.`\n );\n return EXIT_REFUSED;\n }\n\n summarizeRemovePlan(plan, name, opts.yes);\n for (const warning of plan.warnings) {\n log.warn(warning);\n }\n\n if (opts.diff) {\n for (const file of plan.files) {\n if (file.action === \"missing\") {\n continue;\n }\n note(\n renderDeleteDiff(file),\n `${fileActionLabel(file.action, opts.yes)} ${file.target}`\n );\n }\n // A reversal is a destructive edit to someone else's file, so it previews like one.\n // The diff comes from the plan's pure `reversePatch` run; nothing has been written.\n for (const p of plan.patches) {\n if (p.action !== \"revert\") {\n continue;\n }\n note(p.diff, `${pc.red(\"revert\")} ${p.entry.file}`);\n }\n }\n\n // --dry-run and --diff both preview only; nothing is written.\n if (opts.dryRun || opts.diff) {\n outro(\n pc.dim(\n opts.diff\n ? \"diff only — nothing removed\"\n : \"dry run — nothing removed\"\n )\n );\n return EXIT_OK;\n }\n\n // Drift is sacred: confirm per file before deleting hand-edited content.\n // Under --yes, no prompts run at all — drifted files always survive, untracked.\n const deleteDrifted = new Set<string>();\n if (!opts.yes) {\n for (const file of plan.files) {\n if (file.action !== \"drift\") {\n continue;\n }\n const proceed = await confirm({\n message: `${file.target} was hand-edited since it was applied — delete it anyway?`,\n initialValue: false,\n });\n if (isCancel(proceed)) {\n cancel(\"remove cancelled\");\n return EXIT_FAILURE;\n }\n if (proceed) {\n deleteDrifted.add(file.target);\n }\n }\n }\n\n if (!opts.yes) {\n const proceed = await confirm({ message: \"Proceed?\" });\n if (isCancel(proceed)) {\n cancel(\"remove cancelled\");\n return EXIT_FAILURE;\n }\n if (!proceed) {\n outro(pc.dim(\"aborted — nothing removed\"));\n return EXIT_OK;\n }\n }\n\n let result: Awaited<ReturnType<typeof executeRemovePlan>>;\n try {\n result = await executeRemovePlan(plan, {\n root,\n config,\n manifest,\n lock,\n deleteDrifted,\n });\n } finally {\n // Record whatever actually happened even if a mid-plan step failed — the\n // ledger must reflect the real on-disk state (same rationale as `add`).\n await saveManifest(root, manifest);\n await saveConfig(root, config);\n await saveLock(root, lock);\n }\n\n for (const file of result.deleted) {\n log.step(`${pc.red(\"delete\")} ${file.target}`);\n }\n for (const file of result.missingUntracked) {\n log.step(\n `${pc.dim(\"untrack\")} ${file.target} ${pc.dim(\"(already gone)\")}`\n );\n }\n for (const link of result.linksRemoved) {\n log.step(`${pc.red(\"unlink\")} ${link.path}`);\n }\n for (const link of result.linkConflicts) {\n log.warn(\n `Skill link ${pc.cyan(link.path)} left untouched — not ours to remove.`\n );\n }\n for (const p of result.patchesReversed) {\n log.step(`${pc.red(\"revert\")} ${p.file} ${pc.dim(`(${p.patch.kind})`)}`);\n }\n // A reversible entry that landed in `patchesDropped` either had nothing left to undo —\n // the link was hand-removed, or the file is gone — or the inverse refused it, which is\n // a different thing to say. Never claim a revert; the non-reversible kinds were already\n // warned about from the plan.\n const refusals = new Map(\n result.patchRefusals.map((r) => [r.patch, r.reason])\n );\n for (const p of result.patchesDropped) {\n if (!isReversibleKind(p.patch.kind)) {\n continue;\n }\n const reason = refusals.get(p);\n log.warn(\n reason === undefined\n ? `Config patch on ${pc.cyan(p.file)} ${pc.dim(`(${p.patch.kind})`)} was already gone — nothing to revert.`\n : `Config patch on ${pc.cyan(p.file)} ${pc.dim(`(${p.patch.kind})`)} left untouched — ${reason}.`\n );\n }\n for (const dir of result.prunedDirs) {\n log.step(`${pc.dim(\"prune\")} ${dir}`);\n }\n for (const alias of result.droppedAliases) {\n log.warn(\n `Alias ${pc.cyan(alias)} dropped — its target directory is gone.`\n );\n }\n\n // A block was placed by hand, so it is un-placed by hand. `add` wrote the file and\n // printed a pointer to the module's skill; nothing recorded which page the owner\n // imported it into, so the deletion here leaves a live import pointing at a file that\n // no longer exists. Say it plainly rather than let the next build say it (#62).\n const removedBlocks = uiBlockFiles(result.deleted, config.aliases);\n if (removedBlocks.length > 0) {\n const blockLines = removedBlocks\n .map((f) => ` ${pc.red(\"deleted\")} ${f.target}`)\n .join(\"\\n\");\n note(\n wrapForNote(\n `${blockLines}\\n\\n${pc.dim(\"The wire-up you added by hand is not reversed — nothing recorded which page you put it on. Take the import and its tag out yourself, or the build fails.\")}`\n ),\n \"Wire-up left behind\"\n );\n }\n\n // Drift survivors are the designed outcome, not a failure — exit 0 either way.\n if (result.driftSurvivors.length > 0) {\n const survivorLines = result.driftSurvivors\n .map((f) => ` ${pc.yellow(\"kept\")} ${f.target}`)\n .join(\"\\n\");\n note(\n wrapForNote(\n `${survivorLines}\\n\\n${pc.dim(\"Hand-edited since — left on disk, now untracked.\")}`\n ),\n \"Drift survivors\"\n );\n }\n\n outro(\n pc.green(\n `Removed ${pc.bold(name)} ${pc.dim(`(${result.deleted.length} files)`)}`\n )\n );\n return EXIT_OK;\n } catch (error) {\n cancel(formatFailure(error));\n return exitCodeFor(error);\n }\n}\n","import { mkdir, realpath, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, isAbsolute, join, resolve } from \"node:path\";\nimport {\n cancel,\n confirm,\n intro,\n isCancel,\n log,\n note,\n outro,\n} from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport {\n detectCliMismatches,\n formatCliMismatches,\n UPGRADE_COMMAND,\n} from \"../lib/cli-requires.js\";\nimport {\n adoptBase,\n BASE_MODULE,\n baseUpdateInput,\n isBaseTracked,\n missingBaseTargets,\n templateHash,\n} from \"../lib/base.js\";\nimport { restoreLatestBackup, writeBackup } from \"../lib/backup.js\";\nimport { detectConflicts, formatConflicts } from \"../lib/conflicts.js\";\nimport { lineDiff } from \"../lib/diff.js\";\nimport {\n EXIT_FAILURE,\n EXIT_MERGE_PENDING,\n EXIT_OK,\n EXIT_REFUSED,\n exitCodeFor,\n formatFailure,\n} from \"../lib/exit.js\";\nimport { workingTreeState } from \"../lib/git-state.js\";\nimport { resolveProjectName } from \"../lib/project-name.js\";\nimport { LOCK_FILE, loadLock, saveLock } from \"../lib/lock.js\";\nimport { loadManifest, MANIFEST_FILE, saveManifest } from \"../lib/manifest.js\";\nimport { renderMergePlan } from \"../lib/merge-plan.js\";\nimport { readRootPackageJson } from \"../lib/pkg-json.js\";\nimport { findProjectRoot } from \"../lib/project.js\";\nimport {\n createRegistrySource,\n REGISTRY_ENV,\n RemoteRegistrySource,\n} from \"../lib/registry.js\";\nimport type { LoadedModule, RegistrySource } from \"../lib/registry.js\";\nimport type { Graph } from \"../lib/resolve.js\";\nimport { resolveGraph } from \"../lib/resolve.js\";\nimport { CONFIG_FILE, loadConfig, saveConfig } from \"../lib/saasaloy-config.js\";\nimport { baseTemplateDir } from \"../lib/scaffold.js\";\nimport { compareVersions, parseVersion } from \"../lib/semver.js\";\nimport { TUI_ON_STDERR, wrapForNote } from \"../lib/tui.js\";\nimport type { CommandHelp } from \"../lib/usage.js\";\nimport { printCommandHelp, wantsHelp } from \"../lib/usage.js\";\nimport { readVersion } from \"../version.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\nimport {\n buildUpdatePlan,\n compareBase,\n compareInstalled,\n executeUpdatePlan,\n recordRefRewrites,\n} from \"../lib/updater.js\";\nimport type {\n ModuleComparison,\n ModuleUpdateInput,\n ModuleUpdatePlan,\n PlannedUpdateFile,\n UpdateFileAction,\n UpdatePlan,\n UpdateResult,\n} from \"../lib/updater.js\";\n\n// `saasaloy update [<module>]` — re-resolve each installed module's ref, apply what can\n// be applied deterministically, and emit an agent-consumable merge plan for everything\n// that can't (issue #48). The copy-in tool's answer to \"you own the code, so you can\n// never update it\": clean files are overwritten, hand-edited files are never touched,\n// and the difference between the two is written down in enough detail for an agent to\n// resolve it.\n//\n// stdout belongs to the merge plan and nothing else, so the whole clack TUI goes to\n// stderr (`TUI_ON_STDERR`) and `saasaloy update email | claude` works with no flag.\n//\n// The base template rides the same engine (#120, ADR 0032). A bare run considers the base\n// and every module; `update base` considers the base alone. The bundled template is\n// rendered for this project and classified like a module with no merge base. A project\n// with no base record is *adopted* first — its base files are recorded as they stand at\n// the running CLI — and the run stops there, so the next run is a real update. This is\n// the only command that writes that record; `outdated` and `doctor` report `untracked`.\n\ninterface Options {\n name?: string;\n /** `--ref <branch|tag|sha>`: the explicit unpin. Requires a named module. */\n ref?: string;\n /** `--out <path>`: write the merge plan here instead of stdout. */\n out?: string;\n dryRun: boolean;\n diff: boolean;\n yes: boolean;\n /** `--force`: apply even though the git working tree has uncommitted changes. */\n force: boolean;\n /** `--abort`: restore the newest pre-update backup and exit. */\n abort: boolean;\n /** Flags we don't know and extra positionals — reported, never silently ignored. */\n unknown: string[];\n}\n\nconst KNOWN_FLAGS = new Set([\n \"--dry-run\",\n \"--diff\",\n \"--yes\",\n \"-y\",\n \"--force\",\n \"--abort\",\n \"--help\",\n \"-h\",\n]);\nconst VALUE_FLAGS = new Set([\"--ref\", \"--out\"]);\nconst USAGE =\n \"saasaloy update [<module>|base] [--ref <ref>] [--out <path>] [--dry-run] [--diff] [--force] [--yes] | saasaloy update --abort\";\nconst HELP: CommandHelp = {\n name: \"update\",\n describe: DESCRIPTIONS.update,\n usage: USAGE,\n flags: {\n \"--ref <ref>\":\n \"update one module to this branch, tag, or SHA (not the base)\",\n \"--out <path>\": \"write the merge plan to a file instead of stdout\",\n \"--dry-run\": \"show the plan and write nothing\",\n \"--diff\": \"show a per-file diff and write nothing\",\n \"--force\": \"apply even with uncommitted changes in the working tree\",\n \"--abort\": \"restore the files the last update replaced, then stop\",\n \"-y, --yes\": \"skip the confirmation prompt\",\n },\n};\n\nfunction parseArgs(argv: string[]): Options {\n const positional: string[] = [];\n const unknown: string[] = [];\n const values: Record<string, string> = {};\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!;\n if (!arg.startsWith(\"-\")) {\n positional.push(arg);\n continue;\n }\n // Accept both `--ref v2` and `--ref=v2`; a value flag with nothing after it is a\n // usage error, not a silently-empty ref. In the space-separated form the next token\n // must not itself be a flag, or `--ref --dry-run` pins the ref to \"--dry-run\" and\n // reaches the registry with it instead of reporting the usage error.\n const eq = arg.indexOf(\"=\");\n const flag = eq === -1 ? arg : arg.slice(0, eq);\n if (VALUE_FLAGS.has(flag)) {\n const next = eq === -1 ? argv[i + 1] : arg.slice(eq + 1);\n if (!next || (eq === -1 && next.startsWith(\"-\"))) {\n unknown.push(`${flag} (missing value)`);\n continue;\n }\n if (eq === -1) {\n i++;\n }\n values[flag] = next;\n continue;\n }\n if (!KNOWN_FLAGS.has(arg)) {\n unknown.push(arg);\n }\n }\n unknown.push(...positional.slice(1));\n\n return {\n name: positional[0],\n ref: values[\"--ref\"],\n out: values[\"--out\"],\n dryRun: argv.includes(\"--dry-run\"),\n diff: argv.includes(\"--diff\"),\n yes: argv.includes(\"--yes\") || argv.includes(\"-y\"),\n force: argv.includes(\"--force\"),\n abort: argv.includes(\"--abort\"),\n unknown,\n };\n}\n\nconst ACTION_LABEL: Record<UpdateFileAction, string> = {\n skip: pc.dim(\"unchanged upstream\"),\n create: pc.green(\"create\"),\n overwrite: pc.cyan(\"update\"),\n restore: pc.green(\"restore\"),\n unchanged: pc.dim(\"unchanged\"),\n drift: pc.yellow(\"drift → merge\"),\n conflict: pc.yellow(\"conflict → merge\"),\n delete: pc.red(\"delete\"),\n \"delete-drift\": pc.yellow(\"dropped → kept\"),\n \"delete-missing\": pc.dim(\"untrack\"),\n};\n\n// Actions worth a line in the summary — `skip`/`unchanged` are counted, not listed, so\n// a big module doesn't bury the handful of paths that actually move.\nconst QUIET: ReadonlySet<UpdateFileAction> = new Set<UpdateFileAction>([\n \"skip\",\n \"unchanged\",\n]);\n\n// Cap a single file's diff so a big generated file can't flood the terminal (same cap as `add`).\nconst MAX_DIFF_LINES = 60;\n\nfunction renderDiff(file: PlannedUpdateFile): string {\n const lines = lineDiff(file.mine ?? \"\", file.theirs ?? \"\");\n const shown = lines.slice(0, MAX_DIFF_LINES).map((line) => {\n switch (line.kind) {\n case \"add\": {\n return pc.green(`+ ${line.text}`);\n }\n case \"del\": {\n return pc.red(`- ${line.text}`);\n }\n default: {\n return pc.dim(` ${line.text}`);\n }\n }\n });\n if (lines.length > MAX_DIFF_LINES) {\n shown.push(pc.dim(` … ${lines.length - MAX_DIFF_LINES} more lines`));\n }\n return shown.join(\"\\n\");\n}\n\nfunction short(sha: string): string {\n return /^[0-9a-f]{40}$/i.test(sha) ? sha.slice(0, 7) : sha;\n}\n\nfunction summarizeModule(mod: ModuleUpdatePlan): string[] {\n const lines = [\n `${pc.cyan(mod.name)} ${pc.dim(`${short(mod.comparison.current)} → ${short(mod.comparison.latest)} (${mod.comparison.ref})`)}`,\n ];\n if (mod.noMergeBase) {\n lines.push(pc.dim(` no merge base — ${mod.noMergeBase}`));\n }\n for (const name of mod.prereqNames) {\n lines.push(\n ` ${pc.green(\"install\")} ${pc.cyan(name)} ${pc.dim(\"(new prerequisite)\")}`\n );\n }\n for (const file of [...mod.files, ...mod.removals]) {\n if (QUIET.has(file.action)) {\n continue;\n }\n lines.push(` ${ACTION_LABEL[file.action]} ${file.target}`);\n }\n const quiet = [...mod.files, ...mod.removals].filter((f) =>\n QUIET.has(f.action)\n ).length;\n if (quiet > 0) {\n lines.push(pc.dim(` ${quiet} file(s) already up to date`));\n }\n for (const bump of mod.depBumps) {\n lines.push(\n ` ${pc.cyan(\"bump\")} ${bump.name} ${pc.dim(`${bump.from} → ${bump.to}`)}`\n );\n }\n for (const dep of [...mod.depAdds, ...mod.devDepAdds]) {\n lines.push(` ${pc.green(\"dep\")} ${dep.name}@${dep.version}`);\n }\n return lines;\n}\n\nfunction summarizePlan(plan: UpdatePlan): void {\n const lines: string[] = [];\n for (const mod of plan.modules) {\n lines.push(...summarizeModule(mod), \"\");\n }\n const merging = plan.modules.reduce(\n (n, m) =>\n n +\n m.files.filter((f) => f.action === \"drift\" || f.action === \"conflict\")\n .length +\n m.removals.filter((f) => f.action === \"delete-drift\").length,\n 0\n );\n const writing = plan.modules.reduce(\n (n, m) =>\n n +\n m.files.filter(\n (f) =>\n !QUIET.has(f.action) &&\n f.action !== \"drift\" &&\n f.action !== \"conflict\"\n ).length,\n 0\n );\n lines.push(pc.dim(`${writing} file(s) to apply, ${merging} needing merge`));\n note(wrapForNote(lines.join(\"\\n\")), \"Plan\", TUI_ON_STDERR);\n\n // A module with no lock entry already arrives here as an `unresolvable` comparison\n // carrying that reason, so `plan.missingLockEntries` isn't reported a second time.\n for (const comparison of plan.skipped) {\n log.info(\n `${pc.cyan(comparison.name)} ${pc.dim(`— ${skipReason(comparison)}`)}`,\n TUI_ON_STDERR\n );\n }\n for (const mod of plan.modules) {\n for (const conflict of mod.depConflicts) {\n log.warn(`Dependency version conflict — ${conflict}.`, TUI_ON_STDERR);\n }\n for (const patch of mod.patches) {\n if (patch.action === \"missing\") {\n log.warn(\n `Config patch target ${pc.cyan(patch.file)} is missing — ${patch.patch.kind} not applied.`,\n TUI_ON_STDERR\n );\n }\n }\n }\n // Named before the confirmation, in their own box, because a secret the new version\n // started requiring is the one thing that breaks the project after a clean update and\n // the one thing the file list can't tell you (#98).\n const envVars = new Map<string, string>();\n for (const mod of plan.modules) {\n for (const [key, description] of Object.entries(mod.newEnvVars)) {\n envVars.set(key, description);\n }\n }\n if (envVars.size > 0) {\n const envLines = [...envVars].map(\n ([key, description]) => `${pc.cyan(key)} ${pc.dim(`— ${description}`)}`\n );\n note(wrapForNote(envLines.join(\"\\n\")), \"Env vars to set\", TUI_ON_STDERR);\n }\n if (plan.migrationCommand) {\n note(\n wrapForNote(\n `${pc.cyan(plan.migrationCommand)}\\n\\n${pc.dim(\"The update changed the database schema — run this to regenerate migrations.\")}`\n ),\n \"Migrations\",\n TUI_ON_STDERR\n );\n }\n note(\n wrapForNote(\n `${pc.cyan(plan.verifyCommand)}\\n\\n${pc.dim(\"Named, not run — an update usually precedes `pnpm install`.\")}`\n ),\n \"Verify with\",\n TUI_ON_STDERR\n );\n}\n\nfunction skipReason(comparison: ModuleComparison, preview = false): string {\n switch (comparison.status) {\n case \"current\": {\n const at = `already at ${short(comparison.current)}`;\n if (!comparison.refRewrite) {\n return at;\n }\n // The SHA didn't move but the ref did — say so, or \"already at <sha7>\" would read\n // as though `--ref` had been ignored.\n return preview\n ? `${at} — \\`--ref\\` would move the lock onto \\`${comparison.ref}\\``\n : `${at} — now tracking \\`${comparison.ref}\\``;\n }\n default: {\n return comparison.detail ?? comparison.status;\n }\n }\n}\n\n// The three files Saasaloy owns. `--out` writes after the ledger saves, so a path\n// pointing at one of these replaces real state with merge-plan prose and breaks the\n// next command. Refuse the path up front rather than at the write.\nconst STATE_FILES = [CONFIG_FILE, LOCK_FILE, MANIFEST_FILE];\n\n/** Why `--out <path>` is refused, or `undefined` when the path is safe to write. */\nasync function stateFileRefusal(\n root: string,\n out: string | undefined\n): Promise<undefined | string> {\n if (!out) {\n return undefined;\n }\n const target = await canonicalPath(resolvePlanOut(out));\n for (const file of STATE_FILES) {\n if ((await canonicalPath(resolve(root, file))) === target) {\n return `Refusing to write the merge plan to ${pc.cyan(out)} — that is Saasaloy's own ${pc.cyan(file)}. Pick another path.`;\n }\n }\n return undefined;\n}\n\n// Compare through realpath, not lexically: `/var/…` is a symlink to `/private/var/…` on\n// macOS, so two absolute paths naming the same file can differ character for character\n// and a lexical check would wave the state file through. A path that isn't on disk yet\n// still has a real parent, and one that has neither is already lexically resolved.\nasync function canonicalPath(path: string): Promise<string> {\n try {\n return await realpath(path);\n } catch {\n try {\n return join(await realpath(dirname(path)), basename(path));\n } catch {\n return path;\n }\n }\n}\n\n/** `--out <path>` resolved against the working directory, the one place that mapping lives. */\nfunction resolvePlanOut(out: string): string {\n return isAbsolute(out) ? out : resolve(process.cwd(), out);\n}\n\n/** Emit the merge plan: `--out <path>` when given, otherwise stdout — and nothing else ever. */\nasync function emitMergePlan(\n document: string,\n out: string | undefined\n): Promise<void> {\n if (!out) {\n process.stdout.write(document);\n return;\n }\n const target = resolvePlanOut(out);\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, document, \"utf-8\");\n log.success(`Merge plan written to ${pc.cyan(out)}`, TUI_ON_STDERR);\n}\n\n/**\n * Every project-relative path the plan may write or delete — what the backup copies. A\n * file the plan will create has nothing to copy; `writeBackup` records it as absent so\n * `--abort` deletes it again.\n */\nfunction backupTargets(plan: UpdatePlan): string[] {\n const targets = new Set<string>();\n for (const mod of plan.modules) {\n for (const file of [...mod.files, ...mod.removals]) {\n if (!QUIET.has(file.action) && file.action !== \"skip\") {\n targets.add(file.target);\n }\n }\n for (const patch of mod.patches) {\n targets.add(patch.file);\n }\n }\n return [...targets].toSorted();\n}\n\n/** `owner/repo` → the two halves, or undefined when the lock's source isn't a remote slug. */\nfunction splitSlug(slug: string): [string, string] | undefined {\n const [owner, repo, ...rest] = slug.split(\"/\");\n if (!owner || !repo || rest.length > 0) {\n return undefined;\n }\n return [owner, repo];\n}\n\nexport async function runUpdate(argv: string[]): Promise<number> {\n // Parse before help answers, so a typo'd flag alongside `--help` still reports the typo.\n const opts = parseArgs(argv);\n if (opts.unknown.length === 0 && wantsHelp(argv)) {\n printCommandHelp(HELP);\n return EXIT_OK;\n }\n\n intro(pc.bgCyan(pc.black(\" saasaloy update \")), TUI_ON_STDERR);\n\n if (opts.unknown.length > 0) {\n cancel(\n `Unknown argument(s): ${opts.unknown.join(\", \")} — usage: \\`${USAGE}\\`.`,\n TUI_ON_STDERR\n );\n return EXIT_REFUSED;\n }\n // A bare `update --ref v2` would silently move every module onto one ref, which is\n // never what someone means — the unpin is per module by construction (decision 10).\n if (opts.ref && !opts.name) {\n cancel(\n `\\`--ref\\` needs an explicit module — usage: \\`${USAGE}\\`.`,\n TUI_ON_STDERR\n );\n return EXIT_REFUSED;\n }\n\n let root: string;\n let config: Awaited<ReturnType<typeof loadConfig>>;\n try {\n root = await findProjectRoot();\n config = await loadConfig(root);\n } catch (error) {\n cancel(formatFailure(error), TUI_ON_STDERR);\n return exitCodeFor(error);\n }\n\n // `--abort` is the whole command when it is given: it reads no registry, builds no\n // plan, and only puts back what the last run replaced.\n if (opts.abort) {\n try {\n const restored = await restoreLatestBackup(root);\n for (const target of restored.restored) {\n log.step(`${pc.green(\"restore\")} ${target}`, TUI_ON_STDERR);\n }\n for (const target of restored.removed) {\n log.step(\n `${pc.red(\"remove\")} ${target} ${pc.dim(\"(the update created it)\")}`,\n TUI_ON_STDERR\n );\n }\n outro(\n pc.green(\n `Restored ${restored.restored.length} file${restored.restored.length === 1 ? \"\" : \"s\"} from ${restored.from}.`\n ),\n TUI_ON_STDERR\n );\n return EXIT_OK;\n } catch (error) {\n cancel(formatFailure(error), TUI_ON_STDERR);\n return exitCodeFor(error);\n }\n }\n\n const outRefusal = await stateFileRefusal(root, opts.out);\n if (outRefusal) {\n cancel(outRefusal, TUI_ON_STDERR);\n return EXIT_REFUSED;\n }\n\n // The merge plan goes to stdout, so `saasaloy update email | claude` is the designed\n // pipeline and a redirected stdout says nothing about who is watching. This command\n // used to read it as \"a script is driving me\" and set `--yes` for itself, which made\n // `saasaloy update | tee log` overwrite every clean file unconfirmed (#98). stdin is\n // the stream the confirmation actually reads: no terminal there means the prompt can\n // never be answered, so refuse and name the two ways forward. A preview writes nothing,\n // so it is exempt.\n const preview = opts.dryRun || opts.diff;\n\n // Uncommitted work plus an update that writes files leaves nothing to compare the\n // result against. A preview writes nothing, so it is exempt; `--force` says the user\n // has read this and means it.\n if (!preview && !opts.force && (await workingTreeState(root)) === \"dirty\") {\n cancel(\n \"The working tree has uncommitted changes. Commit or stash them first, so `git diff` \" +\n \"shows what this update did — or re-run with `--force`. Nothing was written.\",\n TUI_ON_STDERR\n );\n return EXIT_REFUSED;\n }\n\n if (!opts.yes && !preview && !process.stdin.isTTY) {\n cancel(\n \"No terminal to confirm in — re-run with `--yes` to apply, or `--dry-run` to preview.\",\n TUI_ON_STDERR\n );\n // Refused by design, like every other bad-usage exit here (#98's 0/1/2 scheme).\n return EXIT_REFUSED;\n }\n\n const sources: RegistrySource[] = [];\n // Temp renders of the bundled template, dropped with the sources.\n const cleanups: (() => Promise<void>)[] = [];\n try {\n // `update base` names the template, which ships with the CLI and has no ref to move.\n const wantsBase = !opts.name || opts.name === BASE_MODULE;\n const wantsModules = opts.name !== BASE_MODULE;\n if (!wantsModules && opts.ref) {\n cancel(\n `\\`--ref\\` moves a module's ref; the base template ships with the CLI — usage: \\`${USAGE}\\`.`,\n TUI_ON_STDERR\n );\n return EXIT_REFUSED;\n }\n if (wantsModules && opts.name && !config.installed.includes(opts.name)) {\n cancel(\n `${pc.cyan(opts.name)} isn't installed — nothing to update.`,\n TUI_ON_STDERR\n );\n return EXIT_REFUSED;\n }\n const targets = wantsModules\n ? opts.name\n ? [opts.name]\n : config.installed\n : [];\n\n const manifest = await loadManifest(root);\n const lock = await loadLock(root);\n const registryOverride = !!process.env[REGISTRY_ENV];\n const cliVersion = await readVersion();\n\n // The base first (#120): adopt an unrecorded project and stop, refuse a downgrade,\n // then compare the recorded template hash against the one this CLI ships.\n let baseComparison: ModuleComparison | undefined;\n let templateDir: string | undefined;\n // The name every `{{PROJECT_NAME}}` in the template renders to. Read from the project,\n // never from the directory: a worktree or a CI checkout path is not a rename (#144).\n const project = await resolveProjectName(root, config);\n if (wantsBase) {\n templateDir = await baseTemplateDir();\n if (project.source === \"directory\") {\n log.warn(\n `No \\`name\\` in ${CONFIG_FILE} and no root package.json name — falling back to the directory ` +\n `name ${pc.cyan(project.name)}. Set \\`name\\` in ${CONFIG_FILE} if that isn't this project's name.`,\n TUI_ON_STDERR\n );\n }\n if (!isBaseTracked(lock, manifest)) {\n const adoption = await adoptBase({\n root,\n cliVersion,\n projectName: project.name,\n manifest,\n lock,\n templateDir,\n dryRun: preview,\n });\n const count = adoption.adopted.length;\n const lines = [\n `${preview ? \"Would adopt\" : \"Adopted\"} ${count} base file${count === 1 ? \"\" : \"s\"} at CLI ${pc.cyan(cliVersion)}, ` +\n `each at the hash it has on disk. Those bytes may be yours, so the next update treats ` +\n `every one of them as edited and offers a merge rather than a write.`,\n ];\n if (adoption.absent.length > 0) {\n lines.push(\n \"\",\n `${adoption.absent.length} template file${adoption.absent.length === 1 ? \" is\" : \"s are\"} not on disk and ${preview ? \"would be\" : \"were\"} recorded as missing, so the next update restores ${adoption.absent.length === 1 ? \"it\" : \"them\"}:`,\n ...adoption.absent.map((target) => ` ${pc.dim(target)}`)\n );\n }\n lines.push(\n \"\",\n pc.dim(\n \"Nothing else was applied. Run `saasaloy update` again to update the base.\"\n )\n );\n note(wrapForNote(lines.join(\"\\n\")), \"Base adopted\", TUI_ON_STDERR);\n outro(\n preview\n ? pc.dim(\"dry run — nothing recorded\")\n : pc.green(`Recorded the base at CLI ${cliVersion}.`),\n TUI_ON_STDERR\n );\n return EXIT_OK;\n }\n\n const recorded = lock.base!.cliVersion;\n const recordedVersion = parseVersion(recorded);\n const runningVersion = parseVersion(cliVersion);\n if (\n recordedVersion &&\n runningVersion &&\n compareVersions(recordedVersion, runningVersion) > 0\n ) {\n cancel(\n `This project's base was recorded by saasaloy ${recorded}, and ${cliVersion} is running — ` +\n `updating would move the base backwards. Upgrade with \\`${UPGRADE_COMMAND}\\`. Nothing was written.`,\n TUI_ON_STDERR\n );\n return EXIT_REFUSED;\n }\n if (recorded !== cliVersion) {\n log.info(\n `Base recorded at CLI ${pc.cyan(recorded)}; ${pc.cyan(cliVersion)} is running.`,\n TUI_ON_STDERR\n );\n }\n baseComparison = compareBase({\n lock,\n manifest,\n runningHash: await templateHash(templateDir),\n runningVersion: cliVersion,\n missingTargets: await missingBaseTargets(root, manifest, templateDir),\n });\n }\n\n if (targets.length === 0 && !baseComparison) {\n note(\"Nothing installed.\", \"Nothing to do\", TUI_ON_STDERR);\n outro(pc.dim(\"0 modules\"), TUI_ON_STDERR);\n return EXIT_OK;\n }\n\n // Cache one source per (repo, ref) so a bare `update` resolves each ref once.\n const resolvers = new Map<string, RemoteRegistrySource>();\n const remote = (slug: string, ref: string): RemoteRegistrySource => {\n const key = `${slug}@${ref}`;\n const cached = resolvers.get(key);\n if (cached) {\n return cached;\n }\n const parts = splitSlug(slug);\n if (!parts) {\n throw new Error(\n `Lock entry source \"${slug}\" isn't an owner/repo coordinate.`\n );\n }\n const source = new RemoteRegistrySource(parts[0], parts[1], ref);\n resolvers.set(key, source);\n sources.push(source);\n return source;\n };\n\n if (registryOverride) {\n const fromRemote = targets.filter((name) => {\n const entry = lock.modules[name];\n return entry && entry.source !== \"local\";\n });\n if (fromRemote.length > 0) {\n log.warn(\n `${REGISTRY_ENV} is set, so ${fromRemote.join(\", \")} will be updated from that working ` +\n `copy rather than ${pc.cyan(lock.modules[fromRemote[0]!]?.source ?? \"the registry\")} — ` +\n `${pc.dim(\"there is no merge base for a working copy\")}.`,\n TUI_ON_STDERR\n );\n }\n }\n\n const moduleComparisons =\n targets.length > 0\n ? await compareInstalled({\n installed: targets,\n lock,\n overrideRef: opts.ref,\n registryOverride,\n resolveRef: (_name, entry, ref) =>\n remote(entry.source, ref).resolveSha(),\n })\n : [];\n const comparisons = baseComparison\n ? [baseComparison, ...moduleComparisons]\n : moduleComparisons;\n\n const outdated = comparisons.filter((c) => c.status === \"outdated\");\n const skipped = comparisons.filter((c) => c.status !== \"outdated\");\n if (outdated.length === 0) {\n // `--ref` onto a tag that already points at the SHA the lock records moves no\n // files, but it is still the explicit unpin — record it here or the module stays\n // pinned forever. `--dry-run`/`--diff` preview only, so they write nothing.\n if (!preview && recordRefRewrites(lock, comparisons).length > 0) {\n await saveLock(root, lock);\n }\n for (const comparison of skipped) {\n log.info(\n `${pc.cyan(comparison.name)} ${pc.dim(`— ${skipReason(comparison, preview)}`)}`,\n TUI_ON_STDERR\n );\n }\n outro(pc.green(\"Everything is up to date.\"), TUI_ON_STDERR);\n return EXIT_OK;\n }\n\n // Fetch each outdated module twice — at the new SHA (theirs) and at the SHA the lock\n // records (base). Refetching base is what makes a real three-way merge possible; a\n // base we can't reach degrades the document rather than failing the update.\n const inputs: ModuleUpdateInput[] = [];\n // Mutually exclusive modules, checked the way `add` checks them (#98). A new version\n // can introduce a `dependsOn` on a second driver, and `update` installs a new\n // prerequisite as part of the same plan — so without this the command lands the exact\n // pair `add` refuses. Collected across every module and reported once, before\n // anything is written: `executeUpdatePlan` applies the whole plan under one\n // confirmation, so a refusal here has to stop the run, not one module of it.\n const conflictRefusals: string[] = [];\n // The same shape for `requires.saasaloy` (#50): a module's *new* version can declare\n // a CLI floor its installed version never had, and that is exactly the case the field\n // exists for. Collected across the run and refused before any plan is built.\n const cliRefusals: string[] = [];\n const missingLockEntries = new Set<string>();\n // Every graph that resolved, kept for the combined check below.\n const resolvedGraphs: Graph[] = [];\n if (baseComparison?.status === \"outdated\" && templateDir) {\n const handle = await baseUpdateInput({\n root,\n manifest,\n lock,\n templateDir,\n cliVersion,\n projectName: project.name,\n comparison: baseComparison,\n });\n cleanups.push(handle.cleanup);\n inputs.push(handle.input);\n }\n for (const comparison of outdated) {\n if (comparison.name === BASE_MODULE) {\n continue;\n }\n // One module's fetch failing is that module's problem: a dead tarball, a renamed\n // dependency, a network blip. It is reported and skipped, never fatal, so a bare\n // `update` still lands every other module (criterion 17).\n try {\n const theirsSource = registryOverride\n ? createRegistrySource({})\n : remote(comparison.source, comparison.latest);\n if (registryOverride) {\n sources.push(theirsSource);\n }\n\n const graph = await resolveGraph(theirsSource, comparison.name);\n const theirs = graph.modules.get(comparison.name);\n if (!theirs) {\n throw new Error(\n `${comparison.name} isn't in the registry at ${short(comparison.latest)}.`\n );\n }\n\n resolvedGraphs.push(graph);\n const cliMismatches = detectCliMismatches({ cliVersion, graph });\n if (cliMismatches.length > 0) {\n cliRefusals.push(\n formatCliMismatches(cliMismatches, comparison.name, cliVersion)\n );\n continue;\n }\n const report = detectConflicts({ graph, config, lock });\n for (const name of report.missingLockEntries) {\n missingLockEntries.add(name);\n }\n if (report.conflicts.length > 0) {\n conflictRefusals.push(\n formatConflicts(report.conflicts, comparison.name, \"update\")\n );\n continue;\n }\n\n let base: LoadedModule | undefined;\n let noMergeBase: string | undefined;\n if (registryOverride || comparison.current === \"local\") {\n noMergeBase = \"local install\";\n } else {\n try {\n base = await remote(\n comparison.source,\n comparison.current\n ).readModule(comparison.name);\n } catch (error) {\n // A force-pushed branch, a deleted tag, a repo gone private — the clean path\n // has already been decided, so refusing here would leave the user worse off.\n noMergeBase =\n error instanceof Error ? error.message : String(error);\n }\n }\n\n const intent =\n base && !registryOverride\n ? await theirsSource.commitSubjects(\n `modules/${comparison.name}`,\n comparison.current,\n comparison.latest\n )\n : [];\n\n inputs.push({\n comparison,\n theirs,\n ...(base ? { base } : {}),\n ...(noMergeBase ? { noMergeBase } : {}),\n intent,\n prereqs: {\n order: graph.order.filter((n) => n !== comparison.name),\n modules: graph.modules,\n },\n });\n } catch (error) {\n skipped.push({\n ...comparison,\n status: \"unresolvable\",\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // The per-module pass above cannot see a pair that arrives across two graphs: one\n // outdated module's new version pulls in one driver, another's pulls in the other,\n // and neither graph alone holds both. Union every resolved graph and check once more,\n // before any plan is built or written (#98).\n if (conflictRefusals.length === 0 && resolvedGraphs.length > 1) {\n const merged: Graph = { order: [], modules: new Map() };\n for (const graph of resolvedGraphs) {\n for (const [name, mod] of graph.modules) {\n if (!merged.modules.has(name)) {\n merged.modules.set(name, mod);\n merged.order.push(name);\n }\n }\n }\n const report = detectConflicts({ graph: merged, config, lock });\n for (const name of report.missingLockEntries) {\n missingLockEntries.add(name);\n }\n if (report.conflicts.length > 0) {\n conflictRefusals.push(\n formatConflicts(\n report.conflicts,\n outdated.map((c) => c.name).join(\", \"),\n \"update\"\n )\n );\n }\n }\n\n if (missingLockEntries.size > 0) {\n log.warn(\n `No lock entry for ${[...missingLockEntries].map((m) => pc.cyan(m)).join(\", \")} — ` +\n `any conflict they declare can't be checked ${pc.dim(\"(re-add them to record it)\")}.`,\n TUI_ON_STDERR\n );\n }\n // A CLI too old for one of the incoming descriptors stops the whole run, ahead of the\n // conflict report: there is no point resolving a driver choice with an applier that\n // cannot read the module it would resolve it for.\n if (cliRefusals.length > 0) {\n cancel(cliRefusals.join(\"\\n\"), TUI_ON_STDERR);\n return EXIT_REFUSED;\n }\n if (conflictRefusals.length > 0) {\n cancel(conflictRefusals.join(\"\\n\"), TUI_ON_STDERR);\n return EXIT_REFUSED;\n }\n\n // Everything outdated failed to fetch — there is no plan to summarize or confirm.\n if (inputs.length === 0) {\n for (const comparison of skipped) {\n // Only a fetch failure is a warning here — a `current` module is just news.\n const line = `${pc.cyan(comparison.name)} ${pc.dim(`— ${skipReason(comparison)}`)}`;\n if (comparison.status === \"unresolvable\") {\n log.warn(line, TUI_ON_STDERR);\n } else {\n log.info(line, TUI_ON_STDERR);\n }\n }\n // Every candidate failed to fetch — a failure, not a refusal: a retry may work.\n outro(pc.yellow(\"Nothing could be updated.\"), TUI_ON_STDERR);\n return EXIT_FAILURE;\n }\n\n const pkg = await readRootPackageJson(root);\n if (!pkg) {\n log.warn(\n \"No package.json at the project root — dependency pins won't be updated.\",\n TUI_ON_STDERR\n );\n }\n const plan = await buildUpdatePlan({\n root,\n config,\n manifest,\n lock,\n inputs,\n considered: targets,\n skipped,\n pkg,\n });\n\n summarizePlan(plan);\n\n if (opts.diff) {\n for (const file of [\n ...plan.modules.flatMap((m) => m.files),\n ...plan.modules.flatMap((m) => m.removals),\n ]) {\n if (QUIET.has(file.action)) {\n continue;\n }\n note(\n renderDiff(file),\n `${ACTION_LABEL[file.action]} ${file.target}`,\n TUI_ON_STDERR\n );\n }\n }\n\n // --dry-run and --diff both preview only: nothing is written anywhere, including\n // `--out`. The merge plan still renders to stdout so the preview is complete.\n if (preview) {\n const document = renderMergePlan(plan);\n if (document) {\n process.stdout.write(document);\n }\n outro(\n pc.dim(\n opts.diff\n ? \"diff only — nothing applied\"\n : \"dry run — nothing applied\"\n ),\n TUI_ON_STDERR\n );\n return EXIT_OK;\n }\n\n if (!opts.yes) {\n const proceed = await confirm({ message: \"Proceed?\", ...TUI_ON_STDERR });\n if (isCancel(proceed)) {\n cancel(\"update cancelled\", TUI_ON_STDERR);\n return EXIT_FAILURE;\n }\n if (!proceed) {\n outro(pc.dim(\"aborted — nothing applied\"), TUI_ON_STDERR);\n return EXIT_OK;\n }\n }\n\n // Copy everything the plan will touch, plus the state files, before a byte moves.\n // `saasaloy update --abort` puts this back — the undo this command shipped without.\n const backupDir = await writeBackup({\n root,\n targets: backupTargets(plan),\n cliVersion,\n });\n log.info(\n `Pre-update copies in ${pc.cyan(backupDir)} ${pc.dim(\"— `saasaloy update --abort` restores them\")}`,\n TUI_ON_STDERR\n );\n\n // Capture the outcome rather than letting it throw, so the ledger writes below\n // still run on the failure path and the original error still wins afterwards.\n const outcome = await executeUpdatePlan(plan, {\n root,\n config,\n manifest,\n lock,\n pkg,\n }).then(\n (value) => ({ ok: true as const, value }),\n (error: unknown) => ({ ok: false as const, error })\n );\n\n // Record whatever actually landed even if a mid-plan write failed — the ledger must\n // describe the real on-disk state (`add`'s invariant; #49 owns real transactionality\n // for both commands). All three are attempted together: awaiting them in sequence\n // would let a failed manifest save skip the config and lock, which is the same\n // partial ledger this step exists to prevent.\n const saves = await Promise.allSettled([\n saveManifest(root, manifest),\n saveConfig(root, config),\n saveLock(root, lock),\n ]);\n if (!outcome.ok) {\n throw outcome.error;\n }\n const failedSave = saves.find((save) => save.status === \"rejected\");\n if (failedSave) {\n throw failedSave.reason;\n }\n const result: UpdateResult = outcome.value;\n\n reportResult(result);\n\n // Phase 4: the merge plan is emitted only after the clean path has run, so it\n // describes what is actually left to do rather than what might have been.\n const document = renderMergePlan(plan);\n if (document) {\n await emitMergePlan(document, opts.out);\n note(\n wrapForNote(\n pc.dim(\n opts.out\n ? \"Hand that file to an agent — your edits were left untouched.\"\n : \"That merge plan is on stdout — pipe it to an agent. Your edits were left untouched.\"\n )\n ),\n \"Needs merge\",\n TUI_ON_STDERR\n );\n }\n\n // Drift routing to a merge plan is the designed outcome, not a failure — but the run\n // is unfinished, and a CI job that reads only the exit code has to be able to see\n // that. 3 says \"applied what it could, files are waiting on a merge\"; 0 still means\n // there is nothing left to do.\n outro(summarizeOutcome(plan, result), TUI_ON_STDERR);\n return plan.needsMerge ? EXIT_MERGE_PENDING : EXIT_OK;\n } catch (error) {\n cancel(formatFailure(error), TUI_ON_STDERR);\n return exitCodeFor(error);\n } finally {\n // Each remote source extracted its modules to a temp dir; drop them all.\n for (const source of sources) {\n await source.cleanup?.();\n }\n for (const cleanup of cleanups) {\n await cleanup();\n }\n }\n}\n\n/** The closing line: what landed, and what is still waiting on a merge. */\nfunction summarizeOutcome(plan: UpdatePlan, result: UpdateResult): string {\n const changed = result.written.length + result.deleted.length;\n const merging = plan.modules.filter((m) => m.needsMerge).map((m) => m.name);\n const applied =\n changed > 0\n ? `Updated ${[\n ...new Set(\n [...result.written, ...result.deleted].map((f) => f.module)\n ),\n ]\n .map((n) => pc.bold(n))\n .join(\n \", \"\n )} ${pc.dim(`(${result.written.length} written, ${result.deleted.length} removed)`)}`\n : \"\";\n\n if (merging.length === 0) {\n return pc.green(applied || \"Already up to date.\");\n }\n const names = merging.map((n) => pc.bold(n)).join(\", \");\n const verb = merging.length === 1 ? \"needs\" : \"need\";\n const merge = pc.yellow(\n applied\n ? `${names} still ${verb} a merge — see the plan above.`\n : `${names} ${verb} a merge — see the plan above.`\n );\n return applied ? `${pc.green(applied)} ${merge}` : merge;\n}\n\nfunction reportResult(result: UpdateResult): void {\n for (const name of result.prereqsInstalled) {\n log.step(\n `${pc.green(\"install\")} ${pc.cyan(name)} ${pc.dim(\"(new prerequisite)\")}`,\n TUI_ON_STDERR\n );\n }\n for (const file of result.written) {\n log.step(`${ACTION_LABEL[file.action]} ${file.target}`, TUI_ON_STDERR);\n }\n for (const file of result.deleted) {\n log.step(`${pc.red(\"delete\")} ${file.target}`, TUI_ON_STDERR);\n }\n for (const file of result.untracked) {\n log.step(\n `${pc.dim(\"untrack\")} ${file.target} ${pc.dim(\"(already gone)\")}`,\n TUI_ON_STDERR\n );\n }\n for (const patch of result.patched) {\n log.step(\n `${pc.green(\"patch\")} ${patch.file} ${pc.dim(`— ${patch.patch.kind}`)}`,\n TUI_ON_STDERR\n );\n }\n for (const link of result.linkConflicts) {\n log.warn(\n `Skill link ${pc.cyan(link.path)} left untouched — not ours to replace.`,\n TUI_ON_STDERR\n );\n }\n for (const dep of result.depsWritten) {\n log.step(`${pc.cyan(\"dep\")} ${dep.name}@${dep.version}`, TUI_ON_STDERR);\n }\n if (result.lateDrift.length > 0) {\n const lines = result.lateDrift\n .map((f) => ` ${pc.yellow(\"kept\")} ${f.target}`)\n .join(\"\\n\");\n note(\n wrapForNote(\n `${lines}\\n\\n${pc.dim(\"Edited while the plan was open — left alone rather than overwritten.\")}`\n ),\n \"Changed under us\",\n TUI_ON_STDERR\n );\n }\n if (result.driftSurvivors.length > 0) {\n const lines = result.driftSurvivors\n .map((f) => ` ${pc.yellow(\"kept\")} ${f.target}`)\n .join(\"\\n\");\n note(\n wrapForNote(\n `${lines}\\n\\n${pc.dim(\"Hand-edited — left on disk, now untracked.\")}`\n ),\n \"Drift survivors\",\n TUI_ON_STDERR\n );\n }\n}\n","import {\n copyFile,\n mkdir,\n readdir,\n readFile,\n rm as rmPath,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { RefusalError } from \"./exit.js\";\nimport { pathExists, readIfPresent, resolveWithinRoot } from \"./fs-utils.js\";\n\n// The undo `saasaloy update` never had (#144 follow-up). Before the applier writes a\n// single byte, every path the plan will touch is copied under\n// `.saasaloy/backup-<timestamp>/`, together with the four state files the run rewrites.\n// `saasaloy update --abort` puts the newest backup back.\n//\n// A path the plan will *create* has nothing to copy, so it is recorded as absent and the\n// restore deletes it. That asymmetry is the whole reason the record is a file rather than\n// a directory listing: a directory alone cannot say \"this used to not exist\".\n\n/** Where backups live, relative to the project root. */\nexport const BACKUP_DIR = join(\".saasaloy\", \"backups\");\n\n/** The record written beside the copied files. */\nexport interface BackupRecord {\n /** ISO timestamp, the same value the directory is named after. */\n createdAt: string;\n /** CLI version that took the backup, for the restore's one-line report. */\n cliVersion: string;\n /** Project-relative POSIX paths that existed and were copied. */\n files: string[];\n /** Project-relative POSIX paths the run was about to create — restored by deleting them. */\n absent: string[];\n}\n\nconst RECORD_FILE = \"backup.json\";\n\n/** State files every run rewrites; backed up alongside the plan's own targets. */\nexport const STATE_FILES = [\n \"saasaloy.json\",\n \"saasaloy-lock.json\",\n \"package.json\",\n join(\".saasaloy\", \"manifest.json\"),\n];\n\n/** A directory name that sorts chronologically and is legal on every platform. */\nfunction stampName(now: Date): string {\n return now.toISOString().replaceAll(/[:.]/g, \"-\");\n}\n\nexport interface WriteBackupArgs {\n root: string;\n /** Project-relative POSIX paths the plan will write or delete. */\n targets: readonly string[];\n cliVersion: string;\n now?: Date;\n}\n\n/**\n * Copy `targets` and the state files into a fresh backup directory. Returns the\n * project-relative directory, so the caller can name it in the summary — the user needs\n * to see the path to trust the `--abort` that follows.\n */\nexport async function writeBackup(args: WriteBackupArgs): Promise<string> {\n const { root, targets, cliVersion } = args;\n const createdAt = (args.now ?? new Date()).toISOString();\n const rel = join(BACKUP_DIR, stampName(args.now ?? new Date(createdAt)));\n const dir = join(root, rel);\n const files: string[] = [];\n const absent: string[] = [];\n\n for (const target of new Set([...targets, ...STATE_FILES])) {\n const source = resolveWithinRoot(root, target);\n if (!(await pathExists(source))) {\n absent.push(target);\n continue;\n }\n const destination = join(dir, target);\n await mkdir(dirname(destination), { recursive: true });\n await copyFile(source, destination);\n files.push(target);\n }\n\n const record: BackupRecord = {\n createdAt,\n cliVersion,\n files: files.toSorted(),\n absent: absent.toSorted(),\n };\n await mkdir(dir, { recursive: true });\n await writeFile(\n join(dir, RECORD_FILE),\n `${JSON.stringify(record, null, 2)}\\n`,\n \"utf-8\"\n );\n return rel;\n}\n\n/** Backup directory names, newest last. */\nexport async function listBackups(root: string): Promise<string[]> {\n const dir = join(root, BACKUP_DIR);\n if (!(await pathExists(dir))) {\n return [];\n }\n const entries = await readdir(dir, { withFileTypes: true });\n return entries\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name)\n .toSorted();\n}\n\nexport interface RestoreResult {\n /** Project-relative directory the files came from. */\n from: string;\n restored: string[];\n /** Files the run had created and the restore removed. */\n removed: string[];\n}\n\n/**\n * Put the newest backup back: every copied file over its original path, and every path\n * the run created deleted. Refuses rather than guesses when there is nothing to restore\n * or the record is unreadable — a half-applied restore is worse than none.\n */\nexport async function restoreLatestBackup(\n root: string\n): Promise<RestoreResult> {\n const backups = await listBackups(root);\n const newest = backups.at(-1);\n if (!newest) {\n throw new RefusalError(\n `No backup to restore — nothing under ${BACKUP_DIR}. \\`saasaloy update\\` writes one before it applies a plan.`\n );\n }\n const rel = join(BACKUP_DIR, newest);\n const dir = join(root, rel);\n const raw = await readIfPresent(join(dir, RECORD_FILE));\n if (raw === undefined) {\n throw new RefusalError(\n `${join(rel, RECORD_FILE)} is missing — that backup can't be restored safely.`\n );\n }\n let record: BackupRecord;\n try {\n record = JSON.parse(raw) as BackupRecord;\n } catch (error) {\n throw new RefusalError(`${join(rel, RECORD_FILE)} is invalid.`, {\n cause: error,\n });\n }\n\n const restored: string[] = [];\n for (const target of record.files) {\n const source = join(dir, target);\n const destination = resolveWithinRoot(root, target);\n if (!(await pathExists(source))) {\n continue;\n }\n await mkdir(dirname(destination), { recursive: true });\n await copyFile(source, destination);\n restored.push(target);\n }\n const removed: string[] = [];\n for (const target of record.absent) {\n const destination = resolveWithinRoot(root, target);\n if (await pathExists(destination)) {\n await rmPath(destination, { force: true });\n removed.push(target);\n }\n }\n return { from: rel, restored, removed };\n}\n\n/** Read a backup's record — the tests' way in, and the restore's own parser. */\nexport async function readBackupRecord(\n root: string,\n name: string\n): Promise<BackupRecord> {\n const raw = await readFile(\n join(root, BACKUP_DIR, name, RECORD_FILE),\n \"utf-8\"\n );\n return JSON.parse(raw) as BackupRecord;\n}\n","import { execFile } from \"node:child_process\";\n\n// Whether the project has uncommitted work, so `update` can refuse to write over it.\n//\n// Git is the only undo most projects actually have, and an update that lands on top of\n// uncommitted edits leaves nothing to diff the damage against. A project that is not a\n// git repository, or a box with no git at all, is not blocked: the check reports\n// \"unknown\" and the run continues.\n\nexport type WorkingTreeState = \"clean\" | \"dirty\" | \"unknown\";\n\n/** `git status --porcelain` in `root`, reduced to the one verdict `update` needs. */\nexport async function workingTreeState(\n root: string\n): Promise<WorkingTreeState> {\n return new Promise((resolve) => {\n execFile(\n \"git\",\n // `-- .` scopes the answer to the project. Without it a project nested inside a\n // larger repository reports every unrelated change in that repository as its own.\n [\"status\", \"--porcelain\", \"--untracked-files=no\", \"--\", \".\"],\n { cwd: root },\n (error, stdout) => {\n resolve(error ? \"unknown\" : stdout.trim() === \"\" ? \"clean\" : \"dirty\");\n }\n );\n });\n}\n","import { basename } from \"node:path\";\nimport { readRootPackageJson } from \"./pkg-json.js\";\nimport type { SaasaloyConfig } from \"./schema.js\";\n\n// The project's own name, and the one place that decides where it comes from.\n//\n// `{{PROJECT_NAME}}` is substituted into `package.json`, `wrangler.jsonc` and `siteName`\n// when `init` renders the template. `update` re-renders the same template to compare\n// against, so it must substitute the same value — and it used to call `basename(root)`\n// for it. A git worktree, a renamed folder or a CI checkout path each have a directory\n// name that is not the project's, so every one of those identity values was rewritten to\n// the folder's name. A `wrangler.jsonc` rename is worse than cosmetic: it deploys a\n// second Worker instead of updating the existing one.\n//\n// The order below is provenance, best first: what `init` recorded, then what the project\n// calls itself, then the directory as a last resort for a project scaffolded before\n// either was written.\n\n/** Where the resolved name came from, for the one-line warning the fallback deserves. */\nexport type ProjectNameSource = \"config\" | \"package.json\" | \"directory\";\n\nexport interface ResolvedProjectName {\n name: string;\n source: ProjectNameSource;\n}\n\nexport async function resolveProjectName(\n root: string,\n config: Pick<SaasaloyConfig, \"name\">\n): Promise<ResolvedProjectName> {\n if (config.name) {\n return { name: config.name, source: \"config\" };\n }\n const pkg = await readRootPackageJson(root);\n const pkgName = typeof pkg?.name === \"string\" ? pkg.name : undefined;\n if (pkgName) {\n return { name: pkgName, source: \"package.json\" };\n }\n return { name: basename(root), source: \"directory\" };\n}\n","import { BASE_INTENT, BASE_MODULE } from \"./base.js\";\nimport { toDiff } from \"./patch/diff.js\";\nimport type {\n ModuleUpdatePlan,\n PlannedUpdateFile,\n PlannedUpdatePatch,\n UpdatePlan,\n} from \"./updater.js\";\n\n// The artifact the whole feature exists for (build spec §2.9, CONTEXT.md): for every\n// file `update` refused to overwrite, a structured, agent-consumable merge plan —\n// natural-language intent + target files + old/new context — that can be handed\n// straight to an agent CLI with no editing. Markdown, because it is the one format\n// both a person and an agent read without a renderer (issue #48).\n//\n// Pure string building: it writes nothing and reads nothing. The command decides\n// whether the result goes to stdout or to `--out`.\n\n/** Render the merge plan for everything that drifted, or `\"\"` when nothing did. */\nexport function renderMergePlan(plan: UpdatePlan): string {\n const modules = plan.modules.filter((m) => m.needsMerge);\n if (modules.length === 0) {\n return \"\";\n }\n\n const fileCount = modules.reduce(\n (total, m) =>\n total +\n m.files.filter(isMergeFile).length +\n m.removals.filter(isMergeFile).length,\n 0\n );\n\n const out: string[] = [\n \"# Saasaloy merge plan\",\n \"\",\n `Generated by \\`saasaloy update\\`. ${countLabel(fileCount, \"file\")} across ` +\n `${countLabel(modules.length, \"module\")} changed on both sides — upstream and here — so ` +\n \"the update stopped short of writing them. Saasaloy never overwrites work you did by \" +\n \"hand; below is everything needed to reconcile the two sets of changes.\",\n \"\",\n ];\n\n for (const mod of modules) {\n out.push(...renderModule(mod));\n }\n\n out.push(...renderInstructions(plan));\n return `${out.join(\"\\n\").trimEnd()}\\n`;\n}\n\n/** Only the states that actually need a human or an agent — the merge plan's whole subject. */\nfunction isMergeFile(file: PlannedUpdateFile): boolean {\n return (\n file.action === \"drift\" ||\n file.action === \"conflict\" ||\n file.action === \"delete-drift\"\n );\n}\n\nfunction countLabel(n: number, noun: string): string {\n return `${n} ${noun}${n === 1 ? \"\" : \"s\"}`;\n}\n\nfunction renderModule(mod: ModuleUpdatePlan): string[] {\n const out: string[] = [`## ${mod.name}`, \"\", \"### Intent\", \"\"];\n\n if (mod.baseRecord) {\n // The base has no commit range to read subjects from: the template ships inside the\n // CLI. One line says what the agent needs to know (#120).\n out.push(\n `The base template shipped with a newer CLI. For every file below: ${BASE_INTENT}.`\n );\n } else if (mod.intent.length > 0) {\n out.push(\n \"What changed upstream, from the commits touching this module:\",\n \"\"\n );\n for (const subject of mod.intent) {\n out.push(`- ${subject}`);\n }\n } else {\n out.push(\n `No commit subjects could be read for this range${\n mod.noMergeBase ? ` (${mod.noMergeBase})` : \"\"\n } — read the diffs below to infer what the module intended.`\n );\n }\n out.push(\"\", \"### Provenance\", \"\");\n if (mod.baseRecord) {\n out.push(\n `- Module: \\`${mod.name}\\` (the template \\`saasaloy init\\` rendered)`,\n `- Source: \\`${mod.comparison.source}\\``,\n `- Recorded (your version): \\`${mod.comparison.current}\\` — CLI version, then template hash`,\n `- Running (new version): \\`${mod.comparison.latest}\\``\n );\n } else {\n out.push(\n `- Module: \\`${mod.name}\\``,\n `- Source: \\`${mod.comparison.source}\\``,\n `- Ref: \\`${mod.comparison.ref}\\``,\n `- Base (your version): \\`${mod.comparison.current}\\``,\n `- Theirs (new version): \\`${mod.comparison.latest}\\``\n );\n }\n if (mod.noMergeBase) {\n out.push(\n `- **no merge base — ${mod.noMergeBase}**: the original file can't be refetched, so the ` +\n \"diffs below are two-way (theirs vs. yours) and you must judge which side each \" +\n \"difference came from.\"\n );\n }\n out.push(\"\");\n\n const mergeFiles = mod.files.filter(isMergeFile);\n const droppedFiles = mod.removals.filter(isMergeFile);\n if (mergeFiles.length > 0 || droppedFiles.length > 0) {\n out.push(\"### Files to merge\", \"\");\n for (const file of mergeFiles) {\n out.push(...renderFile(file));\n }\n for (const file of droppedFiles) {\n out.push(...renderDroppedFile(file));\n }\n }\n\n const movedPatches = mod.patches.filter((p) => p.matched !== undefined);\n if (movedPatches.length > 0) {\n out.push(\n \"### Config patches that moved\",\n \"\",\n \"These config edits were skipped: the key the module identifies its entry by is already \" +\n \"present, holding a value that isn't the one the new version declares. That is almost \" +\n \"always a setting you changed on purpose, so nothing was written.\",\n \"\"\n );\n for (const patch of movedPatches) {\n out.push(...renderPatch(patch));\n }\n }\n\n return out;\n}\n\nconst ACTION_HEADING: Record<string, string> = {\n drift: \"you edited this file after it was applied\",\n conflict: \"new file collides with yours\",\n \"delete-drift\": \"the module dropped this file\",\n};\n\nfunction renderFile(file: PlannedUpdateFile): string[] {\n const out: string[] = [\n `#### \\`${file.target}\\``,\n \"\",\n `Source in the ${file.module === BASE_MODULE ? \"template\" : \"module\"}: \\`${file.from}\\` — ${ACTION_HEADING[file.action] ?? file.action}.`,\n \"\",\n ];\n\n if (file.action === \"conflict\") {\n out.push(\n file.base === undefined\n ? \"The new version of the module ships this file, but a file already sits at that path \" +\n \"that Saasaloy never wrote. There is no base for it — the module didn't have it at \" +\n \"your current version — so this is a two-way comparison.\"\n : \"The new version of the module ships this file, but a file already sits at that path \" +\n \"that Saasaloy never wrote, so nothing records what it looked like when it was \" +\n \"applied. The module's own older version of the file is available and is used as \" +\n \"the base below — but the on-disk file was never tracked against it, so treat it as \" +\n \"independent work rather than as edits on top of that base.\",\n \"\"\n );\n }\n\n if (file.projectOwned) {\n out.push(\n \"This file is yours: the template ships it once and never writes it again. The \" +\n \"template's copy moved, so the change is shown below for you to take what you want \" +\n \"from it. Nothing here was written.\",\n \"\"\n );\n } else if (file.adopted) {\n out.push(\n \"Saasaloy adopted this file rather than writing it, so the hash it records came off \" +\n \"your disk and proves nothing about who wrote these bytes. It is treated as edited \" +\n \"and was left alone.\",\n \"\"\n );\n }\n\n if (file.patchedBy && file.patchedBy.length > 0) {\n out.push(\n `Note: \\`${file.patchedBy.join(\"`, `\")}\\` also applied a config patch to this file. Part of ` +\n \"the difference below is that patch rather than an edit anyone made by hand — keep those \" +\n \"lines when you merge.\",\n \"\"\n );\n }\n\n const base = file.base;\n const theirs = file.theirs ?? \"\";\n const mine = file.mine ?? \"\";\n\n if (base === undefined) {\n out.push(\n \"**theirs → mine** (no base available — two-way):\",\n \"\",\n ...fenced(toDiff(theirs, mine, file.target), \"diff\")\n );\n } else {\n out.push(\n \"**base → theirs** (what the module changed):\",\n \"\",\n ...fenced(toDiff(base, theirs, file.target), \"diff\"),\n \"**base → mine** (what you changed):\",\n \"\",\n ...fenced(toDiff(base, mine, file.target), \"diff\")\n );\n }\n return out;\n}\n\nfunction renderDroppedFile(file: PlannedUpdateFile): string[] {\n const out: string[] = [\n `#### \\`${file.target}\\``,\n \"\",\n `Source in the module: \\`${file.from}\\`. The new version of the module dropped this file, ` +\n \"but you had edited it — so it was left exactly as it is and is now yours alone \" +\n \"(Saasaloy no longer tracks it). Your edits are still here; decide whether the work \" +\n \"they represent still belongs in the project now the module no longer ships it.\",\n \"\",\n ];\n if (file.base !== undefined && file.mine !== undefined) {\n out.push(\n \"**base → mine** (what you changed before it was dropped):\",\n \"\",\n ...fenced(toDiff(file.base, file.mine, file.target), \"diff\")\n );\n }\n return out;\n}\n\nfunction renderPatch(patch: PlannedUpdatePatch): string[] {\n const matched = patch.matched;\n if (!matched) {\n return [];\n }\n return [\n `#### \\`${patch.file}\\` — \\`${patch.patch.kind}\\``,\n \"\",\n `- Identity: \\`${matched.key}\\``,\n `- On disk: \\`${JSON.stringify(matched.current)}\\``,\n `- The new version declares: \\`${JSON.stringify(matched.wanted)}\\``,\n \"\",\n \"Reconcile these by hand if the new value matters; keep yours if it was deliberate.\",\n \"\",\n ];\n}\n\n/**\n * Where the lock is left, said concretely when every merging module shares one base SHA\n * and generically when a bare `update` collected several.\n */\nfunction lockStaysOn(plan: UpdatePlan): string {\n const bases = [\n ...new Set(\n plan.modules.filter((m) => m.needsMerge).map((m) => m.comparison.current)\n ),\n ];\n return bases.length === 1\n ? `the lock stays on \\`${bases[0]}\\``\n : \"each module's lock entry stays on the base SHA named in its Provenance section\";\n}\n\nfunction renderInstructions(plan: UpdatePlan): string[] {\n const out: string[] = [\n \"## Instructions\",\n \"\",\n \"You are reconciling two sets of changes to the same files: the module's (base → theirs) \" +\n \"and the user's (base → mine). Work file by file.\",\n \"\",\n \"1. Read the intent above to understand what the module was trying to fix or add.\",\n \"2. For each file, apply the module's change to the file **as it currently stands on disk** \" +\n \"— the on-disk copy is `mine` and it is the version to edit.\",\n \"3. **The user's edits must not be lost.** Where the two changes touch the same lines, keep \" +\n \"the user's intent and re-express the module's fix on top of it. If the two genuinely \" +\n \"conflict and you cannot preserve both, leave the user's version in place and say so \" +\n \"rather than choosing for them.\",\n \"4. Don't touch files that aren't listed here — everything else was already updated cleanly.\",\n \"5. Don't edit `.saasaloy/manifest.json` or `saasaloy-lock.json` — Saasaloy owns both.\",\n \"\",\n \"### What Saasaloy records afterwards\",\n \"\",\n \"Nothing, and that is deliberate. Saasaloy has no way to tell a finished merge from an \" +\n \"unfinished one, so it does not mark this plan resolved: the files above stay tracked at \" +\n `the module's old version and ${lockStaysOn(plan)}. A later ` +\n \"`saasaloy update` will offer the same files again, against the same base, until a file \" +\n \"either matches the new version byte for byte or the module stops shipping it. The old \" +\n \"SHA is the only merge base that exists — advancing it would leave these files with \" +\n \"nothing to diff against, which is why the merge is yours to keep, not Saasaloy's to \" +\n \"record.\",\n \"\",\n \"## Verification\",\n \"\",\n \"Run these after merging — `saasaloy update` deliberately runs nothing itself:\",\n \"\",\n `- \\`${plan.verifyCommand}\\``,\n ];\n if (plan.migrationCommand) {\n out.push(\n `- \\`${plan.migrationCommand}\\` — the update changed the database schema`\n );\n }\n out.push(\"\");\n return out;\n}\n\n/**\n * Wrap content in a fence long enough to survive whatever it contains — a merge plan\n * routinely carries markdown files that hold their own code fences, and a three-backtick\n * fence would end the block early and corrupt the document an agent reads.\n */\nfunction fenced(content: string, language: string): string[] {\n let longest = 0;\n for (const match of content.matchAll(/`{3,}/g)) {\n longest = Math.max(longest, match[0].length);\n }\n const fence = \"`\".repeat(Math.max(3, longest + 1));\n return [`${fence}${language}`, content.replace(/\\n$/, \"\"), fence, \"\"];\n}\n","import { runAdd } from \"./add.js\";\nimport { DESCRIPTIONS } from \"./descriptions.js\";\nimport { runDoctor } from \"./doctor.js\";\nimport { runEnv } from \"./env.js\";\nimport { runInit } from \"./init.js\";\nimport { runList } from \"./list.js\";\nimport { runNew } from \"./new.js\";\nimport { runOutdated } from \"./outdated.js\";\nimport { runRemove } from \"./remove.js\";\nimport { runUpdate } from \"./update.js\";\n\n// The command registry. It is the single source both consumers in `../cli.ts` read —\n// `printHelp()` and the bare-invocation picker — so a new command is one entry here and\n// shows up in help and in the picker with no second list to keep in sync.\n\nexport interface Command {\n describe: string;\n run: (argv: string[]) => Promise<number> | number;\n}\n\nexport type CommandRegistry = Record<string, Command>;\n\n// Insertion order is deliberate: it is the lifecycle a user walks (scaffold → compose →\n// configure → inspect → refresh → undo → browse), then the author-facing `new` and\n// `doctor` — the pair a registry author uses and a consumer never does — and it\n// is the order both help and the picker render. `env` sits directly after `add` because\n// it is the step `add`'s own next-steps note sends you to, and `outdated` sits directly\n// before `update` because it is the question `update` is the answer to. Append with that\n// in mind. No explicit `order`/`group` field until the list outgrows a single screen.\nexport const COMMANDS: CommandRegistry = {\n init: { describe: DESCRIPTIONS.init, run: runInit },\n add: { describe: DESCRIPTIONS.add, run: runAdd },\n env: { describe: DESCRIPTIONS.env, run: runEnv },\n outdated: { describe: DESCRIPTIONS.outdated, run: runOutdated },\n update: { describe: DESCRIPTIONS.update, run: runUpdate },\n remove: { describe: DESCRIPTIONS.remove, run: runRemove },\n list: { describe: DESCRIPTIONS.list, run: runList },\n new: { describe: DESCRIPTIONS.new, run: runNew },\n doctor: { describe: DESCRIPTIONS.doctor, run: runDoctor },\n};\n","#!/usr/bin/env node\n// saasaloy CLI entrypoint. Bootstrap only: it resolves an exit code and hands it to\n// process.exit. Argument dispatch lives in cli.ts and the command registry in\n// commands/index.ts, so both can be imported by a test without running the CLI.\n\nimport { main } from \"./cli.js\";\nimport { exitCodeFor, formatFailure } from \"./lib/exit.js\";\n\nmain(process.argv.slice(2)).then(\n (code) => process.exit(code),\n (error: unknown) => {\n // formatFailure prints the `cause` chain when SAASALOY_DEBUG is set; the chain\n // registry.ts attaches used to stop here and never reach a bug report (#98).\n console.error(formatFailure(error));\n process.exit(exitCodeFor(error));\n }\n);\n"],"mappings":"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","names":["cancel","pc","posix","pc","mkdir","readFile","writeFile","dirname","posix","describe","resolved","mkdir","readFile","dirname","join","readFile","join","join","readFile","join","readFile","mkdir","dirname","parseModule","edits","applyEdits","findNodeAtLocation","getNodeValue","modify","parseTree","parseTree","findNodeAtLocation","getNodeValue","edits","modify","applyEdits","applyEdits","findNodeAtLocation","getNodeValue","modify","parseTree","parseTree","findNodeAtLocation","getNodeValue","edits","modify","applyEdits","builders","generateCode","parseModule","parseModule","builders","removeBindingConflict","generateCode","readFile","writeFile","join","join","readFile","writeFile","posix","readFile","planned","mkdir","dirname","writeFile","partial","describe","describe","mkdir","readFile","writeFile","dirname","posix","posix","text","readFile","mkdir","dirname","writeFile","text","i","j","readFile","writeFile","join","join","readFile","writeFile","readFile","writeFile","join","version","join","readFile","writeFile","dirname","join","join","dirname","readFile","isAbsolute","join","resolve","isAbsolute","resolve","join","readFile","describe","text","describe","readFile","fileURLToPath","UNKNOWN_VERSION","pc","text","posix","cancel","intro","log","note","outro","join","relative","pc","createHash","mkdtemp","readdir","readFile","rm","tmpdir","join","posix","mkdir","readdir","readFile","writeFile","join","posix","fileURLToPath","fileURLToPath","join","mkdir","readdir","posix","readFile","writeFile","join","readdir","posix","createHash","readFile","mkdtemp","tmpdir","rm","readFile","dirname","join","posix","resolve","join","readFile","posix","resolve","dirname","KNOWN_FLAGS","USAGE","HELP","parseArgs","pc","relative","note","outro","cancel","intro","join","log","mkdir","readFile","writeFile","dirname","isAbsolute","resolve","cancel","intro","isCancel","log","note","outro","select","pc","posix","posix","readFile","dirname","join","relative","resolve","sep","text","resolve","join","dirname","relative","sep","readFile","KNOWN_FLAGS","USAGE","HELP","parseArgs","pc","isAbsolute","resolve","mkdir","dirname","writeFile","readFile","intro","cancel","log","outro","note","select","isCancel","readdir","readFile","basename","join","resolve","cancel","intro","isCancel","note","outro","select","text","pc","log","pc","KNOWN_FLAGS","USAGE","HELP","parseArgs","join","intro","pc","cancel","text","basename","resolve","isCancel","readdir","readFile","note","select","outro","cancel","intro","note","outro","join","pc","KNOWN_FLAGS","USAGE","HELP","parseArgs","join","intro","pc","cancel","note","outro","join","relative","resolve","cancel","intro","isCancel","note","outro","select","text","pc","mkdir","writeFile","dirname","join","NAME_PATTERN","SKILL_PREFIX","join","mkdir","dirname","writeFile","KNOWN_FLAGS","USAGE","HELP","parseArgs","pc","intro","cancel","join","text","isCancel","resolve","select","relative","note","outro","cancel","intro","log","note","outro","pc","mkdir","readFile","rmPath","writeFile","dirname","posix","readdir","readFile","writeFile","dirname","join","relative","sep","readFile","relative","sep","dirname","readdir","join","writeFile","version","WRITABLE","readFile","posix","stillMatches","mkdir","dirname","writeFile","rmPath","KNOWN_FLAGS","USAGE","HELP","parseArgs","SHA_PATTERN","short","pc","text","intro","cancel","log","note","outro","cancel","confirm","intro","isCancel","log","note","outro","select","pc","KNOWN_FLAGS","USAGE","HELP","parseArgs","pc","MAX_DIFF_LINES","note","log","intro","cancel","outro","select","isCancel","confirm","mkdir","writeFile","basename","dirname","isAbsolute","join","resolve","cancel","confirm","intro","isCancel","log","note","outro","pc","mkdir","readdir","readFile","rmPath","writeFile","dirname","join","join","mkdir","dirname","writeFile","readdir","rmPath","resolve","basename","basename","KNOWN_FLAGS","VALUE_FLAGS","USAGE","HELP","parseArgs","ACTION_LABEL","pc","MAX_DIFF_LINES","renderDiff","short","summarizePlan","note","log","STATE_FILES","resolve","join","dirname","basename","isAbsolute","mkdir","writeFile","splitSlug","intro","cancel","outro","document","confirm","isCancel","pc","select","isCancel","command","cancel"]}
|