@skill-map/cli 0.7.0 → 0.9.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/README.md +8 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +12246 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/defaults/skill-mapignore +27 -0
- package/dist/conformance/index.d.ts +82 -0
- package/dist/conformance/index.js +357 -0
- package/dist/conformance/index.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1698 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel/index.d.ts +2531 -0
- package/dist/kernel/index.js +1698 -0
- package/dist/kernel/index.js.map +1 -0
- package/dist/migrations/001_initial.sql +267 -0
- package/migrations/001_initial.sql +8 -3
- package/package.json +13 -6
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../cli/entry.ts","../kernel/adapters/silent-logger.ts","../kernel/util/logger.ts","../kernel/ports/logger.ts","../kernel/util/safe-text.ts","../kernel/util/tx.ts","../cli/i18n/logger.texts.ts","../cli/util/logger.ts","../cli/commands/check.ts","../kernel/i18n/registry.texts.ts","../kernel/registry.ts","../cli/i18n/check.texts.ts","../cli/util/db-path.ts","../cli/i18n/util.texts.ts","../cli/util/runtime-context.ts","../cli/util/exit-codes.ts","../cli/util/plugin-runtime.ts","../kernel/util/bucket-by-kind.ts","../built-in-plugins/providers/claude/index.ts","../kernel/scan/ignore.ts","../built-in-plugins/providers/claude/schemas/skill.schema.json","../built-in-plugins/providers/claude/schemas/agent.schema.json","../built-in-plugins/providers/claude/schemas/command.schema.json","../built-in-plugins/providers/claude/schemas/hook.schema.json","../built-in-plugins/providers/claude/schemas/note.schema.json","../built-in-plugins/extractors/frontmatter/index.ts","../kernel/trigger-normalize.ts","../built-in-plugins/extractors/slash/index.ts","../built-in-plugins/extractors/at-directive/index.ts","../built-in-plugins/extractors/external-url-counter/index.ts","../built-in-plugins/i18n/trigger-collision.texts.ts","../built-in-plugins/rules/trigger-collision/index.ts","../built-in-plugins/i18n/broken-ref.texts.ts","../built-in-plugins/rules/broken-ref/index.ts","../built-in-plugins/i18n/superseded.texts.ts","../built-in-plugins/rules/superseded/index.ts","../built-in-plugins/i18n/link-conflict.texts.ts","../built-in-plugins/rules/link-conflict/index.ts","../built-in-plugins/i18n/ascii.texts.ts","../built-in-plugins/formatters/ascii/index.ts","../kernel/adapters/schema-validators.ts","../built-in-plugins/i18n/validate-all.texts.ts","../built-in-plugins/rules/validate-all/index.ts","../built-in-plugins/built-ins.ts","../kernel/adapters/plugin-loader.ts","../kernel/i18n/plugin-loader.texts.ts","../kernel/adapters/plugin-store.ts","../kernel/extensions/hook.ts","../kernel/config/loader.ts","../kernel/i18n/config-loader.texts.ts","../config/defaults.json","../kernel/config/plugin-resolver.ts","../cli/i18n/plugin-runtime.texts.ts","../cli/util/text.ts","../cli/util/with-sqlite.ts","../kernel/adapters/sqlite/storage-adapter.ts","../kernel/i18n/storage.texts.ts","../kernel/adapters/sqlite/dialect.ts","../kernel/adapters/sqlite/history.ts","../kernel/adapters/sqlite/jobs.ts","../kernel/adapters/sqlite/migrations.ts","../kernel/i18n/migrations.texts.ts","../kernel/adapters/sqlite/plugin-migrations.ts","../kernel/adapters/sqlite/plugin-migrations-validator.ts","../kernel/adapters/sqlite/plugins.ts","../kernel/util/enum-parsers.ts","../kernel/adapters/sqlite/scan-load.ts","../kernel/adapters/sqlite/scan-persistence.ts","../kernel/adapters/sqlite/index.ts","../cli/commands/config.ts","../cli/util/elapsed.ts","../cli/util/error-reporter.ts","../cli/i18n/config.texts.ts","../cli/commands/conformance.ts","../conformance/index.ts","../conformance/i18n/runner.texts.ts","../cli/i18n/conformance.texts.ts","../cli/util/conformance-scopes.ts","../cli/commands/db.ts","../cli/util/confirm.ts","../cli/i18n/db.texts.ts","../cli/commands/export.ts","../kernel/scan/query.ts","../cli/i18n/export.texts.ts","../cli/commands/graph.ts","../cli/i18n/graph.texts.ts","../cli/commands/help.ts","../package.json","../cli/version.ts","../cli/i18n/help.texts.ts","../cli/commands/init.ts","../kernel/orchestrator.ts","../kernel/adapters/in-memory-progress.ts","../kernel/i18n/orchestrator.texts.ts","../kernel/scan/watcher.ts","../kernel/scan/delta.ts","../kernel/index.ts","../cli/i18n/init.texts.ts","../cli/i18n/cli-progress-emitter.texts.ts","../cli/util/cli-progress-emitter.ts","../cli/commands/history.ts","../cli/i18n/option-validators.texts.ts","../cli/util/option-validators.ts","../cli/i18n/history.texts.ts","../cli/commands/jobs.ts","../kernel/jobs/orphan-files.ts","../cli/i18n/jobs.texts.ts","../cli/commands/list.ts","../cli/i18n/list.texts.ts","../cli/commands/orphans.ts","../cli/i18n/orphans.texts.ts","../cli/commands/plugins.ts","../cli/i18n/plugins.texts.ts","../cli/commands/refresh.ts","../cli/i18n/refresh.texts.ts","../cli/util/path-guard.ts","../cli/commands/scan.ts","../cli/i18n/scan.texts.ts","../cli/commands/watch.ts","../cli/i18n/watch.texts.ts","../cli/commands/scan-compare.ts","../cli/commands/show.ts","../cli/i18n/show.texts.ts","../cli/commands/stubs.ts","../cli/i18n/stubs.texts.ts","../cli/commands/version.ts","../cli/i18n/version.texts.ts"],"sourcesContent":["/**\n * CLI entry — composed by `bin/sm.js`. Registers every command and hands off\n * to Clipanion. Exit codes are defined once in `src/cli/util/exit-codes.ts`\n * (the `ExitCode` object) and follow `spec/cli-contract.md`:\n *\n * 0 ok — `ExitCode.Ok`\n * 1 issues — `ExitCode.Issues` (non-clean scan / check)\n * 2 error — `ExitCode.Error` (unhandled / config / bad usage)\n * 3 duplicate — `ExitCode.Duplicate` (record stub)\n * 4 nonce-mismatch — `ExitCode.NonceMismatch` (record stub)\n * 5 not-found — `ExitCode.NotFound` (DB / row / dump)\n */\n\nimport { Builtins, Cli } from 'clipanion';\n\nimport { configureLogger } from '../kernel/util/logger.js';\nimport {\n Logger,\n extractLogLevelFlag,\n resolveLogLevel,\n LOGGER_ENV_VAR,\n} from './util/logger.js';\nimport { CheckCommand } from './commands/check.js';\nimport { CONFIG_COMMANDS } from './commands/config.js';\nimport { CONFORMANCE_COMMANDS } from './commands/conformance.js';\nimport { DB_COMMANDS } from './commands/db.js';\nimport { ExportCommand } from './commands/export.js';\nimport { GraphCommand } from './commands/graph.js';\nimport { HelpCommand, RootHelpCommand, routeHelpArgs } from './commands/help.js';\nimport { InitCommand } from './commands/init.js';\nimport { HistoryCommand, HistoryStatsCommand } from './commands/history.js';\nimport { JobPruneCommand } from './commands/jobs.js';\nimport { ListCommand } from './commands/list.js';\nimport { ORPHANS_COMMANDS } from './commands/orphans.js';\nimport { PLUGIN_COMMANDS } from './commands/plugins.js';\nimport { REFRESH_COMMANDS } from './commands/refresh.js';\nimport { ScanCommand } from './commands/scan.js';\nimport { ScanCompareCommand } from './commands/scan-compare.js';\nimport { ShowCommand } from './commands/show.js';\nimport { STUB_COMMANDS } from './commands/stubs.js';\nimport { VersionCommand } from './commands/version.js';\nimport { WatchCommand } from './commands/watch.js';\nimport { BINARY_LABEL, BINARY_NAME, VERSION } from './version.js';\n\nconst cli = new Cli({\n binaryLabel: BINARY_LABEL,\n binaryName: BINARY_NAME,\n binaryVersion: VERSION,\n enableCapture: false,\n});\n\ncli.register(Builtins.VersionCommand);\ncli.register(RootHelpCommand);\ncli.register(HelpCommand);\ncli.register(InitCommand);\ncli.register(ScanCommand);\ncli.register(ScanCompareCommand);\ncli.register(WatchCommand);\ncli.register(VersionCommand);\ncli.register(ListCommand);\ncli.register(ShowCommand);\ncli.register(CheckCommand);\ncli.register(GraphCommand);\ncli.register(ExportCommand);\ncli.register(HistoryCommand);\ncli.register(HistoryStatsCommand);\ncli.register(JobPruneCommand);\nfor (const cmd of CONFIG_COMMANDS) cli.register(cmd);\nfor (const cmd of CONFORMANCE_COMMANDS) cli.register(cmd);\nfor (const cmd of DB_COMMANDS) cli.register(cmd);\nfor (const cmd of PLUGIN_COMMANDS) cli.register(cmd);\nfor (const cmd of ORPHANS_COMMANDS) cli.register(cmd);\nfor (const cmd of REFRESH_COMMANDS) cli.register(cmd);\nfor (const cmd of STUB_COMMANDS) cli.register(cmd);\n\nconst { value: logLevelFlag, rest: args } = extractLogLevelFlag(process.argv.slice(2));\nconst logLevel = resolveLogLevel({\n flag: logLevelFlag,\n env: process.env[LOGGER_ENV_VAR] ?? null,\n fallback: 'warn',\n errStream: process.stderr,\n});\nconfigureLogger(new Logger({ level: logLevel, stream: process.stderr }));\n\nconst routedArgs = routeHelpArgs(args, cli);\nconst exitCode = await cli.run(routedArgs, {\n stdin: process.stdin,\n stdout: process.stdout,\n stderr: process.stderr,\n});\nprocess.exit(exitCode);\n","/**\n * No-op `LoggerPort`. Default when the kernel is invoked without a\n * logger (tests, embedded usage). Equivalent in spirit to\n * `InMemoryProgressEmitter`: callers that don't care get a working\n * implementation that does nothing.\n *\n * Every method is intentionally empty — that IS the contract of this\n * class. We disable `no-empty-function` for the whole file because\n * adding `// eslint-disable-next-line` to each method would be noise.\n */\n\n/* eslint-disable @typescript-eslint/no-empty-function */\n\nimport type { LoggerPort } from '../ports/logger.js';\n\nexport class SilentLogger implements LoggerPort {\n trace(): void {}\n debug(): void {}\n info(): void {}\n warn(): void {}\n error(): void {}\n}\n","/**\n * Module-level singleton `LoggerPort`. The kernel emits warnings /\n * info / debug through `log.*`; the active implementation defaults to\n * `SilentLogger` (no output) and is swapped by the driving adapter at\n * boot time via `configureLogger(...)`.\n *\n * Why a singleton (vs. per-call injection):\n * - Logging crosses every layer; threading a `logger` argument\n * through every kernel function costs a lot of plumbing for a\n * side-channel concern.\n * - The active impl is a pointer; the exported `log` is a stable\n * proxy. Imports made before `configureLogger` runs still see the\n * new impl on every call — no \"captured stale logger\" bugs.\n *\n * Tradeoffs accepted:\n * - Tests must call `resetLogger()` (or replace the active impl) in\n * teardown to avoid cross-test bleed.\n * - Concurrent scans share the same logger; per-scan logging requires\n * reintroducing an explicit `logger` argument on the call path.\n */\n\nimport { SilentLogger } from '../adapters/silent-logger.js';\nimport type { LoggerPort } from '../ports/logger.js';\n\nlet active: LoggerPort = new SilentLogger();\n\n/** Stable proxy. Methods always delegate to the current `active` impl. */\nexport const log: LoggerPort = {\n trace: (message, context) => active.trace(message, context),\n debug: (message, context) => active.debug(message, context),\n info: (message, context) => active.info(message, context),\n warn: (message, context) => active.warn(message, context),\n error: (message, context) => active.error(message, context),\n};\n\n/** Install a logger as the active implementation. Idempotent. */\nexport function configureLogger(impl: LoggerPort): void {\n active = impl;\n}\n\n/** Restore the default `SilentLogger`. Call from test teardown. */\nexport function resetLogger(): void {\n active = new SilentLogger();\n}\n\n/** Inspect the active logger. Test-only — production code uses `log`. */\nexport function getActiveLogger(): LoggerPort {\n return active;\n}\n","/**\n * `LoggerPort` — structured logging port for the kernel.\n *\n * The kernel must NOT write to stdout/stderr directly. Anything that\n * would historically have been a `console.log` / `console.error` goes\n * through this port; the adapter (CLI, server, test harness) decides\n * format, level filter, and destination.\n *\n * Levels follow the conventional ordering, lowest = most verbose:\n *\n * trace < debug < info < warn < error < silent\n *\n * `silent` is a sentinel for filtering only — it never appears as a\n * `LogRecord.level`. Setting an adapter to `silent` disables every\n * method.\n */\n\nexport type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent';\n\nexport type LogMethodLevel = Exclude<LogLevel, 'silent'>;\n\nexport const LOG_LEVELS: readonly LogLevel[] = [\n 'trace',\n 'debug',\n 'info',\n 'warn',\n 'error',\n 'silent',\n] as const;\n\nconst LEVEL_RANK: Record<LogLevel, number> = {\n trace: 0,\n debug: 1,\n info: 2,\n warn: 3,\n error: 4,\n silent: 5,\n};\n\nexport function logLevelRank(level: LogLevel): number {\n return LEVEL_RANK[level];\n}\n\nexport function isLogLevel(value: unknown): value is LogLevel {\n return typeof value === 'string' && Object.prototype.hasOwnProperty.call(LEVEL_RANK, value);\n}\n\n/**\n * Parse a string into a `LogLevel`. Returns `null` for invalid input\n * (incl. `undefined` / `null` / empty). Case-insensitive; trims\n * whitespace.\n */\nexport function parseLogLevel(value: string | undefined | null): LogLevel | null {\n if (value === undefined || value === null) return null;\n const normalized = value.trim().toLowerCase();\n if (normalized === '') return null;\n return isLogLevel(normalized) ? normalized : null;\n}\n\nexport interface LogRecord {\n level: LogMethodLevel;\n /** ISO 8601 timestamp produced at the moment the log call was made. */\n timestamp: string;\n message: string;\n /** Optional structured context. Caller-owned; serialization is up to the formatter. */\n context?: Record<string, unknown>;\n}\n\nexport interface LoggerPort {\n trace(message: string, context?: Record<string, unknown>): void;\n debug(message: string, context?: Record<string, unknown>): void;\n info(message: string, context?: Record<string, unknown>): void;\n warn(message: string, context?: Record<string, unknown>): void;\n error(message: string, context?: Record<string, unknown>): void;\n}\n","/**\n * Sanitisers for strings that flow from disk-resident user content\n * (markdown frontmatter, plugin output, persisted enrichment values)\n * into terminal output. Without sanitisation a hostile file can inject\n * ANSI control sequences that move the cursor, repaint the screen, hide\n * text, or — on certain legacy terminals — trigger command execution.\n *\n * Two layered helpers:\n *\n * - `stripAnsi(text)` — removes the CSI / OSC / ESC sequences proper.\n * - `sanitizeForTerminal(text)` — ANSI strip plus the C0 control\n * subset that has no place in user content (NUL, BEL, BS, VT, FF,\n * SO, SI, DLE..US except `\\t` `\\n` `\\r`). Use this everywhere a\n * disk-sourced string is about to be `write()`-en to stdout/stderr.\n *\n * Surface area kept deliberately small. If a renderer needs richer\n * escaping (HTML, shell, JSON), it should reach for the matching\n * dedicated helper rather than extending this one.\n */\n\n// CSI / OSC / single-char ESC sequences. Pattern adapted from\n// `strip-ansi` v7 (MIT, Sindre Sorhus). The kernel deliberately stays\n// dependency-free for security-critical helpers, so the regex is\n// vendored verbatim (with the `\u001b` / `` lead-byte anchors\n// preserved) instead of pulling in the package. Compared to the\n// pre-audit (M6) version, the OSC tail accepts the full\n// `-a-zA-Z\\d/#&.:=?%@~_` charset that real OSC 8 hyperlinks use, so\n// `\\x1B]8;;https://...\\x07label\\x1B]8;;\\x07` strips cleanly instead\n// of leaving the URL fragment behind. The CSI tail is unchanged.\n// eslint-disable-next-line no-control-regex\nconst ANSI_ESCAPE_RE = /[\u001b][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))/g;\n\n// C0 control characters except TAB (\\x09), LF (\\x0A), CR (\\x0D). DEL\n// (\\x7F) is included — terminals interpret it as backspace.\n// eslint-disable-next-line no-control-regex\nconst C0_CONTROL_RE = /[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]/g;\n\nexport function stripAnsi(text: string): string {\n return text.replace(ANSI_ESCAPE_RE, '');\n}\n\nexport function sanitizeForTerminal(text: string): string {\n return text.replace(ANSI_ESCAPE_RE, '').replace(C0_CONTROL_RE, '');\n}\n","/**\n * `tx(template, vars)` — string interpolation for the project's text\n * tables (`*.texts.ts` files under `kernel/i18n/` and `cli/i18n/`).\n *\n * Templates use the `{{name}}` placeholder shape (Mustache / Handlebars\n * / Transloco compatible) so the same string tables drop into a real\n * i18n library on the day this project migrates.\n *\n * Contract:\n * - Every `{{name}}` token in `template` MUST have a matching key in\n * `vars`. A missing key throws — silent fallback would hide a\n * forgotten arg in a production build, which is the worst kind of\n * bug to chase down.\n * - Values can be `string | number`. `null` / `undefined` keys are\n * rejected; the caller is expected to coerce upstream (e.g. format\n * a missing path as `'(unknown)'` before passing).\n * - Whitespace inside the braces is tolerated (`{{ name }}`); the\n * parser strips it. This keeps long templates readable when wrapped\n * across multiple TS lines via `+`.\n * - Literal `{{` is not currently supported — no real text needs it.\n * Add escaping the day a template needs to render Handlebars-style\n * content.\n *\n * Plural / conditional logic does NOT live in the template. The caller\n * picks the correct template (e.g. `entries_singular` vs\n * `entries_plural`) or composes the variable value upstream and passes\n * the finished string. Keeping templates flat is the price for staying\n * Transloco-ready.\n */\n\nconst TOKEN_RE = /\\{\\{\\s*([A-Za-z][A-Za-z0-9_]*)\\s*\\}\\}/g;\n\nexport function tx(\n template: string,\n vars: Record<string, string | number> = {},\n): string {\n return template.replace(TOKEN_RE, (_match, name: string) => {\n if (!Object.prototype.hasOwnProperty.call(vars, name)) {\n throw new Error(\n `tx: missing variable \"${name}\" for template \"${template.slice(0, 80)}${template.length > 80 ? '…' : ''}\"`,\n );\n }\n const value = vars[name];\n if (value === null || value === undefined) {\n throw new Error(\n `tx: variable \"${name}\" is null/undefined for template \"${template.slice(0, 80)}${template.length > 80 ? '…' : ''}\"`,\n );\n }\n return String(value);\n });\n}\n","/**\n * Strings emitted by the CLI logger and its bootstrap parser.\n */\n\nexport const LOGGER_TEXTS = {\n invalidLevel: 'invalid log level \"{{value}}\" (expected one of: {{allowed}})\\n',\n} as const;\n","/**\n * Concrete CLI logger that implements `LoggerPort`. Configurable level,\n * stream, and format.\n *\n * Defaults: level `warn`, formatter `defaultFormat`, stream is supplied\n * by the caller (almost always `process.stderr` — logging is a side\n * channel, stdout stays clean for data output like JSON / table rows).\n *\n * Wiring: `entry.ts` pre-parses `--log-level` (CLI flag) and\n * `SKILL_MAP_LOG_LEVEL` (env var) via `extractLogLevelFlag` +\n * `resolveLogLevel`, instantiates `Logger`, and installs it as the\n * kernel singleton via `configureLogger(...)`. Anywhere in the codebase\n * that needs to log: `import { log } from '<.../>kernel/util/logger.js'`.\n */\n\nimport type {\n LogLevel,\n LogMethodLevel,\n LogRecord,\n LoggerPort,\n} from '../../kernel/ports/logger.js';\nimport { LOG_LEVELS, logLevelRank, parseLogLevel } from '../../kernel/ports/logger.js';\nimport { sanitizeForTerminal } from '../../kernel/util/safe-text.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { LOGGER_TEXTS } from '../i18n/logger.texts.js';\n\nexport type LogFormatter = (record: LogRecord) => string;\n\nexport interface ILoggerOptions {\n level: LogLevel;\n stream: NodeJS.WritableStream;\n format?: LogFormatter;\n}\n\nconst ENV_VAR = 'SKILL_MAP_LOG_LEVEL';\nconst FLAG_NAME = '--log-level';\n\n/**\n * Default human-readable format: pipe-separated `HH:MM:SS | LEVEL |\n * message [| {context}]`. Local time, no date — CLI sessions are\n * short-lived and the date is implicit. Use a custom formatter via\n * `new Logger({ format: ... })` if you need ISO timestamps or JSON\n * lines.\n *\n * `record.timestamp` is the ISO 8601 string captured at log time; we\n * re-derive local HH:MM:SS from it so the formatter is pure (no extra\n * `new Date()` call) and a custom record passed to the formatter\n * renders consistently.\n */\nfunction localTimeFromIso(iso: string): string {\n const d = new Date(iso);\n const hh = String(d.getHours()).padStart(2, '0');\n const mm = String(d.getMinutes()).padStart(2, '0');\n const ss = String(d.getSeconds()).padStart(2, '0');\n return `${hh}:${mm}:${ss}`;\n}\n\nexport const defaultFormat: LogFormatter = (record) => {\n const time = localTimeFromIso(record.timestamp);\n const level = record.level.toUpperCase().padEnd(5);\n const ctx =\n record.context && Object.keys(record.context).length > 0\n ? ` | ${JSON.stringify(record.context)}`\n : '';\n return `${time} | ${level} | ${record.message}${ctx}\\n`;\n};\n\nexport class Logger implements LoggerPort {\n #level: LogLevel;\n readonly #stream: NodeJS.WritableStream;\n readonly #format: LogFormatter;\n\n constructor(opts: ILoggerOptions) {\n this.#level = opts.level;\n this.#stream = opts.stream;\n this.#format = opts.format ?? defaultFormat;\n }\n\n setLevel(level: LogLevel): void {\n this.#level = level;\n }\n\n level(): LogLevel {\n return this.#level;\n }\n\n trace(message: string, context?: Record<string, unknown>): void {\n this.#emit('trace', message, context);\n }\n debug(message: string, context?: Record<string, unknown>): void {\n this.#emit('debug', message, context);\n }\n info(message: string, context?: Record<string, unknown>): void {\n this.#emit('info', message, context);\n }\n warn(message: string, context?: Record<string, unknown>): void {\n this.#emit('warn', message, context);\n }\n error(message: string, context?: Record<string, unknown>): void {\n this.#emit('error', message, context);\n }\n\n #emit(level: LogMethodLevel, message: string, context?: Record<string, unknown>): void {\n if (logLevelRank(level) < logLevelRank(this.#level)) return;\n const record: LogRecord = {\n level,\n timestamp: new Date().toISOString(),\n message,\n ...(context !== undefined ? { context } : {}),\n };\n this.#stream.write(this.#format(record));\n }\n}\n\nexport interface IResolveLogLevelOptions {\n flag?: string | null;\n env?: string | null;\n fallback: LogLevel;\n /** Where to write the warning when an invalid level is passed. Defaults to `process.stderr`. */\n errStream?: NodeJS.WritableStream;\n}\n\n/**\n * Resolve the active log level from CLI flag (highest priority), env\n * var (`SKILL_MAP_LOG_LEVEL`), then a fallback default. Invalid values\n * write a one-line warning to `errStream` and fall through to the next\n * source so a typo doesn't silently disable logging.\n */\nexport function resolveLogLevel(opts: IResolveLogLevelOptions): LogLevel {\n const allowed = LOG_LEVELS.join(', ');\n const errStream = opts.errStream ?? process.stderr;\n\n const sources: ReadonlyArray<string | null | undefined> = [opts.flag, opts.env];\n for (const raw of sources) {\n if (raw === undefined || raw === null || raw === '') continue;\n const parsed = parseLogLevel(raw);\n if (parsed) return parsed;\n // `raw` is user-controlled (CLI flag value or env var). A hostile\n // env (e.g. shared dotfile, CI variable) could ship ANSI escapes\n // through this warning; sanitize before printing.\n errStream.write(tx(LOGGER_TEXTS.invalidLevel, { value: sanitizeForTerminal(raw), allowed }));\n }\n return opts.fallback;\n}\n\n/**\n * Extract `--log-level` from an argv array without mutating the input.\n * Supports `--log-level=value` and `--log-level value` forms. Returns\n * the extracted value (or null) and the remaining argv with the flag\n * removed so Clipanion never sees it (it isn't a Clipanion option).\n *\n * Edge case: bare `--log-level` at end of argv yields `value: null`,\n * which `resolveLogLevel` treats as \"no source supplied\" and moves on.\n */\nexport function extractLogLevelFlag(argv: readonly string[]): {\n value: string | null;\n rest: string[];\n} {\n const rest: string[] = [];\n let value: string | null = null;\n for (let i = 0; i < argv.length; i += 1) {\n const arg = argv[i]!;\n if (arg === FLAG_NAME) {\n value = argv[i + 1] ?? null;\n i += 1;\n continue;\n }\n if (arg.startsWith(`${FLAG_NAME}=`)) {\n value = arg.slice(FLAG_NAME.length + 1);\n continue;\n }\n rest.push(arg);\n }\n return { value, rest };\n}\n\nexport const LOGGER_ENV_VAR = ENV_VAR;\n","/**\n * `sm check [--json] [-n <node.path>] [--rules <ids>] [--include-prob] [--async]`\n *\n * Print every current issue from `scan_issues`. Equivalent to\n * `sm scan --json | jq '.issues'` but reads from the persisted snapshot,\n * so it skips the entire walk + extract + rule pipeline.\n *\n * Filters (orthogonal):\n * `-n <node.path>` restrict to issues whose nodeIds include the path.\n * `--rules <ids>` comma-separated qualified rule ids (e.g.\n * `core/validate-all,core/broken-ref`); restrict to\n * issues whose `ruleId` matches any entry. Both\n * qualified and short ids match — the verb compares\n * on suffix when the entry has no `<plugin>/` prefix.\n *\n * Probabilistic Rules (spec § A.7):\n * `--include-prob` opt-in flag. Default unchanged: deterministic only,\n * CI-safe. With the flag, the verb loads the plugin\n * runtime, finds Rules with `mode === 'probabilistic'`\n * (filtered by `--rules` if set), and emits a stderr\n * advisory naming the skipped rule ids. Full dispatch\n * requires the job subsystem (Step 10) — until then\n * the flag is a stub: prob rules never produce issues\n * and never alter the exit code.\n * `--async` reserved companion to `--include-prob`. Once jobs\n * land it will return job ids without waiting for\n * completion; today it is a no-op (the advisory\n * simply mentions it).\n *\n * Exit codes (per `spec/cli-contract.md` §Exit codes):\n * 0 ok — no error-severity issues (warns / infos do not fail the verb)\n * 1 one or more issues at severity `error`\n * 5 DB file missing — run `sm scan` first\n *\n * The `1` ≠ `0` boundary intentionally mirrors `sm scan`'s contract: an\n * agent / CI loop can use `sm check` as a fast pre-flight without paying\n * for a full walk.\n *\n * TODO (Step 10): when the job subsystem ships, render an output marker\n * (`(prob)` / `🧠`) on issues whose `ruleId` belongs to a probabilistic\n * rule. Today the stub never produces such issues, so the marker has\n * nothing to attach to and is intentionally absent.\n */\n\nimport { Command, Option } from 'clipanion';\n\nimport { qualifiedExtensionId } from '../../kernel/registry.js';\nimport type { Issue, Severity } from '../../kernel/types.js';\nimport { CHECK_TEXTS } from '../i18n/check.texts.js';\nimport { assertDbExists, resolveDbPath } from '../util/db-path.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport {\n composeScanExtensions,\n emptyPluginRuntime,\n loadPluginRuntime,\n} from '../util/plugin-runtime.js';\nimport { sanitizeForTerminal } from '../../kernel/util/safe-text.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { withSqlite } from '../util/with-sqlite.js';\n\nconst SEVERITY_ORDER: Severity[] = ['error', 'warn', 'info'];\n\nexport class CheckCommand extends Command {\n static override paths = [['check']];\n static override usage = Command.Usage({\n category: 'Browse',\n description: 'Print all current issues (reads from DB, faster than sm scan --json | jq).',\n details: `\n Loads every row from scan_issues. Exits 1 if any issue has\n severity \\`error\\`, otherwise 0. \\`warn\\` and \\`info\\` do not fail.\n\n Run \\`sm scan\\` first to populate the DB.\n\n \\`--include-prob\\` is an opt-in flag for probabilistic Rule\n dispatch (spec § A.7). Default is deterministic-only — same\n CI-safe behaviour as before. With the flag, registered prob\n rules are detected and named in a stderr advisory; full\n dispatch lands when the job subsystem ships at Step 10.\n `,\n examples: [\n ['Print every current issue', '$0 check'],\n ['Machine-readable issue list', '$0 check --json'],\n ['Restrict to a single node', '$0 check -n .claude/agents/architect.md'],\n ['Restrict to specific rules', '$0 check --rules core/broken-ref,core/validate-all'],\n ['Opt in to probabilistic rules (stub until Step 10)', '$0 check --include-prob'],\n ['Check the global scope', '$0 check --global'],\n ['Use a non-default DB file', '$0 check --db /path/to/skill-map.db'],\n ],\n });\n\n global = Option.Boolean('-g,--global', false);\n db = Option.String('--db', { required: false });\n json = Option.Boolean('--json', false);\n node = Option.String('-n,--node', {\n required: false,\n description:\n 'Restrict to issues whose nodeIds include the given path. Combines with --rules and --include-prob.',\n });\n rules = Option.String('--rules', {\n required: false,\n description:\n 'Comma-separated rule ids (qualified or short). Restrict the issue read; with --include-prob, also filters which prob rules surface in the advisory.',\n });\n includeProb = Option.Boolean('--include-prob', false, {\n description:\n 'Detect probabilistic Rules and emit a stub advisory naming them (full dispatch lands at Step 10). Default off → deterministic-only, CI-safe.',\n });\n async = Option.Boolean('--async', false, {\n description:\n 'Reserved companion to --include-prob: once jobs ship, returns job ids without waiting. No effect today.',\n });\n noPlugins = Option.Boolean('--no-plugins', false, {\n description:\n 'Skip drop-in plugin discovery; only kernel built-ins participate in the prob detection. Same flag shape as `sm scan`.',\n });\n\n async execute(): Promise<number> {\n const dbPath = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });\n if (!assertDbExists(dbPath, this.context.stderr)) return ExitCode.NotFound;\n\n // Parse `--rules` once. Empty / whitespace tokens dropped.\n const ruleFilter = parseRulesFlag(this.rules);\n\n // Probabilistic Rule detection. Cheap when the flag is off — we never\n // touch the plugin loader at all (status quo for `sm check`).\n if (this.includeProb) {\n const probRuleIds = await detectProbRuleIds({\n scope: this.global ? 'global' : 'project',\n noPlugins: this.noPlugins,\n ruleFilter,\n stderr: this.context.stderr,\n });\n if (probRuleIds.length > 0) {\n const template = this.async\n ? CHECK_TEXTS.probStubAdvisoryAsync\n : CHECK_TEXTS.probStubAdvisory;\n this.context.stderr.write(\n tx(template, {\n count: probRuleIds.length,\n ruleIds: probRuleIds.join(', '),\n }),\n );\n }\n }\n\n return withSqlite({ databasePath: dbPath, autoBackup: false }, async (adapter) => {\n let issues = await adapter.issues.listAll();\n\n // Filters apply to the persisted issue list. They do NOT affect the\n // prob-rule advisory above (which already honoured `--rules`).\n if (this.node !== undefined) {\n const nodePath = this.node;\n issues = issues.filter((i) => i.nodeIds.includes(nodePath));\n }\n if (ruleFilter !== undefined) {\n issues = issues.filter((i) => matchesRuleFilter(i.ruleId, ruleFilter));\n }\n\n if (this.json) {\n this.context.stdout.write(JSON.stringify(issues) + '\\n');\n } else if (issues.length === 0) {\n this.context.stdout.write(CHECK_TEXTS.noIssues);\n } else {\n this.context.stdout.write(renderHuman(issues));\n }\n\n return issues.some((i) => i.severity === 'error') ? ExitCode.Issues : ExitCode.Ok;\n });\n }\n}\n\n/**\n * Parse the `--rules <ids>` flag into a normalised filter set. Returns\n * `undefined` when the flag is absent — the caller treats that as \"no\n * filter, every rule passes\". Empty entries are dropped silently so a\n * trailing comma does not change the matched set.\n */\nfunction parseRulesFlag(raw: string | undefined): Set<string> | undefined {\n if (raw === undefined) return undefined;\n const ids = raw\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n if (ids.length === 0) return undefined;\n return new Set(ids);\n}\n\n/**\n * Match a `ruleId` (which always arrives qualified — `<plugin>/<id>` —\n * because issues are persisted with the full extension id since spec\n * § A.6) against the user's `--rules` filter. The filter accepts both\n * qualified and short forms; a short entry matches when the ruleId's\n * suffix after `/` is identical, which lets a user type\n * `--rules validate-all` without remembering the `core/` prefix.\n */\nfunction matchesRuleFilter(ruleId: string, filter: Set<string>): boolean {\n if (filter.has(ruleId)) return true;\n const slashIdx = ruleId.indexOf('/');\n if (slashIdx >= 0) {\n const short = ruleId.slice(slashIdx + 1);\n if (filter.has(short)) return true;\n }\n return false;\n}\n\ninterface IDetectProbRulesOptions {\n scope: 'project' | 'global';\n noPlugins: boolean;\n ruleFilter: Set<string> | undefined;\n stderr: NodeJS.WritableStream;\n}\n\n/**\n * Load the plugin runtime + built-ins, collect every Rule with\n * `mode === 'probabilistic'`, and return their qualified ids (filtered\n * by `--rules` when set). Plugin load warnings are forwarded verbatim\n * to stderr so the user sees the same diagnostics `sm scan` produces.\n *\n * Returns an empty list when no prob rules are registered — the caller\n * skips the advisory entirely in that case (advising about nothing\n * would be noise).\n */\n// eslint-disable-next-line complexity\nasync function detectProbRuleIds(opts: IDetectProbRulesOptions): Promise<string[]> {\n const pluginRuntime = opts.noPlugins\n ? emptyPluginRuntime()\n : await loadPluginRuntime({ scope: opts.scope });\n for (const warn of pluginRuntime.warnings) {\n opts.stderr.write(`${warn}\\n`);\n }\n const composed = composeScanExtensions({ noBuiltIns: false, pluginRuntime });\n const rules = composed?.rules ?? [];\n\n const probIds: string[] = [];\n for (const rule of rules) {\n if (rule.mode !== 'probabilistic') continue;\n const qualified = qualifiedExtensionId(rule.pluginId, rule.id);\n if (opts.ruleFilter && !matchesRuleFilter(qualified, opts.ruleFilter)) continue;\n probIds.push(qualified);\n }\n // Stable ordering so the advisory is deterministic across runs.\n probIds.sort();\n return probIds;\n}\n\nfunction renderHuman(issues: Issue[]): string {\n // Group by severity (errors first, then warns, then infos) so the\n // most actionable rows are at the top of the output.\n const grouped = new Map<Severity, Issue[]>();\n for (const sev of SEVERITY_ORDER) grouped.set(sev, []);\n for (const issue of issues) {\n const bucket = grouped.get(issue.severity);\n if (bucket) bucket.push(issue);\n else grouped.set(issue.severity, [issue]);\n }\n\n const lines: string[] = [];\n for (const sev of SEVERITY_ORDER) {\n const bucket = grouped.get(sev) ?? [];\n for (const issue of bucket) {\n // Defence in depth: `ruleId` / `message` / `nodeIds` originate from\n // plugin-authored strings persisted in the DB. Strip ANSI / C0\n // bytes before printing so a hostile plugin cannot repaint the\n // user's terminal via a stored issue row.\n lines.push(\n tx(CHECK_TEXTS.issueRow, {\n severity: issue.severity,\n ruleId: sanitizeForTerminal(issue.ruleId),\n message: sanitizeForTerminal(issue.message),\n nodeIds: issue.nodeIds.map(sanitizeForTerminal).join(', '),\n }),\n );\n }\n }\n return lines.join('\\n') + '\\n';\n}\n","/**\n * Strings emitted by `kernel/registry.ts`. Same `tx(template, vars)`\n * convention as every other `kernel/i18n/*.texts.ts` peer.\n *\n * These messages are thrown as `Error.message`; some surface to the user\n * via CLI verbs that catch them (e.g. `sm scan` registering manifests).\n */\n\nexport const REGISTRY_TEXTS = {\n duplicateExtension:\n 'Extension already registered: {{kind}}:{{qualifiedId}}',\n\n unknownKind:\n 'Unknown extension kind: {{kind}}',\n\n missingPluginId:\n 'Extension {{kind}}:{{id}} is missing pluginId; built-ins declare it directly, user plugins have it injected by PluginLoader.',\n} as const;\n","/**\n * Extension registry — six kinds, first-class, loaded through a single API.\n *\n * The `Extension` shape is aligned with `spec/schemas/extensions/base.schema.json`.\n * Kind-specific manifests (provider / extractor / rule / action / formatter /\n * hook) extend this base structurally; the registry stores the base view\n * and each kind's code carries its own fuller type where needed.\n *\n * **Spec § A.6 — qualified ids.** Every extension is keyed in the registry\n * by `<pluginId>/<id>` (e.g. `core/frontmatter`, `claude/slash`,\n * `hello-world/greet`). `Extension.id` carries the **short** id as authored;\n * `Extension.pluginId` carries the namespace; the registry composes the\n * qualifier internally and exposes lookup APIs that operate on either form\n * (qualified for direct lookup, kind-scoped listing for enumeration).\n *\n * Boot invariant: `new Registry()` is empty. `registry.totalCount() === 0`\n * when the kernel boots with zero extensions. This is the data side of the\n * `kernel-empty-boot` conformance contract.\n */\n\nimport { REGISTRY_TEXTS } from './i18n/registry.texts.js';\nimport type { Stability } from './types.js';\nimport { tx } from './util/tx.js';\n\nexport type ExtensionKind =\n | 'provider'\n | 'extractor'\n | 'rule'\n | 'action'\n | 'formatter'\n | 'hook';\n\nexport const EXTENSION_KINDS: readonly ExtensionKind[] = Object.freeze([\n 'provider',\n 'extractor',\n 'rule',\n 'action',\n 'formatter',\n 'hook',\n] as const);\n\nexport interface Extension {\n /** Short (unqualified) extension id as declared in the manifest. */\n id: string;\n /** Owning plugin namespace. Composed with `id` to form the qualified key. */\n pluginId: string;\n kind: ExtensionKind;\n version: string;\n description?: string;\n stability?: Stability;\n preconditions?: string[];\n entry?: string;\n}\n\n/**\n * Compose the qualified registry key for an extension. Single source of\n * truth so callers don't reinvent the format and a future change (e.g. a\n * different separator) lands in one place.\n */\nexport function qualifiedExtensionId(pluginId: string, id: string): string {\n return `${pluginId}/${id}`;\n}\n\nexport class DuplicateExtensionError extends Error {\n constructor(kind: ExtensionKind, qualifiedId: string) {\n super(tx(REGISTRY_TEXTS.duplicateExtension, { kind, qualifiedId }));\n this.name = 'DuplicateExtensionError';\n }\n}\n\nexport class Registry {\n /** kind → qualifiedId → Extension. */\n readonly #byKind: Map<ExtensionKind, Map<string, Extension>>;\n\n constructor() {\n this.#byKind = new Map(\n EXTENSION_KINDS.map((k) => [k, new Map<string, Extension>()]),\n );\n }\n\n register(ext: Extension): void {\n const bucket = this.#byKind.get(ext.kind);\n if (!bucket) {\n throw new Error(tx(REGISTRY_TEXTS.unknownKind, { kind: ext.kind }));\n }\n if (typeof ext.pluginId !== 'string' || ext.pluginId.length === 0) {\n throw new Error(tx(REGISTRY_TEXTS.missingPluginId, { kind: ext.kind, id: ext.id }));\n }\n const key = qualifiedExtensionId(ext.pluginId, ext.id);\n if (bucket.has(key)) {\n throw new DuplicateExtensionError(ext.kind, key);\n }\n bucket.set(key, ext);\n }\n\n /**\n * Lookup by qualified id (`<pluginId>/<id>`). Returns `undefined` when\n * no extension of that kind is registered under the qualifier.\n */\n get(kind: ExtensionKind, qualifiedId: string): Extension | undefined {\n return this.#byKind.get(kind)?.get(qualifiedId);\n }\n\n /**\n * Convenience wrapper that composes the qualified id for the caller.\n * Equivalent to `get(kind, qualifiedExtensionId(pluginId, id))`.\n */\n find(kind: ExtensionKind, pluginId: string, id: string): Extension | undefined {\n return this.get(kind, qualifiedExtensionId(pluginId, id));\n }\n\n all(kind: ExtensionKind): Extension[] {\n const bucket = this.#byKind.get(kind);\n return bucket ? [...bucket.values()] : [];\n }\n\n count(kind: ExtensionKind): number {\n return this.#byKind.get(kind)?.size ?? 0;\n }\n\n totalCount(): number {\n let n = 0;\n for (const bucket of this.#byKind.values()) n += bucket.size;\n return n;\n }\n}\n","/**\n * CLI strings emitted by `sm check` (`cli/commands/check.ts`).\n *\n * `sm check` reads the persisted issue table from the DB and prints\n * every current row. The `--include-prob` opt-in flag (spec § A.7)\n * detects probabilistic Rules registered via the plugin runtime and\n * emits a stderr advisory naming the rule ids that would dispatch as\n * jobs once the job subsystem ships at Step 10. The flag default is\n * unchanged: deterministic-only, CI-safe — no advisory.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const CHECK_TEXTS = {\n noIssues: 'No issues.\\n',\n\n /**\n * One row of the grouped issue listing. Severity / ruleId / message /\n * comma-separated nodeIds compose the line used by `sm check` pretty\n * output.\n */\n issueRow: '[{{severity}}] {{ruleId}}: {{message}} — {{nodeIds}}',\n\n // --- prob stub advisory ---------------------------------------------------\n probStubAdvisory:\n 'sm check --include-prob: probabilistic Rule dispatch requires the job ' +\n 'subsystem (Step 10). Stub: skipped {{count}} probabilistic rule(s) — ' +\n '{{ruleIds}}. Deterministic rules ran as usual; full dispatch lands when ' +\n 'the job subsystem ships.\\n',\n\n probStubAdvisoryAsync:\n 'sm check --include-prob --async: probabilistic Rule dispatch requires ' +\n 'the job subsystem (Step 10). Stub: skipped {{count}} probabilistic ' +\n 'rule(s) — {{ruleIds}}. The --async flag is reserved for future encoding ' +\n '(returns job ids without waiting once jobs land); today it is a no-op. ' +\n 'Deterministic rules ran as usual.\\n',\n} as const;\n","/**\n * Shared helpers for resolving the project / global skill-map DB file path\n * from CLI flags, and for asserting the file exists before any read-side\n * verb opens it. Used by every command that touches the DB on the\n * read-side (`sm list`, `sm show`, `sm check`) and by `sm db *` for\n * lifecycle ops.\n *\n * Spec global flags (per `spec/cli-contract.md` §Global flags):\n * -g / --global operate on `~/.skill-map/` instead of `./.skill-map/`\n * --db <path> escape hatch for explicit DB file\n */\n\nimport { existsSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\n\nimport { tx } from '../../kernel/util/tx.js';\nimport { UTIL_TEXTS } from '../i18n/util.texts.js';\nimport type { IRuntimeContext } from './runtime-context.js';\n\n/**\n * Per-scope directory the CLI stores its state under (DB file, settings,\n * plugins, etc.). Same name in project (`<cwd>/.skill-map/`) and global\n * (`~/.skill-map/`) scopes; the difference is the parent. Exported so\n * write-side scaffolding (`sm init`) and other helpers can reuse the\n * convention without duplicating the literal.\n */\nexport const SKILL_MAP_DIR = '.skill-map';\n\nconst DB_FILENAME = 'skill-map.db';\nconst JOBS_DIRNAME = 'jobs';\nconst PLUGINS_DIRNAME = 'plugins';\n\n/**\n * Single source of truth for the relative DB path inside a scope\n * directory (`.skill-map/skill-map.db`). Same string in project and\n * global scope; the difference is the parent directory the helper\n * resolves against.\n */\nconst DEFAULT_DB_REL = `${SKILL_MAP_DIR}/${DB_FILENAME}`;\n\n/**\n * Inputs for `resolveDbPath`. Extends `IRuntimeContext` so the helper\n * never reads `process.cwd()` / `homedir()` directly — every caller\n * threads the runtime context (mandatory) alongside the spec flags.\n * Pattern: `resolveDbPath({ global, db, ...defaultRuntimeContext() })`.\n */\nexport interface IDbLocationOptions extends IRuntimeContext {\n global: boolean;\n db: string | undefined;\n}\n\n/**\n * Resolve the DB file path from command-line options.\n *\n * Precedence: explicit `--db <path>` > `-g/--global` (~/.skill-map/) >\n * project default (cwd/.skill-map/).\n *\n * Always returns an absolute path. Does NOT verify existence — pair with\n * `assertDbExists` for read-side verbs.\n */\nexport function resolveDbPath(options: IDbLocationOptions): string {\n if (options.db) return resolve(options.db);\n if (options.global) return join(options.homedir, DEFAULT_DB_REL);\n return resolve(options.cwd, DEFAULT_DB_REL);\n}\n\n/**\n * Default project DB path (`<cwd>/.skill-map/skill-map.db`). Same effect\n * as `resolveDbPath({ global: false, db: undefined, ...ctx })`; this\n * helper is the cheaper and more explicit route for call sites that have\n * no `--global` / `--db` flags to honour (`sm scan`, `sm refresh`,\n * `sm watch`).\n */\nexport function defaultProjectDbPath(ctx: IRuntimeContext): string {\n return resolve(ctx.cwd, DEFAULT_DB_REL);\n}\n\n/**\n * Default project jobs directory (`<cwd>/.skill-map/jobs`). Used by the\n * `sm job prune` orphan-files pass and any other call site that needs\n * the project-scoped jobs spool.\n */\nexport function defaultProjectJobsDir(ctx: IRuntimeContext): string {\n return resolve(ctx.cwd, SKILL_MAP_DIR, JOBS_DIRNAME);\n}\n\n/**\n * Default project plugins directory (`<cwd>/.skill-map/plugins`).\n * Project + user plugin discovery composes this with the user-scoped\n * `<homedir>/.skill-map/plugins` peer.\n */\nexport function defaultProjectPluginsDir(ctx: IRuntimeContext): string {\n return resolve(ctx.cwd, SKILL_MAP_DIR, PLUGINS_DIRNAME);\n}\n\n/**\n * Default user (global) plugins directory (`<homedir>/.skill-map/plugins`).\n * Used alongside `defaultProjectPluginsDir` when discovery walks both\n * scopes.\n */\nexport function defaultUserPluginsDir(ctx: IRuntimeContext): string {\n return join(ctx.homedir, SKILL_MAP_DIR, PLUGINS_DIRNAME);\n}\n\n/**\n * Read-side guard: returns true if the DB file exists (or is `:memory:`),\n * otherwise writes a clear hint to stderr and returns false. Callers\n * should propagate exit code 5 (not-found) on a false return per\n * `spec/cli-contract.md` §Exit codes.\n */\nexport function assertDbExists(path: string, stderr: NodeJS.WritableStream): boolean {\n if (path === ':memory:' || existsSync(path)) return true;\n stderr.write(tx(UTIL_TEXTS.dbNotFound, { path }));\n return false;\n}\n","/**\n * Strings emitted by cross-cutting CLI utilities under `cli/util/*`\n * (db-path, elapsed, confirm). Same convention as the per-verb catalogs:\n * flat string templates with `{{name}}` placeholders for `tx(...)`.\n */\n\nexport const UTIL_TEXTS = {\n // db-path.ts\n dbNotFound: 'DB not found at {{path}}; run `sm scan` first.\\n',\n\n // elapsed.ts\n doneIn: 'done in {{elapsed}}\\n',\n\n // confirm.ts (default-no prompt suffix)\n confirmPromptSuffix: ' [y/N] ',\n /**\n * Regex source matching affirmative answers in `confirm()`. Compiled\n * with the `i` flag in the helper. Pre-i18n today the pattern is\n * English-only; when a non-English locale lands the catalog grows\n * alternations (e.g. `^(y(es)?|s(í|i)?)$`).\n */\n confirmYesPatternSource: '^y(es)?$',\n} as const;\n","/**\n * Bridge between Node globals and kernel functions that need a runtime\n * context (`cwd`, `homedir`). The kernel deliberately does NOT read\n * `process.cwd()` / `os.homedir()` itself — those are CLI / adapter\n * concerns. Anywhere a kernel API needs them, the CLI calls\n * `defaultRuntimeContext()` and passes the values through.\n *\n * Why a helper instead of inlining `{ cwd: process.cwd(), homedir: homedir() }`\n * in every caller: 8+ command sites consume it; centralising keeps the\n * intent obvious (\"use the live process context\") and gives one place\n * to extend if a future override (e.g. resolved absolute cwd) is needed.\n */\n\nimport { homedir } from 'node:os';\n\nexport interface IRuntimeContext {\n cwd: string;\n homedir: string;\n}\n\nexport function defaultRuntimeContext(): IRuntimeContext {\n return { cwd: process.cwd(), homedir: homedir() };\n}\n","/**\n * Canonical CLI exit codes — single source of truth.\n *\n * Every `Command#execute()` in `src/cli/commands/` MUST return one of\n * these values. Numeric values are the public contract documented in\n * `spec/cli-contract.md` §Exit codes; the semantic names are\n * kernel-internal.\n *\n * Ok = 0 success.\n * Issues = 1 command succeeded but the produced result has at\n * least one error-severity issue (`sm scan`,\n * `sm check`, `sm show <node>` when its issue list\n * contains an `error`).\n * Error = 2 unhandled error / config load failure / bad usage\n * / IO failure / DB invariant violation.\n * Duplicate = 3 emitted by `sm record` (stub today) when a\n * submitted record collides with an existing one.\n * NonceMismatch = 4 emitted by `sm record` (stub today) when the\n * submitted nonce does not match the expected one.\n * NotFound = 5 target not on disk / not in DB. DB file missing\n * (most common — see `assertDbExists`), prior\n * scan-result row missing, requested node path\n * missing, dump file passed to `--compare-with`\n * missing.\n *\n * The TS object literal pattern (frozen `as const` + derived union type)\n * is preferred over `enum` because it has zero runtime overhead and\n * narrows correctly when used as a return type.\n */\nexport const ExitCode = {\n Ok: 0,\n Issues: 1,\n Error: 2,\n Duplicate: 3,\n NonceMismatch: 4,\n NotFound: 5,\n} as const;\n\nexport type TExitCode = (typeof ExitCode)[keyof typeof ExitCode];\n","/**\n * Plugin runtime loader — single source of truth for any read-side verb\n * that needs plugin extensions on the wire (`sm scan`, `sm graph`).\n *\n * Step 9.1: this is the path that turns \"discovered\" plugins into\n * \"executing\" plugins. Until now `PluginLoader` was only invoked by the\n * `sm plugins` introspection verbs; the analysis pipeline ran on built-ins\n * exclusively. This helper closes that gap.\n *\n * Behaviour:\n *\n * - Discover + load every plugin under the project + user search paths\n * (or `--plugin-dir <path>` override).\n * - Layer the enabled-resolver: settings.json baseline + DB override\n * (config_plugins). Disabled plugins are surfaced but not run.\n * - Bucket loaded extensions by kind into the same `IBuiltIns` shape\n * the orchestrator already consumes. Caller merges with built-ins.\n * - Convert failure modes into stderr-ready diagnostic strings. The\n * kernel keeps booting on bad plugins — they never abort the verb.\n *\n * Returns the `Extension[]` manifest rows alongside the runtime instances\n * so the Registry can register them for `sm help` / `sm plugins list`\n * introspection without re-reading the manifests.\n */\n\nimport { resolve } from 'node:path';\n\nimport type {\n IProvider,\n IExtractor,\n IFormatter,\n IHook,\n IRule,\n} from '../../kernel/extensions/index.js';\nimport type { Extension } from '../../kernel/registry.js';\nimport {\n builtInBundles,\n type IBuiltInBundle,\n type TBuiltInExtension,\n} from '../../built-in-plugins/built-ins.js';\nimport {\n createPluginLoader,\n installedSpecVersion,\n type IPluginLoaderOptions,\n} from '../../kernel/adapters/plugin-loader.js';\nimport { loadSchemaValidators } from '../../kernel/adapters/schema-validators.js';\nimport { loadConfig } from '../../kernel/config/loader.js';\nimport { makeEnabledResolver } from '../../kernel/config/plugin-resolver.js';\nimport { qualifiedExtensionId } from '../../kernel/registry.js';\nimport type {\n IDiscoveredPlugin,\n ILoadedExtension,\n} from '../../kernel/types/plugin.js';\nimport { bucketByKind } from '../../kernel/util/bucket-by-kind.js';\nimport { sanitizeForTerminal } from '../../kernel/util/safe-text.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { PLUGIN_RUNTIME_TEXTS } from '../i18n/plugin-runtime.texts.js';\nimport {\n defaultProjectPluginsDir,\n defaultUserPluginsDir,\n resolveDbPath,\n} from './db-path.js';\nimport { defaultRuntimeContext } from './runtime-context.js';\nimport { truncateHead } from './text.js';\nimport { tryWithSqlite } from './with-sqlite.js';\n\nexport interface ILoadPluginRuntimeOptions {\n /** Resolution scope. `'global'` reads `~/.skill-map/...` only. */\n scope: 'project' | 'global';\n /** Explicit override; bypasses the project + user search paths. Tests use this. */\n pluginDir?: string;\n}\n\nexport interface IPluginRuntimeBundle {\n /** Bucketed runtime extensions keyed by kind, ready to merge with `builtIns()`. */\n extensions: {\n providers: IProvider[];\n extractors: IExtractor[];\n rules: IRule[];\n formatters: IFormatter[];\n /**\n * Loaded hook extensions (spec § A.11). Surfaced for the dispatcher\n * the orchestrator threads through the scan pipeline; built-ins\n * carry no hooks at this bump (the kind exists; concrete built-in\n * hooks land separately when demand surfaces).\n */\n hooks: IHook[];\n };\n /** Manifest rows for the Registry. One per loaded plugin extension. */\n manifests: Extension[];\n /**\n * Stderr-ready warning lines, one per failed / incompatible plugin.\n * Already prefixed with the plugin id and status. Caller writes them\n * verbatim before doing real work. `disabled` plugins are NOT in here\n * (it's the user's intent, not a problem).\n */\n warnings: string[];\n /** Raw discovery output, for callers (`sm plugins doctor`) that need it. */\n discovered: IDiscoveredPlugin[];\n /**\n * Resolver used to layer `config_plugins` (DB) over `settings.json`.\n * Surfaced so call sites that compose built-ins (`composeScanExtensions`,\n * `composeFormatters`) can apply the same precedence to the\n * `core/<ext-id>` keys without rebuilding the resolver. Returns `true`\n * for any id that has no explicit override (the default-enabled\n * fall-back). Always populated — `emptyPluginRuntime()` returns a\n * resolver that says everything is enabled.\n */\n resolveEnabled: (id: string) => boolean;\n}\n\n/**\n * Discover and load every plugin reachable from the chosen scope, with\n * the layered enabled-resolver applied.\n *\n * Never throws — a bad search path or a corrupt DB row degrades to a\n * warning and an empty (or partial) bundle. The verb that calls this\n * keeps running on whatever loaded successfully.\n */\nexport async function loadPluginRuntime(\n opts: ILoadPluginRuntimeOptions,\n): Promise<IPluginRuntimeBundle> {\n const searchPaths = resolveSearchPaths(opts);\n const validators = loadSchemaValidators();\n\n let resolveEnabled: ((id: string) => boolean) | undefined;\n try {\n resolveEnabled = await buildEnabledResolver(opts.scope);\n } catch {\n // Config / DB read failure here is non-fatal — fall through with\n // the loader's default (\"every plugin enabled\"). The actual scan\n // pipeline still runs; the user gets `sm plugins doctor` as the\n // dedicated diagnostic surface.\n }\n\n const loaderOpts: IPluginLoaderOptions = {\n searchPaths,\n validators,\n specVersion: installedSpecVersion(),\n };\n if (resolveEnabled) loaderOpts.resolveEnabled = resolveEnabled;\n const loader = createPluginLoader(loaderOpts);\n const discovered = await loader.discoverAndLoadAll();\n\n const bundle: IPluginRuntimeBundle = {\n extensions: { providers: [], extractors: [], rules: [], formatters: [], hooks: [] },\n manifests: [],\n warnings: [],\n discovered,\n resolveEnabled: resolveEnabled ?? defaultResolveEnabled,\n };\n\n for (const plugin of discovered) {\n if (plugin.status === 'enabled') {\n bucketLoaded(plugin.extensions ?? [], bundle);\n continue;\n }\n if (plugin.status === 'disabled') continue;\n bundle.warnings.push(formatWarning(plugin));\n }\n\n return bundle;\n}\n\n/**\n * Empty bundle, the right answer for `--no-plugins` paths and any caller\n * that wants the same shape without a discovery pass. Cheaper than\n * calling `loadPluginRuntime` against an empty search path.\n */\nexport function emptyPluginRuntime(): IPluginRuntimeBundle {\n return {\n extensions: { providers: [], extractors: [], rules: [], formatters: [], hooks: [] },\n manifests: [],\n warnings: [],\n discovered: [],\n resolveEnabled: defaultResolveEnabled,\n };\n}\n\n/** Default-enabled fall-back: every id is enabled when no overrides exist. */\nfunction defaultResolveEnabled(_id: string): boolean {\n return true;\n}\n\n/**\n * Granularity-aware filter for built-in bundles. Honours the spec\n * promise that \"no extension is privileged\" — every built-in is\n * removable via `config_plugins` / `settings.json`.\n *\n * Resolution rules (mirror `kernel/config/plugin-resolver.ts`):\n *\n * - bundle granularity (`claude`): the user toggles the namespace\n * once; the lookup key is `<bundle.id>` — every extension in the\n * bundle follows. A user-set DB / settings entry under\n * `<bundle.id>/<ext.id>` is silently ignored (the granularity says\n * \"this bundle is one knob\"); the validation that catches that as\n * a CLI input error happens upstream in `sm plugins enable/disable`.\n * - extension granularity (`core`): the lookup key is the qualified\n * id `<bundle.id>/<ext.id>`. Each extension is independently\n * toggle-able.\n *\n * Defaults to `true` for any id without an explicit override.\n */\nexport function isBuiltInExtensionEnabled(\n bundle: IBuiltInBundle,\n ext: TBuiltInExtension,\n resolveEnabled: (id: string) => boolean,\n): boolean {\n return isBundleEntryEnabled(bundle, ext.id, resolveEnabled);\n}\n\n/**\n * Underlying primitive — works on the plain extension `id` rather than\n * a typed extension instance, so it can be reused from manifest-side\n * filters (`filterBuiltInManifests`) where the value is `IPluginManifest`,\n * not `TBuiltInExtension`. Same toggle semantics as\n * `isBuiltInExtensionEnabled`.\n */\nfunction isBundleEntryEnabled(\n bundle: IBuiltInBundle,\n extId: string,\n resolveEnabled: (id: string) => boolean,\n): boolean {\n if (bundle.granularity === 'bundle') {\n return resolveEnabled(bundle.id);\n }\n return resolveEnabled(qualifiedExtensionId(bundle.id, extId));\n}\n\n/**\n * Conformance-only kill-switch env vars (mirrored in the\n * `conformance-case.schema.json#/properties/setup` toggles). Each var\n * drops every extension of its kind from the scan composer regardless of\n * granularity gates and `--no-built-ins`. Production callers MUST NOT\n * set these — they exist so the conformance runner can drive the\n * `kernel-empty-boot` invariant (and any future case that needs an\n * isolated kind) without depending on fixture content being empty.\n *\n * Truthy = literal `'1'`. Anything else (absent, `'0'`, `'true'`,\n * whitespace) is treated as off so the runner injecting `'1'` is\n * unambiguous and a stray export of the variable in a developer shell\n * does not silently disable production scans.\n */\nconst ENV_DISABLE_PROVIDERS = 'SKILL_MAP_DISABLE_ALL_PROVIDERS';\nconst ENV_DISABLE_EXTRACTORS = 'SKILL_MAP_DISABLE_ALL_EXTRACTORS';\nconst ENV_DISABLE_RULES = 'SKILL_MAP_DISABLE_ALL_RULES';\n\nfunction envFlagOn(name: string): boolean {\n return process.env[name] === '1';\n}\n\n/**\n * Compose the `IScanExtensions` shape the orchestrator consumes. Built-ins\n * load conditionally (gated by `--no-built-ins`); plugin extensions always\n * fold in, even under `--no-built-ins` — the user wants a stripped-down\n * pipeline of \"just my plugins\" in that combo. To get a fully empty\n * pipeline (kernel-empty-boot) the caller passes both `--no-built-ins`\n * AND `--no-plugins`.\n *\n * Built-ins are also gated by `pluginRuntime.resolveEnabled`: a user that\n * disables `claude` (bundle granularity) drops the four Claude\n * extensions; a user that disables `core/superseded` (extension\n * granularity) drops only that rule. `--no-built-ins` is the macro\n * override that wins when both layers say \"skip\".\n *\n * Conformance kill-switches (`SKILL_MAP_DISABLE_ALL_{PROVIDERS,EXTRACTORS,RULES}=1`)\n * win over both layers — they drop every extension of the chosen kind\n * from the composed bundle, including user plugins. The conformance\n * runner injects them per `setup.disableAll*` toggle; nothing in the\n * production code path sets them.\n *\n * Returns `undefined` when both halves are empty so the orchestrator\n * follows its zero-extension code path.\n */\n// eslint-disable-next-line complexity\nexport function composeScanExtensions(opts: {\n noBuiltIns: boolean;\n pluginRuntime: IPluginRuntimeBundle;\n}): {\n providers: IProvider[];\n extractors: IExtractor[];\n rules: IRule[];\n hooks: IHook[];\n} | undefined {\n const providers: IProvider[] = [];\n const extractors: IExtractor[] = [];\n const rules: IRule[] = [];\n const hooks: IHook[] = [];\n\n if (!opts.noBuiltIns) {\n accumulateBuiltInScanExtensions(\n { providers, extractors, rules, hooks },\n opts.pluginRuntime.resolveEnabled,\n );\n }\n providers.push(...opts.pluginRuntime.extensions.providers);\n extractors.push(...opts.pluginRuntime.extensions.extractors);\n rules.push(...opts.pluginRuntime.extensions.rules);\n hooks.push(...opts.pluginRuntime.extensions.hooks);\n\n // Conformance kill-switches. Applied last so they trump every other\n // gate (granularity, --no-built-ins, plugin enable/disable).\n const disabledProviders = envFlagOn(ENV_DISABLE_PROVIDERS);\n const disabledExtractors = envFlagOn(ENV_DISABLE_EXTRACTORS);\n const disabledRules = envFlagOn(ENV_DISABLE_RULES);\n const finalProviders = disabledProviders ? [] : providers;\n const finalExtractors = disabledExtractors ? [] : extractors;\n const finalRules = disabledRules ? [] : rules;\n\n if (\n finalProviders.length === 0 &&\n finalExtractors.length === 0 &&\n finalRules.length === 0 &&\n hooks.length === 0\n ) {\n return undefined;\n }\n return {\n providers: finalProviders,\n extractors: finalExtractors,\n rules: finalRules,\n hooks,\n };\n}\n\n/**\n * Walk every built-in bundle, drop disabled extensions per the\n * resolver, and bucket the survivors into the per-kind arrays.\n * Formatters are consumed by `composeFormatters`, not scan, so they\n * are skipped here even if the bundle ships them.\n */\n// Discriminated-union dispatcher — one branch per `ext.kind` plus the\n// disabled-guard up front. Cyclomatic count comes from the six-kind\n// switch + the per-bundle iteration; splitting per kind would scatter\n// the dispatch table without making the algorithm clearer.\n// eslint-disable-next-line complexity\nfunction accumulateBuiltInScanExtensions(\n buckets: { providers: IProvider[]; extractors: IExtractor[]; rules: IRule[]; hooks: IHook[] },\n resolveEnabled: (id: string) => boolean,\n): void {\n for (const bundle of builtInBundles) {\n for (const ext of bundle.extensions) {\n if (!isBuiltInExtensionEnabled(bundle, ext, resolveEnabled)) continue;\n switch (ext.kind) {\n case 'provider':\n buckets.providers.push(ext);\n break;\n case 'extractor':\n buckets.extractors.push(ext);\n break;\n case 'rule':\n buckets.rules.push(ext);\n break;\n case 'hook':\n buckets.hooks.push(ext);\n break;\n case 'action':\n // Actions dispatch via the job subsystem (Step 10), not the\n // scan pipeline. Skip here; their manifests still register.\n break;\n case 'formatter':\n // Formatters are consumed by `composeFormatters`, not scan.\n break;\n default: {\n const _exhaustive: never = ext;\n throw new Error(`Unhandled built-in extension kind: ${String((_exhaustive as { kind?: string }).kind)}`);\n }\n }\n }\n }\n}\n\n/**\n * Same idea as `composeScanExtensions` but for formatters (consumed by\n * `sm graph`). Built-ins layer first, plugin formatters after — first\n * registration wins on a `formatId` collision, which keeps the kernel's\n * defaults predictable when a plugin claims an existing format. Built-in\n * formatters respect the same granularity filter as scan-side built-ins.\n */\nexport function composeFormatters(opts: {\n noBuiltIns?: boolean;\n pluginRuntime: IPluginRuntimeBundle;\n}): IFormatter[] {\n const noBuiltIns = opts.noBuiltIns ?? false;\n const out: IFormatter[] = [];\n if (!noBuiltIns) {\n for (const bundle of builtInBundles) {\n for (const ext of bundle.extensions) {\n if (ext.kind !== 'formatter') continue;\n if (!isBuiltInExtensionEnabled(bundle, ext, opts.pluginRuntime.resolveEnabled)) continue;\n out.push(ext);\n }\n }\n }\n out.push(...opts.pluginRuntime.extensions.formatters);\n return out;\n}\n\n/**\n * Granularity-aware filter for built-in registry rows. Used by call\n * sites (scan / scan-compare / watch) that register built-in manifests\n * via `listBuiltIns()` BEFORE the orchestrator runs — without this\n * filter a user-disabled built-in would appear in `sm help` /\n * `sm plugins list` as if it were live, contradicting the granularity\n * model.\n */\nexport function filterBuiltInManifests(\n manifests: Extension[],\n resolveEnabled: (id: string) => boolean,\n): Extension[] {\n // Build a per-bundle index so the filter respects whichever granularity\n // each built-in row's owning bundle declared. The index is rebuilt\n // every call (cheap — two bundles, eleven extensions).\n const bundleByPluginId = new Map<string, IBuiltInBundle>();\n for (const bundle of builtInBundles) bundleByPluginId.set(bundle.id, bundle);\n\n return manifests.filter((m) => {\n const bundle = bundleByPluginId.get(m.pluginId);\n if (!bundle) return true; // not a built-in row — leave it alone.\n return isBundleEntryEnabled(bundle, m.id, resolveEnabled);\n });\n}\n\n/** Project + user search paths, or the explicit override. */\nfunction resolveSearchPaths(opts: ILoadPluginRuntimeOptions): string[] {\n if (opts.pluginDir) return [resolve(opts.pluginDir)];\n const ctx = defaultRuntimeContext();\n const project = defaultProjectPluginsDir(ctx);\n const user = defaultUserPluginsDir(ctx);\n return opts.scope === 'global' ? [user] : [project, user];\n}\n\n/**\n * Build the layered settings.json + DB enabled-resolver. Mirrors the\n * shape of `buildResolver` in `src/cli/commands/plugins.ts` (Step 6.6)\n * to keep the resolution policy in lock-step. Any divergence between\n * `sm plugins list` and the runtime would be a confusing UX regression.\n */\nasync function buildEnabledResolver(\n scope: 'project' | 'global',\n): Promise<(id: string) => boolean> {\n const ctx = defaultRuntimeContext();\n const { effective: cfg } = loadConfig({ scope, ...ctx });\n const dbPath = resolveDbPath({\n global: scope === 'global',\n db: undefined,\n ...ctx,\n });\n const dbOverrides =\n (await tryWithSqlite(\n { databasePath: dbPath, autoBackup: false },\n (adapter) => adapter.pluginConfig.loadOverrideMap(),\n )) ?? new Map<string, boolean>();\n return makeEnabledResolver(cfg, dbOverrides);\n}\n\n/**\n * Drop a plugin's loaded extensions into the per-kind buckets. Each\n * `ext.instance` arrives from the loader already cloned with\n * `pluginId` injected (spec § A.6), so this function never mutates.\n *\n * Shares the dispatch table with `built-in-plugins/built-ins.ts:\n * bucketBuiltIn` via `bucketByKind`. Actions are intentionally NOT\n * passed a destination array — they dispatch via the job subsystem\n * (Step 10), not the scan pipeline. The manifest row still records\n * regardless of kind so `sm plugins list` / `sm actions list` see\n * every extension that loaded.\n */\nfunction bucketLoaded(loaded: ILoadedExtension[], bundle: IPluginRuntimeBundle): void {\n for (const ext of loaded) {\n const instance = ext.instance;\n if (!isExtensionInstance(instance)) continue;\n bucketByKind(ext.kind, instance, {\n provider: bundle.extensions.providers,\n extractor: bundle.extensions.extractors,\n rule: bundle.extensions.rules,\n formatter: bundle.extensions.formatters,\n hook: bundle.extensions.hooks,\n // `action` intentionally absent — see docstring.\n });\n bundle.manifests.push({\n id: ext.id,\n pluginId: ext.pluginId,\n kind: ext.kind,\n version: ext.version,\n ...(ext.entryPath ? { entry: ext.entryPath } : {}),\n });\n }\n}\n\n\nfunction isExtensionInstance(v: unknown): v is { id: string; kind: string; version: string } {\n return (\n typeof v === 'object' &&\n v !== null &&\n typeof (v as Record<string, unknown>)['id'] === 'string' &&\n typeof (v as Record<string, unknown>)['kind'] === 'string' &&\n typeof (v as Record<string, unknown>)['version'] === 'string'\n );\n}\n\n// Caps for interpolated values in the warning template. The plugin id\n// passes through the loader's regex validator (short, well-shaped) but\n// is bounded as defence-in-depth. The reason string is plugin-authored\n// (manifest fragments + AJV `instancePath`/`message`, `describe(err)`\n// return values) and unbounded — a hostile or buggy plugin could emit\n// kilobytes of payload that drown the user's terminal.\nconst PLUGIN_ID_DISPLAY_CAP = 200;\nconst PLUGIN_REASON_DISPLAY_CAP = 1000;\n\n/**\n * Render a single-line, scannable diagnostic for a non-loaded plugin.\n * The status name doubles as the failure category so a user can grep\n * `incompatible-spec` / `invalid-manifest` / `load-error` and see the\n * full context. Template lives in `cli/i18n/plugin-runtime.texts.ts`.\n *\n * Both `id` and `reason` flow from plugin-authored sources (manifest\n * fields, AJV error fragments, `describe(err)` payloads). Sanitize +\n * cap before interpolation so a hostile plugin cannot smuggle ANSI\n * control sequences into the user's terminal via its own diagnostic\n * surface.\n *\n * Exported solely for the audit H1 unit tests in\n * `test/plugin-runtime.test.ts` — production callers reach it through\n * `loadPluginRuntime` and write the rendered lines straight to stderr.\n * Renaming or removing the export is a breaking change for the test\n * suite, not for any consumer.\n */\nexport function formatWarning(plugin: IDiscoveredPlugin): string {\n const rawReason = plugin.reason ?? PLUGIN_RUNTIME_TEXTS.warningReasonMissing;\n return tx(PLUGIN_RUNTIME_TEXTS.warningRow, {\n id: sanitizeForTerminal(truncateHead(plugin.id, PLUGIN_ID_DISPLAY_CAP)),\n status: plugin.status,\n reason: sanitizeForTerminal(truncateHead(rawReason, PLUGIN_REASON_DISPLAY_CAP)),\n });\n}\n","/**\n * Shared per-kind dispatcher for extension bucketing.\n *\n * Two call sites — `built-in-plugins/built-ins.ts:bucketBuiltIn` and\n * `cli/util/plugin-runtime.ts:bucketLoaded` — used to open-code an\n * identical six-way `switch (ext.kind) {...}` block, each with the\n * same exhaustive-`never` guard. They diverged only in (a) whether\n * actions / hooks land in arrays of their own and (b) whether a\n * manifest row is recorded alongside the runtime instance.\n *\n * `bucketByKind` is the centralized dispatch. It receives:\n *\n * - the extension kind (the discriminator),\n * - the runtime instance, typed broadly as `unknown` so the helper\n * doesn't take a hard dependency on each kind's concrete TS type\n * (avoids a circular import between `kernel/util/` and\n * `kernel/extensions/`),\n * - a `bag` mapping each kind to its destination array. Each entry\n * is optional — a caller that doesn't care about a given kind\n * (`bucketLoaded` skips actions, for instance) leaves it `undefined`\n * and the helper drops that branch on the floor.\n *\n * The helper still owns the exhaustive switch (so adding a new kind to\n * `ExtensionKind` flags every caller through the `never` guard) and\n * gets one `eslint-disable-next-line complexity` comment, justified by\n * AGENTS.md category 6 (discriminated-union dispatcher) — splitting\n * per case would scatter the central dispatch table without making\n * the algorithm clearer.\n */\n\nimport type { ExtensionKind } from '../registry.js';\n\n/**\n * Per-kind destination arrays. Each property is optional — a caller\n * that ignores a kind passes `undefined` (or simply omits the key).\n * The instance itself is typed `unknown`; callers cast to the kind's\n * concrete type at the destination.\n */\nexport interface IBucketByKindBag {\n provider?: unknown[];\n extractor?: unknown[];\n rule?: unknown[];\n action?: unknown[];\n formatter?: unknown[];\n hook?: unknown[];\n}\n\n/**\n * Push `instance` into `bag[kind]` if the caller declared a\n * destination array for that kind; otherwise drop it. Throws on an\n * unknown kind so a future widening of `ExtensionKind` cannot silently\n * land an instance in nowhere.\n */\n// AGENTS.md category 6 (discriminated-union dispatcher): one branch per\n// `ExtensionKind` plus a per-kind optional-chain guard for absent\n// destination arrays. Splitting per kind would scatter the central\n// dispatch table without making the algorithm clearer.\n// eslint-disable-next-line complexity\nexport function bucketByKind(\n kind: ExtensionKind,\n instance: unknown,\n bag: IBucketByKindBag,\n): void {\n switch (kind) {\n case 'provider':\n bag.provider?.push(instance);\n return;\n case 'extractor':\n bag.extractor?.push(instance);\n return;\n case 'rule':\n bag.rule?.push(instance);\n return;\n case 'action':\n bag.action?.push(instance);\n return;\n case 'formatter':\n bag.formatter?.push(instance);\n return;\n case 'hook':\n bag.hook?.push(instance);\n return;\n default: {\n // Exhaustive guard: a new `ExtensionKind` variant must extend\n // this switch. The `_exhaustive: never` binding triggers a\n // compile error if the discriminant is not exhausted; the\n // runtime throw is defensive in case TS is silenced via\n // `as never` at the call site.\n const _exhaustive: never = kind;\n throw new Error(`Unhandled extension kind: ${String(_exhaustive)}`);\n }\n }\n}\n","/**\n * Built-in `claude` Provider. Walks Claude Code's on-disk convention:\n *\n * <root>/.claude/agents/*.md → kind: agent\n * <root>/.claude/commands/*.md → kind: command\n * <root>/.claude/hooks/*.md → kind: hook\n * <root>/.claude/skills/<name>/SKILL.md → kind: skill\n * <root>/notes/**.md → kind: note\n * <root>/**.md (fallback) → kind: note\n *\n * Frontmatter is parsed with js-yaml; anything that fails to parse still\n * produces a node with an empty-object frontmatter so the scan keeps\n * advancing. Pure filesystem walk + parse — no DB awareness.\n *\n * **Phase 3 (spec 0.8.0).** The Provider owns the per-kind frontmatter\n * schemas (relocated from spec — `skill`, `agent`, `command`, `hook`,\n * `note`). The flat `defaultRefreshAction` map collapsed into the\n * `kinds` map; each kind entry pairs the loaded JSON Schema with its\n * qualified refresh action id. The kernel's frontmatter-validation flow\n * asks the Provider for the schema instead of reading directly from\n * spec/.\n */\n\nimport { readFile, readdir, stat } from 'node:fs/promises';\nimport { join, relative, sep } from 'node:path';\n\nimport yaml from 'js-yaml';\n\nimport { buildIgnoreFilter, type IIgnoreFilter } from '../../../kernel/scan/ignore.js';\nimport type { IProvider, IRawNode } from '../../../kernel/extensions/index.js';\nimport type { NodeKind } from '../../../kernel/types.js';\nimport skillSchema from './schemas/skill.schema.json' with { type: 'json' };\nimport agentSchema from './schemas/agent.schema.json' with { type: 'json' };\nimport commandSchema from './schemas/command.schema.json' with { type: 'json' };\nimport hookSchema from './schemas/hook.schema.json' with { type: 'json' };\nimport noteSchema from './schemas/note.schema.json' with { type: 'json' };\n\nconst FRONTMATTER_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/;\n\nexport const claudeProvider: IProvider = {\n id: 'claude',\n pluginId: 'claude',\n kind: 'provider',\n version: '1.0.0',\n description: 'Walks Claude Code scope conventions (.claude/{agents,commands,hooks,skills} + notes).',\n stability: 'stable',\n\n // The Claude Provider's content lives under `~/.claude` for the global\n // scope (and inside `.claude/` for project scope). `sm doctor` validates\n // the directory exists for global scope; missing → non-blocking warning.\n explorationDir: '~/.claude',\n\n // Per spec § A.6, defaultRefreshAction values MUST be qualified action\n // ids. The summarize-* actions are not yet implemented as registry\n // entries (they ship later under the Claude bundle), but the qualified\n // form is the contract: when those actions land, they will register\n // under `claude/summarize-<kind>` and the Provider resolves them\n // deterministically.\n //\n // Phase 3 (spec 0.8.0): the per-kind catalog lives here. Each entry\n // pairs the relative manifest-style schema path (mirrors what the\n // spec's provider.schema.json validates) with the loaded JSON Schema\n // (`schemaJson`) the kernel registers with AJV at scan boot.\n kinds: {\n agent: {\n schema: './schemas/agent.schema.json',\n schemaJson: agentSchema,\n defaultRefreshAction: 'claude/summarize-agent',\n },\n command: {\n schema: './schemas/command.schema.json',\n schemaJson: commandSchema,\n defaultRefreshAction: 'claude/summarize-command',\n },\n hook: {\n schema: './schemas/hook.schema.json',\n schemaJson: hookSchema,\n defaultRefreshAction: 'claude/summarize-hook',\n },\n skill: {\n schema: './schemas/skill.schema.json',\n schemaJson: skillSchema,\n defaultRefreshAction: 'claude/summarize-skill',\n },\n note: {\n schema: './schemas/note.schema.json',\n schemaJson: noteSchema,\n defaultRefreshAction: 'claude/summarize-note',\n },\n },\n\n async *walk(roots, options = {}): AsyncIterable<IRawNode> {\n // The orchestrator is the canonical source of the filter (it composes\n // bundled defaults + config.ignore + .skill-mapignore). When the\n // Provider is invoked directly (tests, kernel-empty-boot), fall back\n // to bundled defaults only — that's still enough to keep `.git`,\n // `node_modules`, and friends out of the result.\n const filter: IIgnoreFilter = options.ignoreFilter ?? buildIgnoreFilter();\n for (const root of roots) {\n for await (const file of walkMarkdown(root, root, filter)) {\n const relPath = relative(root, file).split(sep).join('/');\n const raw = await readFile(file, 'utf8');\n const parsed = splitFrontmatter(raw);\n yield {\n path: relPath,\n body: parsed.body,\n frontmatterRaw: parsed.frontmatterRaw,\n frontmatter: parsed.frontmatter,\n };\n }\n }\n },\n\n classify(path: string): NodeKind {\n const lower = path.toLowerCase();\n if (lower.startsWith('.claude/agents/')) return 'agent';\n if (lower.startsWith('.claude/commands/')) return 'command';\n if (lower.startsWith('.claude/hooks/')) return 'hook';\n if (lower.startsWith('.claude/skills/')) return 'skill';\n return 'note';\n },\n};\n\n// eslint-disable-next-line complexity\nasync function* walkMarkdown(\n root: string,\n current: string,\n filter: IIgnoreFilter,\n): AsyncIterable<string> {\n let entries;\n try {\n entries = await readdir(current, { withFileTypes: true, encoding: 'utf8' });\n } catch {\n return;\n }\n for (const entry of entries) {\n const name = entry.name;\n const full = join(current, name);\n const rel = relative(root, full).split(sep).join('/');\n if (filter.ignores(rel)) continue;\n // Symlinks are skipped explicitly (audit M7). The follow-symlinks\n // config knob (`scan.followSymlinks` in settings.json) is reserved\n // for a future implementation that would also need cycle detection\n // and a `realpath`-resolved containment check; until then the\n // walker stays in the safe default. Without this guard we relied on\n // `Dirent.isFile()` returning false for symlinks — an implementation\n // detail of node's `withFileTypes`. The explicit skip is both\n // self-documenting and resilient to future Dirent API changes.\n if (entry.isSymbolicLink()) continue;\n if (entry.isDirectory()) {\n yield* walkMarkdown(root, full, filter);\n } else if (entry.isFile() && name.endsWith('.md')) {\n // stat() guards against TOCTOU races where readdir reported a\n // regular file and the entry was swapped for a symlink between\n // calls. `stat` follows symlinks; rejecting non-regular results\n // closes that lane too.\n try {\n const s = await stat(full);\n if (s.isFile()) yield full;\n } catch {\n // silently skip unreadable files\n }\n }\n }\n}\n\ninterface ISplitResult {\n frontmatterRaw: string;\n frontmatter: Record<string, unknown>;\n body: string;\n}\n\nconst FORBIDDEN_FRONTMATTER_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\nfunction splitFrontmatter(raw: string): ISplitResult {\n const match = FRONTMATTER_RE.exec(raw);\n if (!match) return { frontmatterRaw: '', frontmatter: {}, body: raw };\n const frontmatterRaw = match[1]!;\n const body = match[2]!;\n const parsed: Record<string, unknown> = {};\n try {\n // Defence in depth (audit L3): pin the parser schema explicitly.\n // js-yaml v4's default schema is already safe (no `!!js/function`\n // tags) but the explicit `JSON_SCHEMA` selection both documents\n // intent and protects against an upstream default flip. Frontmatter\n // values that are valid JSON (string, number, bool, null, sequence,\n // mapping) round-trip unchanged; YAML-only conveniences like\n // unquoted timestamps would degrade to strings, but the kernel's\n // node schema does not depend on parsed Date objects so the\n // tradeoff is safe.\n const doc = yaml.load(frontmatterRaw, { schema: yaml.JSON_SCHEMA });\n if (doc && typeof doc === 'object' && !Array.isArray(doc)) {\n // js-yaml stores `__proto__:` as an own data property (rather than\n // polluting Object.prototype), but the value still flows into\n // downstream `Object.assign`-style merges where the `__proto__`\n // setter fires. Strip pollution-class keys at parse time so the\n // returned object is safe to spread, copy, and persist. Prototype\n // stays normal so `deepStrictEqual` round-trips against the\n // persisted form (which goes through `JSON.parse` and inherits\n // Object.prototype).\n for (const [k, v] of Object.entries(doc as Record<string, unknown>)) {\n if (FORBIDDEN_FRONTMATTER_KEYS.has(k)) continue;\n parsed[k] = v;\n }\n }\n } catch {\n // Malformed YAML — leave as empty object, keep the raw string for\n // downstream diagnostics.\n }\n return { frontmatterRaw, frontmatter: parsed, body };\n}\n","/**\n * `.skill-mapignore` parser + filter facade. Wraps `ignore` (kaelzhang)\n * with the project-local layering: bundled defaults → `config.ignore`\n * (from `.skill-map/settings.json`) → `.skill-mapignore` file content.\n *\n * Why a wrapper instead of exposing `ignore` directly:\n *\n * 1. Single-source defaults — `src/config/defaults/skill-mapignore` is\n * the canonical default list, loaded once at module init (or at\n * explicit build time, depending on bundling). The runtime never\n * re-reads it per scan.\n * 2. Stable interface — Providers and the orchestrator depend on a\n * minimal `IIgnoreFilter` shape, so the underlying library can be\n * swapped without touching every consumer.\n * 3. Path normalization — every consumer passes the path RELATIVE to\n * the scan root (POSIX separators); the wrapper guarantees that\n * contract before delegating to `ignore`.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport ignoreFactory from 'ignore';\n\nexport interface IIgnoreFilter {\n /**\n * Returns `true` when `relativePath` should be skipped. The caller\n * MUST pass paths relative to the scan root, with POSIX separators\n * (forward slashes), no leading `/`. Directories MAY be passed with\n * or without trailing `/`; the wrapper does not require it.\n */\n ignores(relativePath: string): boolean;\n}\n\nexport interface IBuildIgnoreFilterOptions {\n /** Patterns from `config.ignore` in `.skill-map/settings.json`. */\n configIgnore?: string[] | undefined;\n /**\n * Raw text of the project's `.skill-mapignore` file. Comments and\n * blank lines are tolerated by `ignore` itself; the caller does not\n * need to pre-process. Accepts `undefined` so callers can forward\n * `readIgnoreFileText()` directly without a guard.\n */\n ignoreFileText?: string | undefined;\n /**\n * When `false`, the bundled defaults are NOT pre-loaded. Default is\n * `true`. Tests use `false` to assert the precise effect of a single\n * pattern.\n */\n includeDefaults?: boolean | undefined;\n}\n\n/**\n * Build a filter from any combination of layers. Layer order is fixed:\n *\n * 1. bundled defaults (`src/config/defaults/skill-mapignore`)\n * 2. `configIgnore`\n * 3. `ignoreFileText`\n *\n * Later layers override earlier ones via gitignore negation rules\n * (`!pattern` re-includes a path the prior layer excluded).\n */\nexport function buildIgnoreFilter(opts: IBuildIgnoreFilterOptions = {}): IIgnoreFilter {\n const ig = ignoreFactory();\n if (opts.includeDefaults !== false) {\n ig.add(loadDefaultsText());\n }\n if (opts.configIgnore && opts.configIgnore.length > 0) {\n ig.add(opts.configIgnore);\n }\n if (opts.ignoreFileText && opts.ignoreFileText.length > 0) {\n ig.add(opts.ignoreFileText);\n }\n return {\n ignores(relativePath: string): boolean {\n // `ignore` requires a non-empty relative path; the empty string\n // (the root itself) MUST never be ignored.\n if (relativePath === '' || relativePath === '.' || relativePath === './') {\n return false;\n }\n const normalised = relativePath.replace(/^\\.\\//, '').replace(/\\\\/g, '/').replace(/^\\//, '');\n if (normalised === '') return false;\n return ig.ignores(normalised);\n },\n };\n}\n\n/**\n * Return the bundled defaults text. Useful for `sm init` (which writes\n * the file into the user's scope) and for tests. The same caching\n * logic backs `buildIgnoreFilter` so this never re-reads from disk on\n * a hot path.\n */\nexport function loadBundledIgnoreText(): string {\n return loadDefaultsText();\n}\n\n/**\n * Read `.skill-mapignore` from `<root>/.skill-mapignore` if it exists,\n * else return `undefined`. Caller passes the result as `ignoreFileText`\n * to `buildIgnoreFilter`.\n */\nexport function readIgnoreFileText(scopeRoot: string): string | undefined {\n const path = resolve(scopeRoot, '.skill-mapignore');\n if (!existsSync(path)) return undefined;\n try {\n return readFileSync(path, 'utf8');\n } catch {\n return undefined;\n }\n}\n\n// -----------------------------------------------------------------------------\n// Bundled defaults loader\n// -----------------------------------------------------------------------------\n\nlet cachedDefaults: string | null = null;\n\nfunction loadDefaultsText(): string {\n if (cachedDefaults !== null) return cachedDefaults;\n cachedDefaults = readDefaultsFromDisk();\n return cachedDefaults;\n}\n\n/** Test-only — drop the cache so a unit test can simulate a missing file. */\nexport function _resetDefaultsCacheForTests(): void {\n cachedDefaults = null;\n}\n\n/**\n * Resolve `src/config/defaults/skill-mapignore` from disk. Walks a small\n * list of candidate locations relative to this module so the lookup\n * works in both the dev layout (`src/kernel/scan/ignore.ts` →\n * `src/config/defaults/`) and the bundled layout (single-file\n * `dist/...js` → `dist/config/defaults/`, populated by tsup `onSuccess`).\n */\nfunction readDefaultsFromDisk(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n const candidates = [\n resolve(here, '../../config/defaults/skill-mapignore'), // src/kernel/scan/ → src/config/defaults/\n resolve(here, '../config/defaults/skill-mapignore'), // dist/cli.js → dist/config/defaults/ (siblings)\n resolve(here, 'config/defaults/skill-mapignore'),\n ];\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n try {\n return readFileSync(candidate, 'utf8');\n } catch {\n /* try next candidate */\n }\n }\n }\n // Fail soft: the scan still works without bundled defaults. The user's\n // own `.skill-mapignore` + config.ignore still apply.\n return '';\n}\n","{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://skill-map.dev/providers/claude/v1/frontmatter/skill.schema.json\",\n \"title\": \"FrontmatterSkill\",\n \"description\": \"Frontmatter shape for nodes classified as `skill` by the Claude Provider. Extends the spec's universal `frontmatter/base.schema.json` via $ref-by-$id. Kind-specific fields: `inputs`, `outputs`. Stability: experimental — the parameter shape may tighten once summarizer needs emerge. Owned by the Claude Provider; relocated from `spec/schemas/frontmatter/` in spec 0.8.0 (Phase 3 of plug-in model overhaul).\",\n \"allOf\": [\n { \"$ref\": \"https://skill-map.dev/spec/v0/frontmatter/base.schema.json\" }\n ],\n \"type\": \"object\",\n \"additionalProperties\": true,\n \"properties\": {\n \"inputs\": {\n \"type\": \"array\",\n \"description\": \"Declared inputs for this skill. Optional structured. Stability: experimental.\",\n \"items\": { \"$ref\": \"#/$defs/Parameter\" }\n },\n \"outputs\": {\n \"type\": \"array\",\n \"description\": \"Declared outputs. Optional structured. Stability: experimental.\",\n \"items\": { \"$ref\": \"#/$defs/Parameter\" }\n }\n },\n \"$defs\": {\n \"Parameter\": {\n \"type\": \"object\",\n \"required\": [\"name\"],\n \"additionalProperties\": true,\n \"properties\": {\n \"name\": { \"type\": \"string\", \"minLength\": 1 },\n \"type\": { \"type\": \"string\", \"description\": \"Free-form type hint (e.g. `string`, `integer`, `path`, `glob`).\" },\n \"description\": { \"type\": \"string\" },\n \"required\": { \"type\": \"boolean\", \"default\": false },\n \"default\": { \"description\": \"Any JSON value, or a templated string.\" }\n }\n }\n }\n}\n","{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://skill-map.dev/providers/claude/v1/frontmatter/agent.schema.json\",\n \"title\": \"FrontmatterAgent\",\n \"description\": \"Frontmatter shape for nodes classified as `agent` by the Claude Provider. Extends the spec's universal `frontmatter/base.schema.json` via $ref-by-$id. Kind-specific field: `model`. The `tools` and `allowedTools` fields live on the spec base and apply here unchanged. Color, when needed, lives in `metadata.color` (inherited from base). Owned by the Claude Provider; relocated from `spec/schemas/frontmatter/` in spec 0.8.0 (Phase 3 of plug-in model overhaul).\",\n \"allOf\": [\n { \"$ref\": \"https://skill-map.dev/spec/v0/frontmatter/base.schema.json\" }\n ],\n \"type\": \"object\",\n \"additionalProperties\": true,\n \"properties\": {\n \"model\": {\n \"type\": \"string\",\n \"description\": \"Model identifier for this agent (e.g. `sonnet`, `opus`, `haiku`, or a full `claude-*` id). Free-form string; Providers MAY validate against a platform-specific enum.\"\n }\n }\n}\n","{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://skill-map.dev/providers/claude/v1/frontmatter/command.schema.json\",\n \"title\": \"FrontmatterCommand\",\n \"description\": \"Frontmatter shape for nodes classified as `command` by the Claude Provider. Extends the spec's universal `frontmatter/base.schema.json` via $ref-by-$id. Kind-specific fields: `args`, `shortcut`. Owned by the Claude Provider; relocated from `spec/schemas/frontmatter/` in spec 0.8.0 (Phase 3 of plug-in model overhaul).\",\n \"allOf\": [\n { \"$ref\": \"https://skill-map.dev/spec/v0/frontmatter/base.schema.json\" }\n ],\n \"type\": \"object\",\n \"additionalProperties\": true,\n \"properties\": {\n \"args\": {\n \"type\": \"array\",\n \"description\": \"Declared positional / named arguments for this command.\",\n \"items\": { \"$ref\": \"#/$defs/CommandArg\" }\n },\n \"shortcut\": {\n \"type\": \"string\",\n \"description\": \"Keyboard shortcut hint. Platform-specific notation (e.g. `cmd+shift+k`, `ctrl+alt+m`). Purely advisory.\"\n }\n },\n \"$defs\": {\n \"CommandArg\": {\n \"type\": \"object\",\n \"required\": [\"name\"],\n \"additionalProperties\": true,\n \"properties\": {\n \"name\": { \"type\": \"string\", \"minLength\": 1 },\n \"type\": {\n \"type\": \"string\",\n \"description\": \"Free-form type hint (e.g. `string`, `integer`, `path`, `boolean`, `enum:a|b|c`).\"\n },\n \"required\": { \"type\": \"boolean\", \"default\": false },\n \"description\": { \"type\": \"string\" },\n \"default\": { \"description\": \"Any JSON value.\" }\n }\n }\n }\n}\n","{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://skill-map.dev/providers/claude/v1/frontmatter/hook.schema.json\",\n \"title\": \"FrontmatterHook\",\n \"description\": \"Frontmatter shape for nodes classified as `hook` by the Claude Provider. Extends the spec's universal `frontmatter/base.schema.json` via $ref-by-$id. Kind-specific fields: `event`, `condition`, `blocking`, `idempotent`. Owned by the Claude Provider; relocated from `spec/schemas/frontmatter/` in spec 0.8.0 (Phase 3 of plug-in model overhaul).\",\n \"allOf\": [\n { \"$ref\": \"https://skill-map.dev/spec/v0/frontmatter/base.schema.json\" }\n ],\n \"type\": \"object\",\n \"additionalProperties\": true,\n \"properties\": {\n \"event\": {\n \"type\": \"string\",\n \"description\": \"Event name this hook reacts to (e.g. `PreToolUse`, `PostToolUse`, `SubagentStop`, `SessionStart`). Platform-specific enum; Providers MAY validate.\"\n },\n \"condition\": {\n \"type\": \"string\",\n \"description\": \"Free-form predicate expression evaluated by the host. Syntax is platform-specific; the spec does not constrain it.\"\n },\n \"blocking\": {\n \"type\": \"boolean\",\n \"description\": \"When true, the host MUST wait for the hook to finish before proceeding. When false, the hook runs fire-and-forget.\"\n },\n \"idempotent\": {\n \"type\": \"boolean\",\n \"description\": \"Author-declared: executing twice with the same inputs produces the same result. Consumed by runners for retry and dedup.\"\n }\n }\n}\n","{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://skill-map.dev/providers/claude/v1/frontmatter/note.schema.json\",\n \"title\": \"FrontmatterNote\",\n \"description\": \"Frontmatter shape for nodes classified as `note` by the Claude Provider. Extends the spec's universal `frontmatter/base.schema.json` via $ref-by-$id with no additional fields. Exists so that every kind has a matching schema for uniform routing. Owned by the Claude Provider; relocated from `spec/schemas/frontmatter/` in spec 0.8.0 (Phase 3 of plug-in model overhaul).\",\n \"allOf\": [\n { \"$ref\": \"https://skill-map.dev/spec/v0/frontmatter/base.schema.json\" }\n ],\n \"type\": \"object\",\n \"additionalProperties\": true\n}\n","/**\n * Frontmatter extractor. Reads the parsed frontmatter block and emits one\n * link per structured reference:\n *\n * metadata.supersedes[] → supersedes links (this node → listed paths)\n * metadata.supersededBy → supersedes link (listed path → this node)\n * metadata.requires[] → references links\n * metadata.related[] → references links\n *\n * Frontmatter-scope extractor — the orchestrator passes an empty body.\n * No trigger normalization on these links: the source is structured\n * path strings, not a user-typed invocation. `originalTrigger` and\n * `normalizedTrigger` stay null.\n */\n\nimport type { IExtractor, IExtractorContext } from '../../../kernel/extensions/index.js';\nimport type { Link } from '../../../kernel/types.js';\n\nconst ID = 'frontmatter';\n\nexport const frontmatterExtractor: IExtractor = {\n id: ID,\n pluginId: 'claude',\n kind: 'extractor',\n version: '1.0.0',\n description: 'Reads structured references from the frontmatter (supersedes, supersededBy, requires, related).',\n stability: 'stable',\n mode: 'deterministic',\n emitsLinkKinds: ['supersedes', 'references'],\n defaultConfidence: 'high',\n scope: 'frontmatter',\n\n extract(ctx: IExtractorContext): void {\n const meta = pickMetadata(ctx.frontmatter);\n if (!meta) return;\n\n const sourcePath = ctx.node.path;\n\n for (const target of stringArray(meta['supersedes'])) {\n ctx.emitLink(link(sourcePath, target, 'supersedes'));\n }\n const supersededBy = meta['supersededBy'];\n if (typeof supersededBy === 'string' && supersededBy.length > 0) {\n // Inverse direction: the path listed in supersededBy is the new node,\n // and it supersedes `sourcePath`. Emit the edge FROM the new node\n // so consumers can ask \"what did X supersede?\" with a single query.\n ctx.emitLink(link(supersededBy, sourcePath, 'supersedes'));\n }\n for (const target of stringArray(meta['requires'])) {\n ctx.emitLink(link(sourcePath, target, 'references'));\n }\n for (const target of stringArray(meta['related'])) {\n ctx.emitLink(link(sourcePath, target, 'references'));\n }\n },\n};\n\nfunction pickMetadata(frontmatter: Record<string, unknown>): Record<string, unknown> | null {\n const meta = frontmatter['metadata'];\n if (meta && typeof meta === 'object' && !Array.isArray(meta)) {\n return meta as Record<string, unknown>;\n }\n return null;\n}\n\nfunction stringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.filter((v): v is string => typeof v === 'string' && v.length > 0);\n}\n\nfunction link(source: string, target: string, kind: 'supersedes' | 'references'): Link {\n return {\n source,\n target,\n kind,\n confidence: 'high',\n sources: [ID],\n };\n}\n","/**\n * Normative trigger normalisation pipeline. Shared utility used by every\n * extractor that emits invocation-style links (slash, at-directive,\n * command-name) and by the `trigger-collision` rule that keys on the\n * result.\n *\n * Defined by `spec/architecture.md` §Extractor · trigger normalization:\n *\n * 1. Unicode NFD (canonical decomposition).\n * 2. Strip diacritics (every code point in category Mn / Nonspacing_Mark).\n * 3. Locale-independent lowercase.\n * 4. Separator unification: hyphen, underscore, and any whitespace → ASCII space.\n * 5. Collapse runs of whitespace to a single space.\n * 6. Trim.\n *\n * Non-separator, non-alphanumeric characters (e.g. /, @, :, .) are\n * PRESERVED — stripping them is the extractor's concern, not the\n * normalizer's. This keeps `/ns:verb` and `@scope/foo` comparable in\n * their intended form.\n */\n\nexport function normalizeTrigger(source: string): string {\n // Step 1: NFD.\n let out = source.normalize('NFD');\n // Step 2: strip Mn (diacritics). \\p{Mn} with the `u` flag matches\n // Nonspacing_Mark across the full Unicode range.\n out = out.replace(/\\p{Mn}+/gu, '');\n // Step 3: locale-independent lowercase. String#toLowerCase in JS is\n // already locale-independent by default (unlike toLocaleLowerCase).\n out = out.toLowerCase();\n // Step 4: separator unification. Hyphen, underscore, or any whitespace\n // character becomes a single ASCII space. We use \\s (any whitespace),\n // which covers tab, newline, NBSP, and other Unicode spaces.\n out = out.replace(/[-_\\s]+/g, ' ');\n // Step 5: runs of ≥ 2 spaces collapse to one. Step 4 already collapsed\n // runs of whitespace-equivalents; this handles stray multi-space input\n // that somehow survived (e.g. a unicode space followed by an ASCII one\n // that would each map independently).\n out = out.replace(/ +/g, ' ');\n // Step 6: trim.\n return out.trim();\n}\n","/**\n * Slash extractor. Scans the node body for `/<command>` tokens and emits\n * one `invokes` link per distinct invocation. Deduplicates by trigger so\n * a body mentioning `/deploy` three times produces a single link.\n *\n * Matching rules:\n *\n * - Token must start with a standalone `/` (start-of-line or\n * non-word char before) so file paths like `src/cli` don't match.\n * - Command identifier is one or more of `[a-z0-9_-]`, optionally\n * followed by a namespace separator `:` + another identifier\n * (matches Claude Code plugin namespace convention, e.g.\n * `/skill-map:explore`).\n * - Case-insensitive match; the original text is preserved verbatim\n * in `originalTrigger`.\n *\n * Target resolution is left to the rules layer: the extractor emits\n * `target: <command>` as a bare name, and `broken-ref` marks it invalid\n * if no node in the scan advertises that trigger.\n */\n\nimport type { IExtractor, IExtractorContext } from '../../../kernel/extensions/index.js';\nimport { normalizeTrigger } from '../../../kernel/trigger-normalize.js';\n\nconst ID = 'slash';\n\n// Allow `/` at start of body, after whitespace, or after any non-word char\n// other than `/` itself (so `//` doesn't match, and filenames like `foo/bar`\n// don't trigger on `/bar`).\nconst SLASH_RE = /(?:^|[^A-Za-z0-9_/])(\\/[a-z0-9][a-z0-9_-]*(?::[a-z0-9][a-z0-9_-]*)?)/gi;\n\nexport const slashExtractor: IExtractor = {\n id: ID,\n pluginId: 'claude',\n kind: 'extractor',\n version: '1.0.0',\n description: 'Detects /command invocation tokens in the node body.',\n stability: 'stable',\n mode: 'deterministic',\n emitsLinkKinds: ['invokes'],\n defaultConfidence: 'medium',\n scope: 'body',\n\n extract(ctx: IExtractorContext): void {\n const seen = new Set<string>();\n\n for (const match of ctx.body.matchAll(SLASH_RE)) {\n const original = match[1]!;\n const normalized = normalizeTrigger(original);\n if (seen.has(normalized)) continue;\n seen.add(normalized);\n ctx.emitLink({\n source: ctx.node.path,\n target: original,\n kind: 'invokes',\n confidence: 'medium',\n sources: [ID],\n trigger: {\n originalTrigger: original,\n normalizedTrigger: normalized,\n },\n });\n }\n },\n};\n","/**\n * At-directive extractor. Scans the node body for `@<agent>` tokens and\n * emits one `mentions` link per distinct handle. Deduplicates by\n * normalized trigger.\n *\n * Matching rules are a close mirror of the slash extractor's:\n *\n * - Token must start with a standalone `@` (SOL or non-word prefix) so\n * emails (`foo@bar.com`) and `@@` don't match.\n * - Handle is one or more of `[a-z0-9_-]`, optionally followed by a\n * namespace segment `/<id>` or `:<id>` — matches both GitHub-style\n * (`@my-plugin/foo-extractor`) and Claude-style (`@skill-map:explore`)\n * handles.\n */\n\nimport type { IExtractor, IExtractorContext } from '../../../kernel/extensions/index.js';\nimport { normalizeTrigger } from '../../../kernel/trigger-normalize.js';\n\nconst ID = 'at-directive';\n\nconst AT_RE = /(?:^|[^A-Za-z0-9_@])(@[a-z0-9][a-z0-9_-]*(?:[/:][a-z0-9][a-z0-9_-]*)?)/gi;\n\nexport const atDirectiveExtractor: IExtractor = {\n id: ID,\n pluginId: 'claude',\n kind: 'extractor',\n version: '1.0.0',\n description: 'Detects @agent-name mentions in the node body.',\n stability: 'stable',\n mode: 'deterministic',\n emitsLinkKinds: ['mentions'],\n defaultConfidence: 'medium',\n scope: 'body',\n\n extract(ctx: IExtractorContext): void {\n const seen = new Set<string>();\n\n for (const match of ctx.body.matchAll(AT_RE)) {\n const original = match[1]!;\n const normalized = normalizeTrigger(original);\n if (seen.has(normalized)) continue;\n seen.add(normalized);\n ctx.emitLink({\n source: ctx.node.path,\n target: original,\n kind: 'mentions',\n confidence: 'medium',\n sources: [ID],\n trigger: {\n originalTrigger: original,\n normalizedTrigger: normalized,\n },\n });\n }\n },\n};\n","/**\n * External URL counter extractor. Scans the node body for `http://` and\n * `https://` URLs and emits one \"pseudo-link\" per distinct normalized URL.\n *\n * The pseudo-links are the on-the-wire transport for a count: the\n * orchestrator partitions them out of `result.links` (any link whose\n * target starts with `http://` or `https://`), increments\n * `node.externalRefsCount` per source, then DROPS them. They are never\n * persisted to `scan_links` and never reach the rules layer.\n *\n * Design constraint: the spec's `link.kind` enum is locked to\n * `invokes / references / mentions / supersedes`. We reuse `references`\n * (closest semantic match — a URL IS a reference, just to something\n * outside the graph) at low confidence to avoid bumping the spec for a\n * counter that the orchestrator strips before serialising.\n *\n * URL normalization rules (cheap, deterministic):\n * 1. `new URL(raw)` — bad URLs are silently dropped.\n * 2. Lowercase the host (RFC 3986 case-insensitive).\n * 3. Drop the fragment (`#a` and `#b` count as the same external ref).\n * 4. Preserve scheme, port, path, query verbatim.\n * 5. Dedup key is the resulting `url.href`.\n *\n * Per-node dedup: the first occurrence of a normalized URL wins; later\n * duplicates within the same body are skipped.\n *\n * The trigger-normalize util in `kernel/trigger-normalize.ts` is for\n * human-typed slash / at-directive triggers, NOT URLs — it would mangle\n * paths and queries. We roll our own URL normalization here.\n */\n\nimport type { IExtractor, IExtractorContext } from '../../../kernel/extensions/index.js';\nimport type { Link } from '../../../kernel/types.js';\n\nconst ID = 'external-url-counter';\n\n// Greedy match of http(s) URLs. Stops at whitespace and the markdown\n// delimiters that commonly wrap URLs: `<`, `>`, `\"`, `'`, backtick,\n// `)`, `]`. The trailing-punctuation pass below trims sentence enders\n// like `.`, `,`, `;`, `:`, `!`, `?` that the regex still picks up.\nconst URL_RE = /https?:\\/\\/[^\\s<>\"'`)\\]]+/g;\n\nconst TRAILING_PUNCT = /[.,;:!?]+$/;\n\nexport const externalUrlCounterExtractor: IExtractor = {\n id: ID,\n pluginId: 'core',\n kind: 'extractor',\n version: '1.0.0',\n description:\n 'Counts distinct external http(s) URLs in the node body. Emits pseudo-links the orchestrator strips after counting.',\n stability: 'stable',\n mode: 'deterministic',\n emitsLinkKinds: ['references'],\n defaultConfidence: 'low',\n scope: 'body',\n\n extract(ctx: IExtractorContext): void {\n const seen = new Set<string>();\n const lineStarts = computeLineStarts(ctx.body);\n\n for (const match of ctx.body.matchAll(URL_RE)) {\n const original = stripTrailingPunctuation(match[0]);\n if (original.length === 0) continue;\n\n const normalized = normalizeUrl(original);\n if (normalized === null) continue;\n if (seen.has(normalized)) continue;\n seen.add(normalized);\n\n const offset = match.index ?? 0;\n const link: Link = {\n source: ctx.node.path,\n target: normalized,\n kind: 'references',\n confidence: 'low',\n sources: [ID],\n trigger: {\n originalTrigger: original,\n normalizedTrigger: normalized,\n },\n location: { line: lineFor(lineStarts, offset) },\n };\n ctx.emitLink(link);\n }\n },\n};\n\nfunction stripTrailingPunctuation(raw: string): string {\n return raw.replace(TRAILING_PUNCT, '');\n}\n\nfunction normalizeUrl(raw: string): string | null {\n try {\n const url = new URL(raw);\n // URL already lowercases host on parse, but be explicit so future\n // refactors don't regress.\n url.hostname = url.hostname.toLowerCase();\n url.hash = '';\n return url.href;\n } catch {\n return null;\n }\n}\n\nfunction computeLineStarts(body: string): number[] {\n const starts = [0];\n for (let i = 0; i < body.length; i += 1) {\n if (body.charCodeAt(i) === 10 /* \\n */) starts.push(i + 1);\n }\n return starts;\n}\n\nfunction lineFor(lineStarts: number[], offset: number): number {\n // Binary search: find the largest start <= offset, return its 1-indexed line.\n let lo = 0;\n let hi = lineStarts.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >>> 1;\n if (lineStarts[mid]! <= offset) lo = mid;\n else hi = mid - 1;\n }\n return lo + 1;\n}\n","/**\n * User-facing strings emitted by the `trigger-collision` built-in rule\n * (`built-in-plugins/rules/trigger-collision/index.ts`).\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const TRIGGER_COLLISION_TEXTS = {\n /**\n * Top-level message when `analyzeTriggerBucket` accumulated exactly one\n * cause part. Used for the advertiser-ambiguous-only, invocation-\n * ambiguous-only, and cross-kind-only branches.\n */\n messageOnePart: 'Trigger \"{{normalized}}\" has {{part}}.',\n\n /**\n * Top-level message when `analyzeTriggerBucket` accumulated two cause\n * parts (advertiser-ambiguous AND invocation-ambiguous fire together).\n * The joiner lives inside the template so future locales can adapt it\n * (e.g. `'; y '` in Spanish) without touching the rule code.\n */\n messageTwoParts: 'Trigger \"{{normalized}}\" has {{first}}; and {{second}}.',\n\n /** `<n> nodes advertise it: <list>` part — fires on the advertiser-ambiguous branch. */\n partAdvertisers: '{{count}} nodes advertise it: {{paths}}',\n\n /** `<n> distinct invocation forms: <list>` part — fires on the invocation-ambiguous branch. */\n partInvocations: '{{count}} distinct invocation forms: {{forms}}',\n\n /** Singular cross-kind cause: `non-canonical invocation <form> against advertiser <path>`. */\n partNonCanonicalSingular: 'non-canonical invocation {{forms}} against advertiser {{advertiser}}',\n\n /** Plural cross-kind cause: `non-canonical invocations <forms> against advertiser <path>`. */\n partNonCanonicalPlural: 'non-canonical invocations {{forms}} against advertiser {{advertiser}}',\n} as const;\n","/**\n * `trigger-collision` rule. Flags ambiguous trigger ownership. Two\n * independent kinds of ambiguity contribute claims to the same trigger\n * bucket:\n *\n * 1. **Advertisement claims** — every node with `kind in {command,\n * skill, agent}` and a `frontmatter.name` advertises the trigger\n * `'/' + normalizeTrigger(name)`. The claim token is `node.path`\n * (so two advertisers of `deploy` produce two distinct tokens).\n * Canonical example: two commands both declaring `name: deploy`\n * from different plugins compete for `/deploy`.\n * 2. **Invocation claims** — every detected link with a\n * `trigger.normalizedTrigger` claims that trigger. The claim token\n * is `link.target` (the raw trigger string), so five sources\n * invoking `/deploy` collapse to a single token, while `/Deploy`\n * and `/deploy` from two different sources stay distinct (the\n * case-mismatch ambiguity).\n *\n * The rule fires (one `error` issue per trigger) under any of:\n * - `≥ 2` distinct advertiser paths, OR\n * - `≥ 2` distinct invocation targets, OR\n * - exactly one advertiser plus at least one non-canonical invocation\n * (raw target does not match the advertiser's literal canonical form\n * `'/' + frontmatter.name`). `/Deploy` against advertiser `deploy`\n * is non-canonical; `/foblex-flow` against `foblex-flow` is\n * canonical (separator unification is a normalizer concern, not a\n * user-facing ambiguity).\n *\n * The \"one advertiser + canonical invocation\" case (`name: deploy`\n * advertised, `/deploy` invoked) is the normal flow and stays silent.\n * Severity is `error` — the rule can't pick which claim is \"right\";\n * the user has to rename one or the other.\n */\n\nimport type { IRule, IRuleContext } from '../../../kernel/extensions/index.js';\nimport { normalizeTrigger } from '../../../kernel/trigger-normalize.js';\nimport type { Issue } from '../../../kernel/types.js';\nimport { tx } from '../../../kernel/util/tx.js';\nimport { TRIGGER_COLLISION_TEXTS } from '../../i18n/trigger-collision.texts.js';\n\nconst ID = 'trigger-collision';\n\n// Kinds whose nodes \"advertise\" a trigger (slash command name, skill\n// trigger, etc.). The set is keyed by string because `node.kind` is an\n// open string — external Providers may declare additional advertising\n// kinds in the future, and the rule applies if and only if the kind\n// is in this set. Built-in Claude catalog covers the three values today.\nconst ADVERTISING_KINDS: ReadonlySet<string> = new Set<string>([\n 'command',\n 'skill',\n 'agent',\n]);\n\ninterface IInvocationClaim {\n kind: 'invocation';\n /** Raw `link.target` — the unnormalized trigger string the source emitted. */\n token: string;\n /** Path of the source node that issued the invocation. */\n nodeId: string;\n}\n\ninterface IAdvertiserClaim {\n kind: 'advertiser';\n /** `node.path` — guarantees two advertisers of the same name produce distinct tokens. */\n token: string;\n nodeId: string;\n /**\n * Canonical literal form of the advertised trigger: `'/' + frontmatter.name`.\n * Used to decide whether an invocation in the same bucket is \"canonical\"\n * for this advertiser (literal match) vs \"non-canonical\" (e.g. `/Deploy`\n * vs advertiser `deploy`, which is a real case-mismatch ambiguity).\n * Note: this is the LITERAL form, not the normalized one — an\n * advertiser of `foblex-flow` is canonically `/foblex-flow`, even\n * though normalization yields `/foblex flow`.\n */\n canonicalForm: string;\n}\n\ntype IClaim = IInvocationClaim | IAdvertiserClaim;\n\nexport const triggerCollisionRule: IRule = {\n id: ID,\n pluginId: 'core',\n kind: 'rule',\n mode: 'deterministic',\n version: '1.0.0',\n description:\n 'Flags trigger names (/command, @agent) claimed by multiple distinct nodes — by advertisement (frontmatter.name) or by invocation.',\n stability: 'stable',\n\n // Two claim-collection passes (advertisement + invocation) feeding\n // the bucket map. Per-bucket analysis lives in `analyzeTriggerBucket`.\n // eslint-disable-next-line complexity\n evaluate(ctx: IRuleContext): Issue[] {\n // Bucket claims by normalized trigger.\n const buckets = new Map<string, IClaim[]>();\n const push = (key: string, claim: IClaim): void => {\n const bucket = buckets.get(key) ?? [];\n bucket.push(claim);\n buckets.set(key, bucket);\n };\n\n // 1. Advertisement claims. Only nodes whose kind can advertise a\n // trigger contribute (a `note` happening to carry `frontmatter.name`\n // isn't competing for a slash command). The advertised trigger is\n // `/<normalized name>`.\n for (const node of ctx.nodes) {\n if (!ADVERTISING_KINDS.has(node.kind)) continue;\n const raw = node.frontmatter?.['name'];\n if (typeof raw !== 'string' || raw.length === 0) continue;\n const normalized = `/${normalizeTrigger(raw)}`;\n // Empty after normalization (e.g. `name: \" \"`): ignore — it can't\n // be invoked anyway.\n if (normalized === '/') continue;\n push(normalized, {\n kind: 'advertiser',\n token: node.path,\n nodeId: node.path,\n canonicalForm: `/${raw}`,\n });\n }\n\n // 2. Invocation claims. Only links carrying a normalized trigger\n // contribute. Using `link.target` as the token preserves the\n // historical \"same target = no collision\" behaviour: five sources\n // invoking `/deploy` collapse to a single token.\n for (const link of ctx.links) {\n const normalized = link.trigger?.normalizedTrigger;\n if (!normalized) continue;\n push(normalized, {\n kind: 'invocation',\n token: link.target,\n nodeId: link.source,\n });\n }\n\n const issues: Issue[] = [];\n for (const [normalized, claims] of buckets) {\n const issue = analyzeTriggerBucket(normalized, claims);\n if (issue) issues.push(issue);\n }\n return issues;\n },\n};\n\n/**\n * Analyze one bucket of trigger claims and decide whether to emit an\n * `error` issue. Three independent fire conditions:\n *\n * 1. ≥ 2 distinct advertisers (two nodes both `name: deploy`) — real\n * ambiguity even without any invocations.\n * 2. ≥ 2 distinct invocation forms (`/Deploy` + `/deploy` from\n * different sources) — historical case-mismatch ambiguity.\n * 3. Exactly 1 advertiser + ≥ 1 non-canonical invocation. An\n * invocation is \"canonical\" if its raw target equals the\n * advertiser's literal canonical form (`/<frontmatter.name>`).\n * `/foblex-flow` against `foblex-flow` IS canonical (separator\n * unification is a normalizer concern, not user-facing ambiguity).\n *\n * Otherwise (1 advertiser + only canonical invocations, or repeated\n * invocations of the same target) we stay silent: same logical claim.\n */\n// eslint-disable-next-line complexity\nfunction analyzeTriggerBucket(normalized: string, claims: IClaim[]): Issue | null {\n const advertiserPaths = [\n ...new Set(claims.filter((c) => c.kind === 'advertiser').map((c) => c.token)),\n ].sort();\n const invocationTargets = [\n ...new Set(claims.filter((c) => c.kind === 'invocation').map((c) => c.token)),\n ].sort();\n const advertisers = claims.filter(\n (c): c is IAdvertiserClaim => c.kind === 'advertiser',\n );\n\n const advertiserAmbiguous = advertiserPaths.length >= 2;\n const invocationAmbiguous = invocationTargets.length >= 2;\n const canonicalForms = new Set(advertisers.map((a) => a.canonicalForm));\n const nonCanonicalInvocations = invocationTargets.filter((t) => !canonicalForms.has(t));\n const crossKindAmbiguous =\n advertiserPaths.length === 1 && nonCanonicalInvocations.length >= 1;\n\n if (!advertiserAmbiguous && !invocationAmbiguous && !crossKindAmbiguous) {\n return null;\n }\n\n const nodeIds = [...new Set(claims.map((c) => c.nodeId))].sort();\n const parts: string[] = [];\n if (advertiserAmbiguous) {\n parts.push(\n tx(TRIGGER_COLLISION_TEXTS.partAdvertisers, {\n count: advertiserPaths.length,\n paths: advertiserPaths.join(', '),\n }),\n );\n }\n if (invocationAmbiguous) {\n parts.push(\n tx(TRIGGER_COLLISION_TEXTS.partInvocations, {\n count: invocationTargets.length,\n forms: invocationTargets.join(', '),\n }),\n );\n } else if (crossKindAmbiguous) {\n const template =\n nonCanonicalInvocations.length > 1\n ? TRIGGER_COLLISION_TEXTS.partNonCanonicalPlural\n : TRIGGER_COLLISION_TEXTS.partNonCanonicalSingular;\n parts.push(\n tx(template, {\n forms: nonCanonicalInvocations.join(', '),\n advertiser: advertiserPaths[0]!,\n }),\n );\n }\n\n // `parts.length` ∈ {1, 2}: advertiserAmbiguous and crossKindAmbiguous\n // are mutually exclusive (the latter requires advertiserPaths.length===1),\n // so the two-part path is exactly advertiserAmbiguous + invocationAmbiguous.\n const message =\n parts.length === 2\n ? tx(TRIGGER_COLLISION_TEXTS.messageTwoParts, {\n normalized,\n first: parts[0]!,\n second: parts[1]!,\n })\n : tx(TRIGGER_COLLISION_TEXTS.messageOnePart, {\n normalized,\n part: parts[0]!,\n });\n\n return {\n ruleId: ID,\n severity: 'error',\n nodeIds,\n message,\n data: {\n normalizedTrigger: normalized,\n invocationTargets,\n advertiserPaths,\n },\n };\n}\n","/**\n * User-facing strings emitted by the `broken-ref` built-in rule\n * (`built-in-plugins/rules/broken-ref/index.ts`). Issue messages land\n * in `scan_issues.message` and surface through `sm check` / `sm show` /\n * `sm export`, so the same i18n discipline as the CLI catalogs applies.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const BROKEN_REF_TEXTS = {\n /** `Broken <kind> reference from <source> → <target>` */\n message: 'Broken {{kind}} reference from {{source}} → {{target}}',\n} as const;\n","/**\n * `broken-ref` rule. Emits a `warn` issue for every link whose target\n * cannot be resolved to a node in the current scan:\n *\n * - Path-style targets (frontmatter extractor's output): target must\n * match some `node.path` verbatim.\n * - Trigger-style targets (slash / at-directive extractors): resolution\n * matches against `node.frontmatter.name` with the same normalization\n * the extractor applied. An extractor's `/foo` link resolves to a node\n * whose `metadata.name` normalizes to `foo`.\n *\n * Rule is advisory — broken refs aren't errors; authors commonly\n * reference external or not-yet-scanned artifacts. Severity stays at\n * `warn`.\n */\n\nimport type { IRule, IRuleContext } from '../../../kernel/extensions/index.js';\nimport type { Issue, Link, Node } from '../../../kernel/types.js';\nimport { normalizeTrigger } from '../../../kernel/trigger-normalize.js';\nimport { tx } from '../../../kernel/util/tx.js';\nimport { BROKEN_REF_TEXTS } from '../../i18n/broken-ref.texts.js';\n\nconst ID = 'broken-ref';\n\nexport const brokenRefRule: IRule = {\n id: ID,\n pluginId: 'core',\n kind: 'rule',\n version: '1.0.0',\n description: 'Flags links whose target cannot be resolved to a scanned node.',\n stability: 'stable',\n mode: 'deterministic',\n\n evaluate(ctx: IRuleContext): Issue[] {\n const byPath = new Set(ctx.nodes.map((n) => n.path));\n const byNormalizedName = indexByNormalizedName(ctx.nodes);\n\n const issues: Issue[] = [];\n for (const link of ctx.links) {\n if (isResolved(link, byPath, byNormalizedName)) continue;\n issues.push({\n ruleId: ID,\n severity: 'warn',\n nodeIds: [link.source],\n message: tx(BROKEN_REF_TEXTS.message, {\n kind: link.kind,\n source: link.source,\n target: link.target,\n }),\n data: {\n target: link.target,\n kind: link.kind,\n trigger: link.trigger?.normalizedTrigger ?? null,\n },\n });\n }\n return issues;\n },\n};\n\nfunction indexByNormalizedName(nodes: Node[]): Map<string, Node[]> {\n const out = new Map<string, Node[]>();\n for (const node of nodes) {\n const raw = node.frontmatter?.['name'];\n const name = typeof raw === 'string' ? raw : '';\n if (!name) continue;\n const key = normalizeTrigger(name);\n const bucket = out.get(key) ?? [];\n bucket.push(node);\n out.set(key, bucket);\n }\n return out;\n}\n\nfunction isResolved(\n link: Link,\n byPath: Set<string>,\n byNormalizedName: Map<string, Node[]>,\n): boolean {\n // Trigger-style: compare against normalized name index. An extractor may\n // have emitted `/deploy` or `@agent-name`; strip the leading sigil\n // before normalising for the name lookup.\n const normalized = link.trigger?.normalizedTrigger;\n if (normalized) {\n const withoutSigil = normalized.replace(/^[/@]/, '').trim();\n if (byNormalizedName.has(withoutSigil)) return true;\n }\n\n // Path-style (frontmatter-derived links) or fallback: verbatim path\n // must exist in the scan.\n if (byPath.has(link.target)) return true;\n\n return false;\n}\n","/**\n * User-facing strings emitted by the `superseded` built-in rule\n * (`built-in-plugins/rules/superseded/index.ts`).\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const SUPERSEDED_TEXTS = {\n /** `<path> is superseded by <supersededBy>` */\n message: '{{path}} is superseded by {{supersededBy}}',\n} as const;\n","/**\n * `superseded` rule. Emits an `info` issue for every node whose\n * frontmatter carries `metadata.supersededBy` — the author has declared\n * the node obsolete, so the rule just surfaces that declaration as a\n * graph-level finding.\n *\n * Does not inspect `metadata.stability: deprecated` on its own; a\n * deprecated node without a supersededBy is a different conversation\n * (the user wants to know *what replaces it*). That surface can land as\n * a separate rule once the use case materialises.\n */\n\nimport type { IRule, IRuleContext } from '../../../kernel/extensions/index.js';\nimport type { Issue } from '../../../kernel/types.js';\nimport { tx } from '../../../kernel/util/tx.js';\nimport { SUPERSEDED_TEXTS } from '../../i18n/superseded.texts.js';\n\nconst ID = 'superseded';\n\nexport const supersededRule: IRule = {\n id: ID,\n pluginId: 'core',\n kind: 'rule',\n version: '1.0.0',\n description: 'Surfaces nodes that declare a supersededBy replacement in their frontmatter.',\n stability: 'stable',\n mode: 'deterministic',\n\n evaluate(ctx: IRuleContext): Issue[] {\n const issues: Issue[] = [];\n for (const node of ctx.nodes) {\n const meta = node.frontmatter?.['metadata'];\n if (!meta || typeof meta !== 'object' || Array.isArray(meta)) continue;\n const supersededBy = (meta as Record<string, unknown>)['supersededBy'];\n if (typeof supersededBy !== 'string' || supersededBy.length === 0) continue;\n\n issues.push({\n ruleId: ID,\n severity: 'info',\n nodeIds: [node.path],\n message: tx(SUPERSEDED_TEXTS.message, {\n path: node.path,\n supersededBy,\n }),\n data: { supersededBy },\n });\n }\n return issues;\n },\n};\n","/**\n * User-facing strings emitted by the `link-conflict` built-in rule\n * (`built-in-plugins/rules/link-conflict/index.ts`).\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const LINK_CONFLICT_TEXTS = {\n /** `Detectors disagree on link kind for <source> → <target> (<kindList>)` */\n message: 'Detectors disagree on link kind for {{source}} → {{target}} ({{kindList}})',\n} as const;\n","/**\n * `link-conflict` rule. Surfaces detector disagreement.\n *\n * Per Decision #90 (`ROADMAP.md` §Step 12 review pass), two detectors\n * that emit a link for the same `(source, target)` pair coexist as\n * separate rows in `scan_links`. No merge, no dedup. That keeps the\n * raw graph honest, but it leaves consumers without a signal when\n * detectors actually **disagree** on what the link means.\n *\n * Concrete example. A skill `audit-flow.md` declares\n *\n * metadata:\n * related: [security-scanner]\n *\n * AND its body says `Apoyate en /security-scanner para el paso 3.`\n *\n * - `frontmatter` detector emits (audit-flow → security-scanner, kind=references)\n * - `slash` detector emits (audit-flow → security-scanner, kind=invokes)\n *\n * Same pair, different kinds. The author is sending two different\n * signals: \"see-also navigation\" (frontmatter) vs \"I invoke this\"\n * (body). The user usually wants to pick one — promote `related[]` to\n * `requires[]`, or remove the slash from the body.\n *\n * Rule contract:\n *\n * - Group links by `(source, target)` strings.\n * - For each group, collect the distinct `kind` values across all\n * emitted links. If size ≥ 2 → emit one `warn` issue.\n * - Agreement (single kind across all detectors) is silent — that's\n * the happy path. We don't emit `info` findings for cross-detector\n * confirmation: it would generate massive noise on real graphs.\n *\n * Severity is `warn`, not `error` — the rule cannot tell which kind is\n * correct. The user has to decide. Exit-code propagation lives in the\n * CLI: warns do not fail the verb (per `spec/cli-contract.md` §Exit\n * codes — only `error`-severity issues flip exit 1).\n */\n\nimport type { IRule, IRuleContext } from '../../../kernel/extensions/index.js';\nimport type { Confidence, Issue, Link, LinkKind } from '../../../kernel/types.js';\nimport { tx } from '../../../kernel/util/tx.js';\nimport { LINK_CONFLICT_TEXTS } from '../../i18n/link-conflict.texts.js';\n\nconst ID = 'link-conflict';\n\ninterface ILinkVariant {\n kind: LinkKind;\n sources: string[];\n confidence: Confidence;\n}\n\nexport const linkConflictRule: IRule = {\n id: ID,\n pluginId: 'core',\n kind: 'rule',\n version: '1.0.0',\n description: 'Flags (source, target) pairs where detectors disagree on the link kind.',\n stability: 'stable',\n mode: 'deterministic',\n\n // Bucket links by (source, target), then per-bucket detect distinct\n // kinds. The branching is intrinsic to the per-bucket conflict\n // detection.\n // eslint-disable-next-line complexity\n evaluate(ctx: IRuleContext): Issue[] {\n // Group links by `${source}\\0${target}`. Using a NUL separator is\n // safe for the path format we ship (POSIX, no NULs by spec).\n const groups = new Map<string, Link[]>();\n for (const link of ctx.links) {\n const key = `${link.source}\u0000${link.target}`;\n const bucket = groups.get(key);\n if (bucket) bucket.push(link);\n else groups.set(key, [link]);\n }\n\n const issues: Issue[] = [];\n for (const [key, links] of groups) {\n // No conflict possible with a single emitted link.\n if (links.length < 2) continue;\n\n // Count distinct kinds. Same-kind groups are silent — they're\n // either repeated emission from one detector (slash sees a\n // trigger twice) or genuine cross-detector agreement, which is\n // the happy path.\n const kinds = new Set(links.map((l) => l.kind));\n if (kinds.size < 2) continue;\n\n // Build the per-kind variant rollup so consumers know who\n // contributed what. Multiple links of the same kind from\n // multiple detectors collapse into one variant; sources from\n // each row are unioned, deduped, and sorted (deterministic).\n const variantByKind = new Map<LinkKind, ILinkVariant>();\n for (const link of links) {\n const existing = variantByKind.get(link.kind);\n if (existing) {\n for (const src of link.sources) {\n if (!existing.sources.includes(src)) existing.sources.push(src);\n }\n // Keep the highest-confidence value across rows of the\n // same kind. Order: high > medium > low.\n if (rankConfidence(link.confidence) > rankConfidence(existing.confidence)) {\n existing.confidence = link.confidence;\n }\n } else {\n variantByKind.set(link.kind, {\n kind: link.kind,\n sources: [...link.sources],\n confidence: link.confidence,\n });\n }\n }\n for (const v of variantByKind.values()) v.sources.sort();\n const variants = [...variantByKind.values()].sort((a, b) =>\n a.kind.localeCompare(b.kind),\n );\n\n const [source, target] = key.split('\u0000') as [string, string];\n const kindList = variants.map((v) => v.kind).join(' / ');\n issues.push({\n ruleId: ID,\n severity: 'warn',\n nodeIds: [source, target],\n message: tx(LINK_CONFLICT_TEXTS.message, {\n source,\n target,\n kindList,\n }),\n data: { source, target, variants },\n });\n }\n return issues;\n },\n};\n\nfunction rankConfidence(c: Confidence): number {\n switch (c) {\n case 'high':\n return 2;\n case 'medium':\n return 1;\n case 'low':\n return 0;\n }\n}\n","/**\n * User-facing strings emitted by the `ascii` built-in formatter\n * (`built-in-plugins/formatters/ascii/index.ts`). Produces the\n * `sm graph --format ascii` output.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const ASCII_FORMATTER_TEXTS = {\n /** Header line: `skill-map graph — N nodes, M links, K issues`. */\n header: 'skill-map graph — {{nodes}} nodes, {{links}} links, {{issues}} issues',\n\n /** Per-node-kind section header: `## <kind> (<count>)`. */\n kindSectionHeader: '## {{kind}} ({{count}})',\n\n /** Plain node bullet: `- <path>`. */\n nodeBullet: '- {{path}}',\n\n /** Node bullet with title suffix: `- <path> — \"<title>\"`. */\n nodeBulletWithTitle: '- {{path}} — \"{{title}}\"',\n\n /** `## links (<count>)` section header. */\n linksSectionHeader: '## links ({{count}})',\n\n /** Link bullet: `- <source> --<kind>--> <target> [<confidence>]`. */\n linkBullet: '- {{source}} --{{kind}}--> {{target}} [{{confidence}}]',\n\n /** `## issues (<count>)` section header. */\n issuesSectionHeader: '## issues ({{count}})',\n\n /** Issue bullet: `- [<severity>] <ruleId>: <message>`. */\n issueBullet: '- [{{severity}}] {{ruleId}}: {{message}}',\n} as const;\n","/**\n * `ascii` formatter. Produces a plain-text dump of the graph for\n * `sm graph --format ascii`. Purposely minimal — a human reads it to\n * grok the shape of a scan, not to study layout. Fancier formatters\n * (mermaid, dot) land as drop-in additions in later steps.\n *\n * Output layout:\n *\n * skill-map graph — <N> nodes, <M> links, <K> issues\n *\n * ## agent (3)\n * - agents/backend-architect.md — \"Backend Architect\"\n * - agents/doc-writer.md — \"Doc Writer\"\n *\n * ## command (2)\n * - commands/deploy.md — \"Deploy\"\n *\n * ## links\n * - agents/a.md --supersedes--> agents/b.md [high]\n * - notes/n.md --references--> notes/m.md [high]\n *\n * ## issues (1)\n * - [warn] broken-ref: ...\n */\n\nimport type { IFormatter, IFormatterContext } from '../../../kernel/extensions/index.js';\nimport { sanitizeForTerminal } from '../../../kernel/util/safe-text.js';\nimport { tx } from '../../../kernel/util/tx.js';\nimport { ASCII_FORMATTER_TEXTS } from '../../i18n/ascii.texts.js';\n\nconst ID = 'ascii';\n// Built-in Claude Provider catalog rendered first, in this canonical\n// order. Anything else (`'cursorRule'`, `'daily'`, … from external\n// Providers) is rendered after, sorted alphabetically — the formatter\n// no longer assumes the closed enum and the order stays deterministic.\nconst KIND_ORDER: readonly string[] = ['agent', 'command', 'hook', 'skill', 'note'];\n\nexport const asciiFormatter: IFormatter = {\n id: ID,\n pluginId: 'core',\n kind: 'formatter',\n version: '1.0.0',\n description: 'Plain-text graph dump, grouped by node kind then links then issues.',\n stability: 'stable',\n formatId: 'ascii',\n\n // ASCII tree formatter — header + per-kind sections + per-issue\n // section. Each section iterates and renders; splitting per section\n // would multiply the for-loop boilerplate.\n // eslint-disable-next-line complexity\n format(ctx: IFormatterContext): string {\n const out: string[] = [];\n out.push(\n tx(ASCII_FORMATTER_TEXTS.header, {\n nodes: ctx.nodes.length,\n links: ctx.links.length,\n issues: ctx.issues.length,\n }),\n '',\n );\n\n // Group nodes by kind. `kind` is an open string — the formatter\n // accepts whatever an enabled Provider classified into.\n const byKind = new Map<string, typeof ctx.nodes>();\n for (const node of ctx.nodes) {\n if (!byKind.has(node.kind)) byKind.set(node.kind, []);\n byKind.get(node.kind)!.push(node);\n }\n\n // Built-in Claude catalog first in canonical order, then any extra\n // kinds an external Provider emitted, sorted alphabetically so the\n // output stays deterministic across runs.\n const renderedKinds = new Set<string>();\n for (const kind of KIND_ORDER) {\n const group = byKind.get(kind);\n if (!group || group.length === 0) continue;\n renderSection(out, kind, group);\n renderedKinds.add(kind);\n }\n const extraKinds = [...byKind.keys()]\n .filter((k) => !renderedKinds.has(k))\n .sort();\n for (const kind of extraKinds) {\n const group = byKind.get(kind);\n if (!group || group.length === 0) continue;\n renderSection(out, kind, group);\n }\n\n if (ctx.links.length > 0) {\n out.push(tx(ASCII_FORMATTER_TEXTS.linksSectionHeader, { count: ctx.links.length }));\n const sorted = [...ctx.links].sort((a, b) => {\n const aKey = `${a.source}\\0${a.kind}\\0${a.target}`;\n const bKey = `${b.source}\\0${b.kind}\\0${b.target}`;\n return aKey.localeCompare(bKey);\n });\n for (const link of sorted) {\n out.push(\n tx(ASCII_FORMATTER_TEXTS.linkBullet, {\n source: sanitizeForTerminal(link.source),\n kind: sanitizeForTerminal(link.kind),\n target: sanitizeForTerminal(link.target),\n confidence: link.confidence,\n }),\n );\n }\n out.push('');\n }\n\n if (ctx.issues.length > 0) {\n out.push(tx(ASCII_FORMATTER_TEXTS.issuesSectionHeader, { count: ctx.issues.length }));\n for (const issue of ctx.issues) {\n // Defence in depth: `ruleId` is regex-validated at registration\n // (matches `[a-z0-9-]+`) but the sibling `message` already\n // sanitizes — wrap `ruleId` for symmetry so a future loosening\n // of the registry validator can't regress this gate.\n out.push(\n tx(ASCII_FORMATTER_TEXTS.issueBullet, {\n severity: issue.severity,\n ruleId: sanitizeForTerminal(issue.ruleId),\n message: sanitizeForTerminal(issue.message),\n }),\n );\n }\n out.push('');\n }\n\n return out.join('\\n');\n },\n};\n\nfunction pickTitle(node: { title?: string | null; frontmatter?: Record<string, unknown> }): string | null {\n if (node.title) return node.title;\n const name = node.frontmatter?.['name'];\n return typeof name === 'string' ? name : null;\n}\n\nfunction renderSection(\n out: string[],\n kind: string,\n group: ReadonlyArray<{ path: string; title?: string | null; frontmatter?: Record<string, unknown> }>,\n): void {\n const sorted = [...group].sort((a, b) => a.path.localeCompare(b.path));\n out.push(\n tx(ASCII_FORMATTER_TEXTS.kindSectionHeader, {\n kind: sanitizeForTerminal(kind),\n count: sorted.length,\n }),\n );\n for (const node of sorted) {\n const title = pickTitle(node);\n out.push(\n title\n ? tx(ASCII_FORMATTER_TEXTS.nodeBulletWithTitle, {\n path: sanitizeForTerminal(node.path),\n title: sanitizeForTerminal(title),\n })\n : tx(ASCII_FORMATTER_TEXTS.nodeBullet, { path: sanitizeForTerminal(node.path) }),\n );\n }\n out.push('');\n}\n","/**\n * AJV validator loader. Compiles every JSON Schema the kernel needs into a\n * map of reusable validators keyed by a stable logical name. Schemas load\n * directly from the `@skill-map/spec` package at startup; any missing file\n * is a fatal boot error (the kernel cannot validate without them).\n *\n * Key design choices:\n *\n * - **Single Ajv instance per loader** so `$ref` resolution can reach sibling\n * schemas (e.g. `extensions/base.schema.json` → extended by every kind).\n * - **`strict: false`** because the spec uses a few keywords AJV considers\n * unknown under strict mode (`const` inside `oneOf`, tuple length hints)\n * that are nevertheless valid Draft 2020-12.\n * - **`ajv-formats`** enabled for `uri`, `date`, `date-time` — all used by\n * frontmatter base and plugin manifest.\n * - **Lazy compilation** is NOT used: every validator compiles eagerly on\n * `load()` so the kernel fails fast on a spec corruption instead of\n * crashing the first time a plugin tries to register.\n *\n * **Spec 0.8.0**. Per-kind frontmatter schemas (`skill`, `agent`,\n * `command`, `hook`, `note`) relocated from spec to the Provider that\n * owns them. Spec-only validators no longer cover those\n * five names. `buildProviderFrontmatterValidator(providers)` produces a\n * dedicated AJV instance pre-loaded with `frontmatter/base` (from spec)\n * plus every Provider's per-kind schemas — the kernel composes it once\n * per scan and the orchestrator validates each node's frontmatter\n * through it.\n */\n\nimport { readFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { createRequire } from 'node:module';\n\nimport { Ajv2020, type ValidateFunction } from 'ajv/dist/2020.js';\nimport addFormatsModule from 'ajv-formats';\n\nimport type { IProvider } from '../extensions/index.js';\nimport type { ExtensionKind } from '../registry.js';\n\n// ajv-formats ships CJS-first; the default export is the callable plugin\n// under ESM interop but TS sometimes types it as the namespace. Normalise.\nconst addFormats = (addFormatsModule as unknown as { default?: typeof addFormatsModule })\n .default ?? addFormatsModule;\n\ntype TAjv = InstanceType<typeof Ajv2020>;\n\nexport type TSchemaName =\n | 'node'\n | 'link'\n | 'issue'\n | 'scan-result'\n | 'execution-record'\n | 'project-config'\n | 'plugins-registry'\n | 'job'\n | 'report-base'\n | 'conformance-case'\n | 'history-stats'\n | 'extension-provider'\n | 'extension-extractor'\n | 'extension-rule'\n | 'extension-action'\n | 'extension-formatter'\n | 'extension-hook'\n | 'frontmatter-base';\n\n/**\n * Re-export of `ExtensionKind` (canonical declaration in `kernel/registry.ts`)\n * for callers that already depend on this module for related schema names.\n * Single source of truth keeps the extension-kind set in lock-step with\n * `EXTENSION_KINDS`.\n */\nexport type { ExtensionKind } from '../registry.js';\n\nconst SCHEMA_FILES: Record<TSchemaName, string> = {\n node: 'schemas/node.schema.json',\n link: 'schemas/link.schema.json',\n issue: 'schemas/issue.schema.json',\n 'scan-result': 'schemas/scan-result.schema.json',\n 'execution-record': 'schemas/execution-record.schema.json',\n 'project-config': 'schemas/project-config.schema.json',\n 'plugins-registry': 'schemas/plugins-registry.schema.json',\n job: 'schemas/job.schema.json',\n 'report-base': 'schemas/report-base.schema.json',\n 'conformance-case': 'schemas/conformance-case.schema.json',\n 'history-stats': 'schemas/history-stats.schema.json',\n 'extension-provider': 'schemas/extensions/provider.schema.json',\n 'extension-extractor': 'schemas/extensions/extractor.schema.json',\n 'extension-rule': 'schemas/extensions/rule.schema.json',\n 'extension-action': 'schemas/extensions/action.schema.json',\n 'extension-formatter': 'schemas/extensions/formatter.schema.json',\n 'extension-hook': 'schemas/extensions/hook.schema.json',\n 'frontmatter-base': 'schemas/frontmatter/base.schema.json',\n};\n\n/** Schemas that other schemas reference via $ref but aren't validated directly. */\nconst SUPPORTING_SCHEMAS: string[] = [\n 'schemas/extensions/base.schema.json',\n 'schemas/frontmatter/base.schema.json',\n 'schemas/summaries/security-scanner.schema.json',\n];\n\nexport interface ISchemaValidators {\n validate<T = unknown>(name: TSchemaName, data: unknown): { ok: true; data: T } | { ok: false; errors: string };\n getValidator(name: TSchemaName): ValidateFunction;\n validatorForExtension(kind: ExtensionKind): ValidateFunction;\n /**\n * Validate raw plugin.json against `$defs/PluginManifest` inside\n * plugins-registry.schema.json. Returns the typed manifest on success.\n */\n validatePluginManifest<T = unknown>(data: unknown): { ok: true; data: T } | { ok: false; errors: string };\n}\n\n// Module-level cache. Cold load compiles ~17 validators\n// (~20 schemas counting supporting refs) which is ~100 ms cold for a CLI\n// startup. Subsequent calls in the same process return the same instance,\n// so future verbs that validate at multiple boundaries pay the cost once.\n// `null` means \"not yet loaded\"; we never expose a way to invalidate\n// because the schemas are static, baked-in, and the underlying spec\n// package version doesn't change at runtime.\nlet cachedValidators: ISchemaValidators | null = null;\n\n/** Test-only escape hatch — drop the cache so a test can re-trigger load. */\nexport function _resetSchemaValidatorsCacheForTests(): void {\n cachedValidators = null;\n}\n\nexport function loadSchemaValidators(): ISchemaValidators {\n if (cachedValidators !== null) return cachedValidators;\n cachedValidators = buildSchemaValidators();\n return cachedValidators;\n}\n\nfunction buildSchemaValidators(): ISchemaValidators {\n const specRoot = resolveSpecRoot();\n const ajv: TAjv = new Ajv2020({\n strict: false,\n allErrors: true,\n allowUnionTypes: true,\n });\n (addFormats as unknown as (a: TAjv) => void)(ajv);\n\n // Add supporting schemas first so $ref targets resolve during compile.\n for (const rel of SUPPORTING_SCHEMAS) {\n const file = resolve(specRoot, rel);\n if (!existsSyncSafe(file)) continue;\n const schema = JSON.parse(readFileSync(file, 'utf8'));\n ajv.addSchema(schema);\n }\n\n const validators = new Map<TSchemaName, ValidateFunction>();\n for (const [name, rel] of Object.entries(SCHEMA_FILES) as Array<[TSchemaName, string]>) {\n const file = resolve(specRoot, rel);\n const schema = JSON.parse(readFileSync(file, 'utf8'));\n // Reuse existing compilation if the schema was already added above.\n const byId = typeof schema.$id === 'string' ? ajv.getSchema(schema.$id) : undefined;\n validators.set(name, byId ?? ajv.compile(schema));\n }\n\n const extensionByKind: Record<ExtensionKind, TSchemaName> = {\n provider: 'extension-provider',\n extractor: 'extension-extractor',\n rule: 'extension-rule',\n action: 'extension-action',\n formatter: 'extension-formatter',\n hook: 'extension-hook',\n };\n\n // Dedicated validator that targets PluginManifest inside the oneOf of\n // plugins-registry.schema.json, so callers don't have to hand-filter\n // against the combined schema.\n const pluginManifestValidator = ajv.compile({\n $ref: 'https://skill-map.dev/spec/v0/plugins-registry.schema.json#/$defs/PluginManifest',\n });\n\n return {\n getValidator(name) {\n const v = validators.get(name);\n if (!v) throw new Error(`Unknown schema: ${name}`);\n return v;\n },\n validatorForExtension(kind) {\n return validators.get(extensionByKind[kind])!;\n },\n validate<T = unknown>(name: TSchemaName, data: unknown) {\n const v = validators.get(name);\n if (!v) throw new Error(`Unknown schema: ${name}`);\n if (v(data)) return { ok: true as const, data: data as T };\n const errors = (v.errors ?? []).map(formatError).join('; ');\n return { ok: false as const, errors };\n },\n validatePluginManifest<T = unknown>(data: unknown) {\n if (pluginManifestValidator(data)) return { ok: true as const, data: data as T };\n const errors = (pluginManifestValidator.errors ?? []).map(formatError).join('; ');\n return { ok: false as const, errors };\n },\n };\n}\n\n/**\n * Validator for Provider-owned per-kind frontmatter schemas. Built from\n * the live set of registered Providers — each Provider declares its\n * `kinds[<kind>].schemaJson` and the loader compiles them into a single\n * AJV instance that also carries the spec's `frontmatter/base.schema.json`\n * so cross-package `$ref`-by-`$id` resolves. The orchestrator builds\n * one of these per scan via `buildProviderFrontmatterValidator`.\n */\nexport interface IProviderFrontmatterValidator {\n /**\n * Validate a node's frontmatter against the schema declared by\n * `provider.kinds[kind]`. `kind` is the value `provider.classify`\n * returned for the node, so the entry is guaranteed to exist for any\n * Provider implemented per spec; an absent entry returns\n * `{ ok: false, errors: 'no-schema' }` so the caller can emit a\n * directed `frontmatter-invalid` issue without crashing.\n */\n validate(\n provider: IProvider,\n kind: string,\n data: unknown,\n ): { ok: true } | { ok: false; errors: string };\n}\n\n/**\n * Build a Provider-frontmatter validator. Composes one AJV instance,\n * pre-registers `frontmatter/base.schema.json` from spec so per-kind\n * schemas can `$ref` it by `$id`, then compiles every Provider's\n * `kinds[<kind>].schemaJson` keyed by `(providerId, kind)`. Idempotent\n * across providers that share kinds (same `$id` → AJV's `addSchema`\n * dedupes silently); the keying is by `providerId` first so two\n * Providers exporting different schemas under the same kind name don't\n * collide.\n */\nexport function buildProviderFrontmatterValidator(\n providers: IProvider[],\n): IProviderFrontmatterValidator {\n const specRoot = resolveSpecRoot();\n const ajv: TAjv = new Ajv2020({\n strict: false,\n allErrors: true,\n allowUnionTypes: true,\n });\n (addFormats as unknown as (a: TAjv) => void)(ajv);\n\n // Register spec's frontmatter/base.schema.json so per-kind schemas can\n // resolve `$ref: 'https://skill-map.dev/spec/v0/frontmatter/base.schema.json'`.\n const baseFile = resolve(specRoot, 'schemas/frontmatter/base.schema.json');\n const baseSchema = JSON.parse(readFileSync(baseFile, 'utf8'));\n ajv.addSchema(baseSchema);\n\n const compiled = new Map<string, ValidateFunction>();\n for (const provider of providers) {\n for (const [kind, entry] of Object.entries(provider.kinds)) {\n const key = `${provider.id}::${kind}`;\n // Reuse a previously-compiled schema (multiple Providers may legitimately\n // share the same `$id` if they bundle a copy of another's schema).\n const json = entry.schemaJson as { $id?: string };\n const existing = typeof json.$id === 'string' ? ajv.getSchema(json.$id) : undefined;\n compiled.set(key, existing ?? ajv.compile(entry.schemaJson as object));\n }\n }\n\n return {\n validate(provider, kind, data) {\n const key = `${provider.id}::${kind}`;\n const v = compiled.get(key);\n if (!v) return { ok: false as const, errors: 'no-schema' };\n if (v(data)) return { ok: true as const };\n const errors = (v.errors ?? []).map(formatError).join('; ');\n return { ok: false as const, errors };\n },\n };\n}\n\nfunction formatError(err: { instancePath: string; message?: string; keyword: string; params?: unknown }): string {\n const path = err.instancePath || '(root)';\n return `${path} ${err.message ?? err.keyword}`;\n}\n\n/**\n * Locate the installed `@skill-map/spec` package root. Prefer Node's\n * resolver (handles npm workspaces + published installs symmetrically)\n * and fall back to the package's `package.json` directory.\n */\nfunction resolveSpecRoot(): string {\n const require = createRequire(import.meta.url);\n // @skill-map/spec's exports field doesn't expose package.json, but\n // ./index.json is always exported and always lives at the package root.\n try {\n const indexPath = require.resolve('@skill-map/spec/index.json');\n return dirname(indexPath);\n } catch {\n throw new Error(\n '@skill-map/spec not resolvable — ensure the workspace is linked or the package is installed.',\n );\n }\n}\n\nfunction existsSyncSafe(path: string): boolean {\n try {\n readFileSync(path, 'utf8');\n return true;\n } catch {\n return false;\n }\n}\n","/**\n * User-facing strings emitted by the `validate-all` built-in rule\n * (`built-in-plugins/rules/validate-all/index.ts`).\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const VALIDATE_ALL_TEXTS = {\n /** `Node <path> failed schema validation: <errors>` */\n nodeFailure: 'Node {{path}} failed schema validation: {{errors}}',\n\n /** `Link <source> → <target> failed schema validation: <errors>` */\n linkFailure: 'Link {{source}} → {{target}} failed schema validation: {{errors}}',\n} as const;\n","/**\n * `validate-all` rule. Cross-graph consistency check that runs alongside\n * the other deterministic rules. Validates the in-flight scan output back\n * through AJV against the authoritative schemas:\n *\n * - Every Node's record against `node.schema.json`. The per-kind\n * `frontmatter/<kind>.schema.json` is reached transitively via the\n * node schema's `$ref`s.\n * - Every Link against `link.schema.json` (except the id/location\n * numeric fields that only exist on the DB row).\n *\n * Failures become `Issue[]` like every other rule. The CLI / report\n * formatter wrapping is no longer this extension's concern; consumers\n * surface `validate-all`-emitted issues the same way they surface\n * `broken-ref` / `trigger-collision` / etc.\n *\n * Manifest validation for registered extensions is already enforced at\n * load time by the PluginLoader — there's no need to redo it here. This\n * rule focuses on user content that the scan produced. Cross-rule issue\n * validation (revalidating other rules' `Issue[]` output) is intentionally\n * NOT done here; rules see only the graph (`nodes` + `links`), and the\n * kernel's own `validateIssue()` already gates issues at emit time.\n */\n\nimport type { IRule, IRuleContext } from '../../../kernel/extensions/index.js';\nimport type { Issue, Link, Node } from '../../../kernel/types.js';\nimport { loadSchemaValidators, type ISchemaValidators } from '../../../kernel/adapters/schema-validators.js';\nimport { tx } from '../../../kernel/util/tx.js';\nimport { VALIDATE_ALL_TEXTS } from '../../i18n/validate-all.texts.js';\n\nconst ID = 'validate-all';\n\nexport const validateAllRule: IRule = {\n id: ID,\n pluginId: 'core',\n kind: 'rule',\n version: '1.0.0',\n description: 'Validates every scanned node / link against the authoritative @skill-map/spec schemas.',\n stability: 'stable',\n mode: 'deterministic',\n\n evaluate(ctx: IRuleContext): Issue[] {\n const validators = loadSchemaValidators();\n const findings: Issue[] = [];\n\n for (const node of ctx.nodes) {\n collectNodeFindings(validators, node, findings);\n }\n for (const link of ctx.links) {\n collectLinkFindings(validators, link, findings);\n }\n\n return findings;\n },\n};\n\nfunction collectNodeFindings(v: ISchemaValidators, node: Node, out: Issue[]): void {\n const result = v.validate('node', toNodeForSchema(node));\n if (result.ok) return;\n out.push({\n ruleId: ID,\n severity: 'error',\n nodeIds: [node.path],\n message: tx(VALIDATE_ALL_TEXTS.nodeFailure, {\n path: node.path,\n errors: result.errors,\n }),\n data: { target: 'node', path: node.path },\n });\n}\n\nfunction collectLinkFindings(v: ISchemaValidators, link: Link, out: Issue[]): void {\n const result = v.validate('link', toLinkForSchema(link));\n if (result.ok) return;\n out.push({\n ruleId: ID,\n severity: 'error',\n nodeIds: [link.source],\n message: tx(VALIDATE_ALL_TEXTS.linkFailure, {\n source: link.source,\n target: link.target,\n errors: result.errors,\n }),\n data: { target: 'link', source: link.source, to: link.target },\n });\n}\n\n// The runtime TypeScript types carry a convenience shape (e.g. bytes as\n// a triple-split object); the spec schemas use slightly different field\n// layouts. These shape transformers bridge the two without leaking the\n// DB-internal fields (id, `data_json`, etc.).\nfunction toNodeForSchema(node: Node): unknown {\n return {\n path: node.path,\n kind: node.kind,\n provider: node.provider,\n title: node.title ?? undefined,\n description: node.description ?? undefined,\n stability: node.stability ?? undefined,\n version: node.version ?? undefined,\n author: node.author ?? undefined,\n bodyHash: node.bodyHash,\n frontmatterHash: node.frontmatterHash,\n bytes: node.bytes,\n tokens: node.tokens ?? undefined,\n linksOutCount: node.linksOutCount,\n linksInCount: node.linksInCount,\n externalRefsCount: node.externalRefsCount,\n frontmatter: node.frontmatter ?? {},\n };\n}\n\nfunction toLinkForSchema(link: Link): unknown {\n return {\n source: link.source,\n target: link.target,\n kind: link.kind,\n confidence: link.confidence,\n sources: link.sources,\n trigger: link.trigger ?? undefined,\n location: link.location ?? undefined,\n raw: link.raw ?? undefined,\n };\n}\n","/**\n * Built-in extension registry. Returns the eleven extensions bundled with\n * the reference implementation, ready to be registered on a Kernel. The\n * set matches ROADMAP §Step 2 verbatim.\n *\n * Keeping runtime references separate from the manifest-only entries the\n * Registry indexes: a consumer that only needs to list what's bundled\n * iterates `listBuiltIns()` for cheap manifest facts, while the\n * orchestrator needs the concrete `IProvider` / `IExtractor` / ... values\n * to actually call walk / extract / evaluate / format. Two exports\n * keep both access patterns first-class.\n *\n * **Spec § A.6 — qualified ids.** Every built-in declares its `pluginId`\n * directly in its module export (built-ins have no `plugin.json`, so\n * the bundle declaration IS the source of truth for their namespace).\n * Two namespaces by convention:\n *\n * - **`core/`** — kernel-internal primitives (every rule, the ASCII\n * formatter, the external-URL counter extractor). Platform-agnostic.\n * - **`claude/`** — the Claude Code Provider bundle (the Provider plus\n * its kind-aware extractors: frontmatter, slash, at-directive).\n *\n * The registry composes the qualified id `<pluginId>/<id>` at registration\n * time; cross-extension references (`defaultRefreshAction`, future\n * `composes[]`) MUST use the qualified form.\n *\n * **Spec § A.7 — granularity.** Each bundle declares whether the user\n * toggles it whole (`granularity: 'bundle'`) or one extension at a time\n * (`granularity: 'extension'`). The two built-in bundles split:\n *\n * - `claude` — `granularity: 'bundle'`. Provider + its kind-aware\n * extractors form a coherent platform integration; the user enables\n * or disables the whole Claude Code surface, never half of it.\n * - `core` — `granularity: 'extension'`. Per the spec promise that\n * \"no extension is privileged, removable\", every kernel built-in\n * (each rule, the ASCII formatter, the external-URL counter extractor)\n * is independently toggle-able via its qualified id (e.g.\n * `sm plugins disable core/superseded`).\n */\n\nimport type {\n IAction,\n IProvider,\n IExtractor,\n IFormatter,\n IHook,\n IRule,\n} from '../kernel/extensions/index.js';\nimport type { Extension } from '../kernel/registry.js';\nimport type { TGranularity } from '../kernel/types/plugin.js';\nimport { bucketByKind } from '../kernel/util/bucket-by-kind.js';\nimport { claudeProvider } from './providers/claude/index.js';\nimport { frontmatterExtractor } from './extractors/frontmatter/index.js';\nimport { slashExtractor } from './extractors/slash/index.js';\nimport { atDirectiveExtractor } from './extractors/at-directive/index.js';\nimport { externalUrlCounterExtractor } from './extractors/external-url-counter/index.js';\nimport { triggerCollisionRule } from './rules/trigger-collision/index.js';\nimport { brokenRefRule } from './rules/broken-ref/index.js';\nimport { supersededRule } from './rules/superseded/index.js';\nimport { linkConflictRule } from './rules/link-conflict/index.js';\nimport { asciiFormatter } from './formatters/ascii/index.js';\nimport { validateAllRule } from './rules/validate-all/index.js';\n\nexport interface IBuiltIns {\n providers: IProvider[];\n extractors: IExtractor[];\n rules: IRule[];\n /**\n * Built-in actions. Empty until the job subsystem ships (Decision\n * #114 — `IAction` is manifest-only today, runtime invocation is\n * deferred). Carried as a typed field so the bucketing covers all\n * six kinds without conditional checks at call sites.\n */\n actions: IAction[];\n formatters: IFormatter[];\n /**\n * Hooks bundled with the reference impl. Empty in this bump (A.11\n * adds the kind itself; concrete built-in hooks land separately if\n * the demand surfaces — bookkeeping / metrics hooks are the obvious\n * future candidates). Carried as a typed field so call sites can\n * iterate `bundle.hooks` without conditional checks.\n */\n hooks: IHook[];\n}\n\n/**\n * Concrete runtime instance of any extension kind a built-in can carry.\n * Mirrors what the orchestrator actually invokes (`walk` / `extract` /\n * `evaluate` / `format` / `on`); composed into the `IBuiltIns` buckets\n * by `builtIns()`. `IAction` is manifest-only today (runtime entry\n * point lands with the job subsystem); kept in the union so the\n * bucketing is structurally exhaustive.\n */\nexport type TBuiltInExtension = IProvider | IExtractor | IRule | IAction | IFormatter | IHook;\n\n/**\n * One bundle of built-in extensions. The bundle's `id` is the plugin id\n * (`'core'` / `'claude'`) — built-ins have no `plugin.json` so the\n * bundle declaration IS the source of truth for both the namespace and\n * the granularity policy.\n */\nexport interface IBuiltInBundle {\n id: string;\n granularity: TGranularity;\n extensions: TBuiltInExtension[];\n}\n\n/**\n * The two built-in bundles, in their canonical order. Consumers that\n * need to apply per-bundle / per-extension policies (the runtime\n * `composeScanExtensions`, `sm plugins list`) iterate this directly.\n *\n * Iteration order is stable: claude first, core second. It mirrors the\n * order in which built-ins land in the registry (claude Provider +\n * extractors, then core rules / formatter). Stable order matters for\n * snapshot tests and CI output diffs.\n */\nexport const builtInBundles: IBuiltInBundle[] = [\n {\n id: 'claude',\n granularity: 'bundle',\n extensions: [\n claudeProvider,\n frontmatterExtractor,\n slashExtractor,\n atDirectiveExtractor,\n ],\n },\n {\n id: 'core',\n granularity: 'extension',\n extensions: [\n externalUrlCounterExtractor,\n triggerCollisionRule,\n brokenRefRule,\n supersededRule,\n linkConflictRule,\n asciiFormatter,\n validateAllRule,\n ],\n },\n];\n\n/**\n * Bucketed view of every built-in, in the shape the orchestrator\n * consumes. Composed from `builtInBundles` so the source of truth stays\n * single. NOT filtered by `config_plugins` — call sites that need\n * granular gating (`composeScanExtensions`) walk the bundles themselves.\n */\nexport function builtIns(): IBuiltIns {\n const out: IBuiltIns = {\n providers: [],\n extractors: [],\n rules: [],\n actions: [],\n formatters: [],\n hooks: [],\n };\n for (const bundle of builtInBundles) {\n for (const ext of bundle.extensions) {\n bucketBuiltIn(ext, out);\n }\n }\n return out;\n}\n\n/** Flat view as Registry-ready Extension rows. */\nexport function listBuiltIns(): Extension[] {\n const out: Extension[] = [];\n for (const bundle of builtInBundles) {\n for (const x of bundle.extensions) {\n out.push(toExtensionRow(x));\n }\n }\n return out;\n}\n\n/**\n * Drop a built-in into the right bucket for the orchestrator. Shares the\n * dispatch table with `cli/util/plugin-runtime.ts:bucketLoaded` via\n * `bucketByKind` — the only difference is which kinds get a destination\n * array (built-ins surface every kind, including actions; the loaded-\n * plugin path skips actions because they dispatch via the job subsystem,\n * not the scan pipeline).\n */\nfunction bucketBuiltIn(ext: TBuiltInExtension, out: IBuiltIns): void {\n bucketByKind(ext.kind, ext, {\n provider: out.providers,\n extractor: out.extractors,\n rule: out.rules,\n action: out.actions,\n formatter: out.formatters,\n hook: out.hooks,\n });\n}\n\nfunction toExtensionRow(x: TBuiltInExtension): Extension {\n const row: Extension = {\n id: x.id,\n pluginId: x.pluginId,\n kind: x.kind,\n version: x.version,\n };\n if (x.description !== undefined) row.description = x.description;\n if (x.stability !== undefined) row.stability = x.stability;\n if (x.preconditions !== undefined) row.preconditions = x.preconditions;\n if (x.entry !== undefined) row.entry = x.entry;\n return row;\n}\n","/**\n * `PluginLoader` — default `PluginLoaderPort` implementation.\n *\n * Responsibilities (per spec §Plugin discovery + spec v0.8.0 § A.5 —\n * id uniqueness):\n *\n * 1. Discover plugin directories under one or more search paths, each\n * containing a `plugin.json` at its root.\n * 2. Parse + AJV-validate the manifest against\n * `plugins-registry.schema.json#/$defs/PluginManifest`.\n * 3. Enforce the structural rule **directory name == manifest id**. A\n * mismatch surfaces as `invalid-manifest` with a directed reason.\n * This rule alone rules out same-root collisions by construction\n * (a filesystem cannot host two siblings with the same name).\n * 4. Semver-check `manifest.specCompat` against the installed\n * `@skill-map/spec` version.\n * 5. Dynamic-import every path listed in `manifest.extensions[]`, expect a\n * default export matching the extension-kind schema, validate it, and\n * collect the loaded extensions.\n * 6. After every plugin has been loaded individually, scan the result set\n * for cross-root id collisions. Two plugins claiming the same id (any\n * combination of project + global + `--plugin-dir`) BOTH receive\n * status `id-collision`; no precedence rule applies. The user resolves\n * by renaming one and rerunning.\n * 7. Surface one of the documented failure modes when anything fails:\n * `invalid-manifest` / `incompatible-spec` / `load-error` /\n * `id-collision`. The kernel keeps booting regardless — a bad plugin\n * cannot take the process down.\n */\n\nimport { createRequire } from 'node:module';\nimport { existsSync, readFileSync, readdirSync } from 'node:fs';\nimport { isAbsolute, join, relative, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport { Ajv2020, type ValidateFunction } from 'ajv/dist/2020.js';\nimport addFormatsModule from 'ajv-formats';\nimport semver from 'semver';\n\nimport type {\n IDiscoveredPlugin,\n ILoadedExtension,\n IPluginManifest,\n IPluginStorageSchema,\n TPluginLoadStatus,\n} from '../types/plugin.js';\nimport type { PluginLoaderPort } from '../ports/plugin-loader.js';\nimport { PLUGIN_LOADER_TEXTS } from '../i18n/plugin-loader.texts.js';\nimport { tx } from '../util/tx.js';\nimport { KV_SCHEMA_KEY } from './plugin-store.js';\nimport type { ExtensionKind } from '../registry.js';\nimport type { ISchemaValidators } from './schema-validators.js';\nimport { HOOK_TRIGGERS } from '../extensions/hook.js';\n\n// ajv-formats ships CJS-first; the default export is the callable plugin\n// under ESM interop but TS sometimes types it as the namespace. Match\n// the normalisation `schema-validators.ts` does for the same reason.\nconst addFormats = (addFormatsModule as unknown as { default?: typeof addFormatsModule })\n .default ?? addFormatsModule;\n\ntype TAjv = InstanceType<typeof Ajv2020>;\n\n/**\n * Default per-extension dynamic-import timeout. Generous on purpose —\n * a plugin that legitimately takes >5s to import is misbehaving (it\n * should not have heavy work at module top level), but the extra\n * headroom avoids spurious timeouts on cold disk caches and slow CI\n * runners.\n */\nexport const DEFAULT_PLUGIN_IMPORT_TIMEOUT_MS = 5000;\n\nexport interface IPluginLoaderOptions {\n /** Search paths to scan for plugin directories. Non-existent paths are skipped. */\n searchPaths: string[];\n /** Required — used to validate plugin.json and each extension manifest. */\n validators: ISchemaValidators;\n /** Installed @skill-map/spec version, used for specCompat check. */\n specVersion: string;\n /**\n * When supplied, the loader calls this with every parsed plugin id\n * AFTER manifest + specCompat validation succeed. A return value of\n * `false` short-circuits the load: the plugin is reported with\n * `status: 'disabled'` and its extensions are NOT imported. Defaults\n * to \"always enabled\" when omitted (no DB / config integration —\n * useful for tests that assert raw discovery behaviour).\n */\n resolveEnabled?: (pluginId: string) => boolean;\n /**\n * Per-extension dynamic-import timeout in milliseconds. A plugin whose\n * top-level work (imports, side effects) exceeds this is reported as\n * `load-error` with a message naming the timeout, instead of hanging\n * the host CLI command (`sm scan`, `sm plugins list`, `sm watch`).\n * Defaults to `DEFAULT_PLUGIN_IMPORT_TIMEOUT_MS` (5s). Tests pass a\n * smaller value to exercise the timeout path quickly.\n *\n * Note: there is no AbortSignal on `import()` in Node 24 — when the\n * timer wins, the import is abandoned (the dangling promise resolves\n * later and is GC'd) but its side effects, if any, still run. The\n * timeout protects the orchestrator from hanging, not the host\n * process from a misbehaving plugin's runtime cost.\n */\n loadTimeoutMs?: number;\n}\n\n/**\n * Factory — preferred entry point for production callers (CLI). Returns\n * the port shape so the consumer is pinned to the abstract contract,\n * not the concrete class. Tests that need to access internals continue\n * to use `new PluginLoader(...)` directly.\n */\nexport function createPluginLoader(options: IPluginLoaderOptions): PluginLoaderPort {\n return new PluginLoader(options);\n}\n\nexport class PluginLoader implements PluginLoaderPort {\n readonly #options: IPluginLoaderOptions;\n readonly #loadTimeoutMs: number;\n\n constructor(options: IPluginLoaderOptions) {\n this.#options = options;\n this.#loadTimeoutMs = options.loadTimeoutMs ?? DEFAULT_PLUGIN_IMPORT_TIMEOUT_MS;\n }\n\n /**\n * Discover every plugin directory across the configured search paths.\n * Each direct child directory containing a `plugin.json` is considered a\n * plugin root. Non-plugin directories are silently skipped.\n */\n discoverPaths(): string[] {\n const out: string[] = [];\n for (const root of this.#options.searchPaths) {\n if (!existsSync(root)) continue;\n for (const entry of readdirSync(root, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n const candidate = join(root, entry.name);\n if (existsSync(join(candidate, 'plugin.json'))) {\n out.push(resolve(candidate));\n }\n }\n }\n return out;\n }\n\n /**\n * Full pass — discover every plugin, attempt to load each, then apply\n * the cross-root id-collision pass over the results. Two plugins that\n * survived their individual load with the same `manifest.id` both get\n * downgraded to status `id-collision` (no precedence — the spec is\n * explicit that \"no extension is privileged\"). Plugins that already\n * failed their individual load (`invalid-manifest` /\n * `incompatible-spec` / `load-error`) keep their original status:\n * their `id` field is untrusted (it may be a fall-back path hint when\n * the manifest could not be parsed) and they would muddy the\n * collision report.\n */\n async discoverAndLoadAll(): Promise<IDiscoveredPlugin[]> {\n const paths = this.discoverPaths();\n const out: IDiscoveredPlugin[] = [];\n for (const path of paths) {\n out.push(await this.loadOne(path));\n }\n return applyIdCollisions(out);\n }\n\n /**\n * Load a single plugin from its directory. Never throws — a failure is\n * reported via the returned status.\n */\n // eslint-disable-next-line complexity\n async loadOne(pluginPath: string): Promise<IDiscoveredPlugin> {\n const manifestResult = this.#parseAndValidateManifest(pluginPath);\n if (!manifestResult.ok) return manifestResult.failure;\n const manifest = manifestResult.manifest;\n\n // --- enabled resolution ----------------------------------------------\n // Only check after manifest + specCompat pass: a `disabled` status\n // implies \"we know this plugin enough to surface it; we just chose\n // not to run it\". An invalid or incompatible plugin gets its own\n // status and never reaches this branch.\n //\n // Spec § A.7 — granularity. The loader's pre-import resolveEnabled()\n // check uses the plugin id (the bundle-level key). Plugins with\n // granularity='extension' that want to gate individual extensions\n // need a richer policy at the runtime composer (see\n // `cli/util/plugin-runtime.ts`); the loader stage is intentionally\n // coarse — disabling the bundle id always wins, so the import work\n // is skipped wholesale.\n if (this.#options.resolveEnabled && !this.#options.resolveEnabled(manifest.id)) {\n return {\n path: pluginPath,\n id: manifest.id,\n status: 'disabled',\n manifest,\n granularity: manifest.granularity ?? 'bundle',\n reason: PLUGIN_LOADER_TEXTS.disabledByConfig,\n };\n }\n\n // --- extension imports + kind validation ------------------------------\n const loaded: ILoadedExtension[] = [];\n for (const relEntry of manifest.extensions) {\n const result = await this.#loadAndValidateExtensionEntry(pluginPath, manifest, relEntry);\n if (!result.ok) return result.failure;\n loaded.push(result.extension);\n }\n\n // --- storage output schemas (spec § A.12) -----------------------------\n // Opt-in: only plugins that declare `storage.schemas` (Mode B) or\n // `storage.schema` (Mode A) trigger the read+compile pass. A schema\n // file missing on disk OR failing AJV compile blocks the load with\n // `load-error` so the user sees the typo or syntax error at boot\n // instead of at first write. Storage modes without any schema\n // declaration stay permissive (status quo) — `storageSchemas` is\n // simply omitted from the discovered plugin row.\n const storageSchemasResult = loadStorageSchemas(pluginPath, manifest);\n if (!storageSchemasResult.ok) {\n return {\n ...fail(pluginPath, manifest.id, 'load-error', storageSchemasResult.reason),\n manifest,\n };\n }\n\n return {\n path: pluginPath,\n id: manifest.id,\n status: 'enabled',\n manifest,\n granularity: manifest.granularity ?? 'bundle',\n extensions: loaded,\n ...(storageSchemasResult.schemas\n ? { storageSchemas: storageSchemasResult.schemas }\n : {}),\n };\n }\n\n /**\n * Phase 1 of `loadOne` — read `plugin.json`, AJV-validate the manifest,\n * enforce the directory-name == manifest.id structural rule, and check\n * specCompat (range syntax + satisfies the installed spec version).\n * Returns either the validated manifest or an `IDiscoveredPlugin` with\n * the appropriate failure status.\n */\n #parseAndValidateManifest(\n pluginPath: string,\n ): { ok: true; manifest: IPluginManifest } | { ok: false; failure: IDiscoveredPlugin } {\n const manifestPath = join(pluginPath, 'plugin.json');\n\n let raw: unknown;\n try {\n raw = JSON.parse(readFileSync(manifestPath, 'utf8'));\n } catch (err) {\n return { ok: false, failure: fail(\n pluginPath,\n pathId(pluginPath),\n 'invalid-manifest',\n tx(PLUGIN_LOADER_TEXTS.invalidManifestJsonParse, {\n manifestPath,\n errDescription: describe(err),\n }),\n )};\n }\n\n const manifestResult = this.#options.validators.validatePluginManifest<IPluginManifest>(raw);\n if (!manifestResult.ok) {\n return { ok: false, failure: fail(\n pluginPath,\n pathId(pluginPath),\n 'invalid-manifest',\n tx(PLUGIN_LOADER_TEXTS.invalidManifestAjv, {\n manifestPath,\n errors: manifestResult.errors,\n }),\n )};\n }\n const manifest = manifestResult.data;\n\n // Cheap structural rule (spec § A.5 — plugin id global uniqueness).\n // Two siblings on the same filesystem cannot share a name; matching\n // the directory to the id rules out same-root collisions by construction.\n const dirName = pathId(pluginPath);\n if (dirName !== manifest.id) {\n return { ok: false, failure: {\n ...fail(\n pluginPath,\n manifest.id,\n 'invalid-manifest',\n tx(PLUGIN_LOADER_TEXTS.invalidManifestDirMismatch, {\n dirName,\n manifestId: manifest.id,\n }),\n ),\n manifest,\n }};\n }\n\n if (!semver.validRange(manifest.specCompat)) {\n return { ok: false, failure: {\n ...fail(\n pluginPath,\n manifest.id,\n 'invalid-manifest',\n tx(PLUGIN_LOADER_TEXTS.invalidSpecCompat, { specCompat: manifest.specCompat }),\n ),\n manifest,\n }};\n }\n if (!semver.satisfies(this.#options.specVersion, manifest.specCompat, { includePrerelease: true })) {\n return { ok: false, failure: {\n path: pluginPath,\n id: manifest.id,\n status: 'incompatible-spec',\n manifest,\n granularity: manifest.granularity ?? 'bundle',\n reason: tx(PLUGIN_LOADER_TEXTS.incompatibleSpec, {\n installedSpecVersion: this.#options.specVersion,\n specCompat: manifest.specCompat,\n }),\n }};\n }\n\n return { ok: true, manifest };\n }\n\n /**\n * Phase 3 of `loadOne` — load and validate one extension entry. Six\n * sub-checks (file exists, dynamic import, has kind, kind known,\n * pluginId match, kind-specific manifest validation including hook\n * trigger pre-check). On success returns the `ILoadedExtension` with\n * `pluginId` injected; on failure returns the `IDiscoveredPlugin`\n * with the appropriate status (`load-error` or `invalid-manifest`).\n */\n // Six sub-validations per extension entry (file exists, dynamic\n // import, has-kind, kind-known, pluginId match, kind-specific schema\n // including hook trigger pre-check). Each branch is one early-return;\n // splitting per sub-check would multiply the discriminated-union\n // boilerplate without making the validation pipeline clearer.\n // eslint-disable-next-line complexity\n async #loadAndValidateExtensionEntry(\n pluginPath: string,\n manifest: IPluginManifest,\n relEntry: string,\n ): Promise<{ ok: true; extension: ILoadedExtension } | { ok: false; failure: IDiscoveredPlugin }> {\n if (!isInsidePlugin(pluginPath, relEntry)) {\n return { ok: false, failure: {\n ...fail(\n pluginPath,\n manifest.id,\n 'invalid-manifest',\n tx(PLUGIN_LOADER_TEXTS.loadErrorPathEscapesPlugin, { relEntry, pluginPath }),\n ),\n manifest,\n }};\n }\n const abs = resolve(pluginPath, relEntry);\n if (!existsSync(abs)) {\n return { ok: false, failure: {\n ...fail(\n pluginPath,\n manifest.id,\n 'load-error',\n tx(PLUGIN_LOADER_TEXTS.loadErrorFileNotFound, { relEntry, abs }),\n ),\n manifest,\n }};\n }\n\n let mod: unknown;\n try {\n mod = await importWithTimeout(pathToFileURL(abs).href, this.#loadTimeoutMs);\n } catch (err) {\n return { ok: false, failure: {\n ...fail(\n pluginPath,\n manifest.id,\n 'load-error',\n tx(PLUGIN_LOADER_TEXTS.loadErrorImportFailed, {\n relEntry,\n errDescription: describe(err),\n }),\n ),\n manifest,\n }};\n }\n\n const exported = extractDefault(mod);\n if (!isRecord(exported) || typeof exported['kind'] !== 'string') {\n return { ok: false, failure: {\n ...fail(\n pluginPath,\n manifest.id,\n 'load-error',\n tx(PLUGIN_LOADER_TEXTS.loadErrorMissingKind, {\n relEntry,\n knownKindsList: KNOWN_KINDS_LIST,\n }),\n ),\n manifest,\n }};\n }\n\n const kind = exported['kind'] as ExtensionKind;\n if (!KNOWN_KINDS.has(kind)) {\n return { ok: false, failure: {\n ...fail(\n pluginPath,\n manifest.id,\n 'load-error',\n tx(PLUGIN_LOADER_TEXTS.loadErrorUnknownKind, {\n relEntry,\n kindReceived: String(exported['kind']),\n knownKindsList: KNOWN_KINDS_LIST,\n }),\n ),\n manifest,\n }};\n }\n\n // Spec § A.6 — `pluginId` is loader-injected. A hand-declared\n // mismatch is a hard load error; a matching declaration is tolerated\n // (stripped before AJV).\n const declaredPluginId = exported['pluginId'];\n if (typeof declaredPluginId === 'string' && declaredPluginId !== manifest.id) {\n return { ok: false, failure: {\n ...fail(\n pluginPath,\n manifest.id,\n 'invalid-manifest',\n tx(PLUGIN_LOADER_TEXTS.loadErrorPluginIdMismatch, {\n relEntry,\n declared: declaredPluginId,\n manifestId: manifest.id,\n }),\n ),\n manifest,\n }};\n }\n\n // Strip runtime methods + `pluginId` so AJV's strict\n // `unevaluatedProperties: false` doesn't reject the export.\n const manifestView = stripFunctionsAndPluginId(exported);\n\n if (kind === 'hook') {\n const hookFailure = validateHookTriggers(pluginPath, manifest, relEntry, exported, manifestView);\n if (hookFailure) return { ok: false, failure: hookFailure };\n }\n\n const extValidator = this.#options.validators.validatorForExtension(kind);\n if (!extValidator(manifestView)) {\n const errors = (extValidator.errors ?? [])\n .map((e) => `${e.instancePath || '(root)'} ${e.message ?? e.keyword}`)\n .join('; ');\n return { ok: false, failure: {\n ...fail(\n pluginPath,\n manifest.id,\n 'load-error',\n tx(PLUGIN_LOADER_TEXTS.loadErrorManifestInvalid, { relEntry, kind, errors }),\n ),\n manifest,\n }};\n }\n\n // Shallow-clone the runtime instance + inject `pluginId` so two\n // plugins importing the same ESM-cached file don't stomp each\n // other's `pluginId`.\n const instance = isRecord(exported)\n ? { ...exported, pluginId: manifest.id }\n : exported;\n\n return { ok: true, extension: {\n kind,\n id: exported['id'] as string,\n pluginId: manifest.id,\n version: exported['version'] as string,\n entryPath: abs,\n module: mod,\n instance,\n }};\n }\n}\n\n/**\n * Spec § A.11 — Hook triggers validation. Runs BEFORE AJV so the user\n * gets a directed `invalid-manifest` reason (with offending trigger and\n * full hookable list) rather than a generic AJV enum error string under\n * `load-error`. Returns an `IDiscoveredPlugin` failure or `null` if the\n * triggers are valid.\n */\nfunction validateHookTriggers(\n pluginPath: string,\n manifest: IPluginManifest,\n relEntry: string,\n exported: Record<string, unknown>,\n manifestView: unknown,\n): IDiscoveredPlugin | null {\n const triggers = (manifestView as Record<string, unknown>)['triggers'];\n const hookId = (exported['id'] as string) ?? '?';\n if (!Array.isArray(triggers) || triggers.length === 0) {\n return {\n ...fail(\n pluginPath,\n manifest.id,\n 'invalid-manifest',\n tx(PLUGIN_LOADER_TEXTS.invalidManifestHookEmptyTriggers, { hookId }),\n ),\n manifest,\n };\n }\n for (const trig of triggers) {\n if (typeof trig !== 'string' || !(HOOK_TRIGGERS as readonly string[]).includes(trig)) {\n return {\n ...fail(\n pluginPath,\n manifest.id,\n 'invalid-manifest',\n tx(PLUGIN_LOADER_TEXTS.invalidManifestHookUnknownTrigger, {\n hookId,\n trigger: String(trig),\n hookableList: HOOKABLE_TRIGGERS_LIST,\n }),\n ),\n manifest,\n };\n }\n }\n return null;\n}\n\n// --- helpers ---------------------------------------------------------------\n\nconst KNOWN_KINDS = new Set<ExtensionKind>(['provider', 'extractor', 'rule', 'action', 'formatter', 'hook']);\nconst KNOWN_KINDS_LIST = [...KNOWN_KINDS].join(' / ');\n\n/**\n * Spec § A.11 — curated hookable trigger set. Single source of truth lives\n * in `kernel/extensions/hook.ts` (`HOOK_TRIGGERS`); the loader imports it\n * directly so the loader and the runtime contract cannot drift apart.\n */\nconst HOOKABLE_TRIGGERS_LIST = HOOK_TRIGGERS.join(', ');\n\n/**\n * Race the dynamic import against a timer. When the timer wins we throw\n * a clear timeout error — the caller turns it into a `load-error` row\n * naming the offending entry. The dangling import promise lingers in\n * Node's loader and resolves later (the result is GC'd unreferenced);\n * there is no public `import()` cancellation API in Node 24, so this\n * is the best we can do without spawning a worker thread.\n */\nasync function importWithTimeout(href: string, timeoutMs: number): Promise<unknown> {\n let timer: NodeJS.Timeout | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n reject(new Error(tx(PLUGIN_LOADER_TEXTS.importExceededTimeout, { timeoutMs })));\n }, timeoutMs);\n });\n try {\n return await Promise.race([import(href), timeout]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nfunction fail(\n path: string,\n id: string,\n status: TPluginLoadStatus,\n reason: string,\n): IDiscoveredPlugin {\n return { path, id, status, reason };\n}\n\n/**\n * Check that a manifest-declared relative path stays inside the plugin\n * tree once resolved. Rejects absolute paths and any value whose\n * resolved form lies above (or beside) the plugin root via `..`\n * components. Returns `null` when safe; otherwise the resolved\n * absolute path is returned for diagnostics.\n *\n * Closes the lane where one plugin directory references another\n * plugin's source (or arbitrary files on disk) by way of\n * `extensions: [\"../foo/index.js\"]` or `storage.schema:\n * \"../bar.schema.json\"`.\n */\nfunction isInsidePlugin(pluginPath: string, relEntry: string): boolean {\n if (isAbsolute(relEntry)) return false;\n const abs = resolve(pluginPath, relEntry);\n const rel = relative(pluginPath, abs);\n if (rel === '') return true;\n if (rel.startsWith('..')) return false;\n if (isAbsolute(rel)) return false;\n return true;\n}\n\nfunction describe(err: unknown): string {\n if (err instanceof Error) return err.message;\n try {\n return String(err);\n } catch {\n return 'unknown error';\n }\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nfunction extractDefault(mod: unknown): unknown {\n if (!isRecord(mod)) return mod;\n return 'default' in mod ? mod['default'] : mod;\n}\n\n/**\n * Drop function-typed properties AND the runtime-only `pluginId` so the\n * resulting object is JSON-Schema-validatable. Used on the runtime export\n * before AJV gets it: an extension's `detect` / `render` / etc. method is\n * part of its TypeScript contract, not its declarative manifest, and JSON\n * Schema's `unevaluatedProperties: false` posture would otherwise reject\n * the whole export. Same posture for `pluginId` — per spec § A.6 it's a\n * runtime concern injected by the loader, not a manifest field.\n *\n * Spec 0.8.0: Provider runtime instances carry an additional\n * runtime-only field per `kinds` entry — `schemaJson`, the loaded JSON\n * Schema for the kind. The manifest declares `schema` (a relative path\n * string); `schemaJson` is loaded by the kernel/loader at boot. Strip\n * it before AJV-validating against the strict provider schema (which\n * has `additionalProperties: false` on each kind entry).\n *\n * Cheap shallow + one-level-deep copy — manifests are flat enough.\n */\nfunction stripFunctionsAndPluginId(input: unknown): unknown {\n if (!isRecord(input)) return input;\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(input)) {\n if (typeof v === 'function') continue;\n if (k === 'pluginId') continue;\n if (k === 'kinds' && isRecord(v)) {\n out[k] = stripKindsRuntimeFields(v);\n continue;\n }\n out[k] = v;\n }\n return out;\n}\n\n/**\n * Provider `kinds` map: for each entry, drop runtime-only fields\n * (`schemaJson`) so AJV sees only the manifest-level fields the spec\n * declares (`schema`, `defaultRefreshAction`).\n */\nfunction stripKindsRuntimeFields(kinds: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [kind, entry] of Object.entries(kinds)) {\n if (!isRecord(entry)) {\n out[kind] = entry;\n continue;\n }\n const cleaned: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(entry)) {\n if (k === 'schemaJson') continue;\n if (typeof v === 'function') continue;\n cleaned[k] = v;\n }\n out[kind] = cleaned;\n }\n return out;\n}\n\n/** Fall-back plugin id derived from directory name when the manifest is unreadable. */\nfunction pathId(p: string): string {\n const parts = p.split(/[/\\\\]/);\n return parts[parts.length - 1] ?? p;\n}\n\n/**\n * Cross-root id-collision pass. Group survivors (plugins whose individual\n * load reached a status that exposes a *trusted* `manifest.id`) by id, and\n * for any group of size ≥ 2 rewrite every member's status to\n * `id-collision` with a reason naming the other path(s).\n *\n * \"Trusted id\" means the manifest parsed and validated. The eligible\n * statuses are therefore `enabled`, `disabled`, and `incompatible-spec`\n * (each of those keeps `manifest` populated). The remaining failure\n * modes — `invalid-manifest` and `load-error` — either never reached the\n * id-trust point (`invalid-manifest`) or carry a manifest that's still\n * structurally fine; we treat them inclusively. Pragmatically, the only\n * status whose `id` is a path fall-back is `invalid-manifest` from a\n * manifest that failed to parse — and those are excluded because the\n * fall-back id is the directory name, which by the same-root pigeonhole\n * cannot collide with another fall-back id (and a collision against a\n * real id would be misleading noise: \"rename your plugin to fix your\n * neighbour's broken JSON\" is bad guidance).\n *\n * Concretely we only consider plugins that have a `manifest` populated.\n */\n// eslint-disable-next-line complexity\nfunction applyIdCollisions(plugins: IDiscoveredPlugin[]): IDiscoveredPlugin[] {\n const buckets = new Map<string, IDiscoveredPlugin[]>();\n for (const p of plugins) {\n if (!p.manifest) continue; // skip path-fall-back ids (untrusted)\n const id = p.manifest.id;\n const bucket = buckets.get(id);\n if (bucket) bucket.push(p);\n else buckets.set(id, [p]);\n }\n\n const collidingPaths = new Set<string>();\n const collisionReason = new Map<string, string>();\n for (const [id, bucket] of buckets) {\n if (bucket.length < 2) continue;\n // Stable order so the rendered \"collides with\" list is deterministic\n // across runs — essential for snapshot tests and CI output diffs.\n const sorted = [...bucket].sort((a, b) => a.path.localeCompare(b.path));\n for (const member of sorted) {\n collidingPaths.add(member.path);\n const others = sorted.filter((p) => p.path !== member.path).map((p) => p.path);\n // Reason names the FIRST other path explicitly (matches the spec\n // suggestion) and lists the rest (if any) for the rare 3-way case.\n const pathB = others.length === 1 ? others[0]! : others.join(', ');\n collisionReason.set(\n member.path,\n tx(PLUGIN_LOADER_TEXTS.idCollision, { id, pathA: member.path, pathB }),\n );\n }\n }\n\n if (collidingPaths.size === 0) return plugins;\n\n return plugins.map((p) => {\n if (!collidingPaths.has(p.path)) return p;\n const next: IDiscoveredPlugin = {\n ...p,\n status: 'id-collision',\n reason: collisionReason.get(p.path) ?? p.reason ?? '',\n };\n // A colliding plugin's extensions are inert — strip them so a\n // careless caller cannot register them anyway. Manifest is kept\n // for diagnostics (`sm plugins list/show` shows version, author).\n delete next.extensions;\n return next;\n });\n}\n\n/**\n * Spec § A.12 — read and AJV-compile the storage output schemas a\n * plugin declares in its manifest. Returns either:\n *\n * - `{ ok: true, schemas: undefined }` — the plugin declared no\n * schemas (Mode A without `schema`, Mode B without `schemas`, or\n * no storage at all). Permissive — `storageSchemas` is omitted\n * from the discovered row and the runtime store wrapper skips\n * validation.\n * - `{ ok: true, schemas }` — every declared schema was read and\n * compiled. Mode A's single value-shape lives under the sentinel\n * `KV_SCHEMA_KEY`; Mode B's per-table schemas live under their\n * logical table name (matching the manifest map).\n * - `{ ok: false, reason }` — at least one schema file was missing,\n * unparseable as JSON, or rejected by AJV's compiler. The caller\n * surfaces the reason as `load-error`.\n *\n * One fresh Ajv instance per plugin keeps schema `$id` collisions from\n * leaking across plugins (and from polluting the kernel's spec\n * validators, which live on a separate cached instance — see\n * `schema-validators.ts`).\n */\n// eslint-disable-next-line complexity\nfunction loadStorageSchemas(\n pluginPath: string,\n manifest: IPluginManifest,\n):\n | { ok: true; schemas?: Record<string, IPluginStorageSchema> }\n | { ok: false; reason: string } {\n const storage = manifest.storage;\n if (!storage) return { ok: true };\n\n // Mode A — single optional `schema`.\n if (storage.mode === 'kv') {\n if (!storage.schema) return { ok: true };\n const compiled = compilePluginSchema(pluginPath, storage.schema);\n if (!compiled.ok) {\n const reason = tx(\n compiled.phase === 'read'\n ? PLUGIN_LOADER_TEXTS.loadErrorStorageKvSchemaRead\n : PLUGIN_LOADER_TEXTS.loadErrorStorageKvSchemaCompile,\n {\n pluginId: manifest.id,\n schemaPath: storage.schema,\n errDescription: compiled.errDescription,\n },\n );\n return { ok: false, reason };\n }\n return {\n ok: true,\n schemas: {\n [KV_SCHEMA_KEY]: {\n schemaPath: storage.schema,\n validate: compiled.validate,\n },\n },\n };\n }\n\n // Mode B — optional `schemas` map keyed by logical table name.\n if (!storage.schemas || Object.keys(storage.schemas).length === 0) {\n return { ok: true };\n }\n const out: Record<string, IPluginStorageSchema> = {};\n for (const [table, relPath] of Object.entries(storage.schemas)) {\n const compiled = compilePluginSchema(pluginPath, relPath);\n if (!compiled.ok) {\n const reason = tx(\n compiled.phase === 'read'\n ? PLUGIN_LOADER_TEXTS.loadErrorStorageSchemaRead\n : PLUGIN_LOADER_TEXTS.loadErrorStorageSchemaCompile,\n {\n pluginId: manifest.id,\n table,\n schemaPath: relPath,\n errDescription: compiled.errDescription,\n },\n );\n return { ok: false, reason };\n }\n out[table] = { schemaPath: relPath, validate: compiled.validate };\n }\n return { ok: true, schemas: out };\n}\n\n/**\n * Read a single JSON Schema file relative to the plugin directory and\n * compile it with a fresh Ajv2020 instance. Two failure modes:\n * - `phase: 'read'` — file missing, unreadable, or not JSON.\n * - `phase: 'compile'` — JSON parsed but AJV rejected it.\n * Both surface to the caller as `load-error` with a phase-specific\n * template message.\n */\nfunction compilePluginSchema(\n pluginPath: string,\n relPath: string,\n):\n | {\n ok: true;\n validate: ValidateFunction & {\n errors?: { instancePath: string; message?: string; keyword: string }[] | null;\n };\n }\n | { ok: false; phase: 'read' | 'compile'; errDescription: string } {\n if (!isInsidePlugin(pluginPath, relPath)) {\n return {\n ok: false,\n phase: 'read',\n errDescription: tx(PLUGIN_LOADER_TEXTS.loadErrorSchemaPathEscapesPlugin, { relPath, pluginPath }),\n };\n }\n const abs = resolve(pluginPath, relPath);\n let raw: unknown;\n try {\n raw = JSON.parse(readFileSync(abs, 'utf8'));\n } catch (err) {\n return { ok: false, phase: 'read', errDescription: describe(err) };\n }\n try {\n const ajv: TAjv = new Ajv2020({ strict: false, allErrors: true, allowUnionTypes: true });\n (addFormats as unknown as (a: TAjv) => void)(ajv);\n const compiled = ajv.compile(raw as object) as ValidateFunction & {\n errors?: { instancePath: string; message?: string; keyword: string }[] | null;\n };\n return { ok: true, validate: compiled };\n } catch (err) {\n return { ok: false, phase: 'compile', errDescription: describe(err) };\n }\n}\n\n/**\n * Locate the installed `@skill-map/spec` version at runtime. Handy default\n * for `IPluginLoaderOptions.specVersion` when the caller just wants the\n * real installed version without plumbing it through.\n */\nexport function installedSpecVersion(): string {\n const require = createRequire(import.meta.url);\n // Spec exports index.json but not package.json; we use the former to\n // locate the package root and then read package.json off disk directly.\n const indexPath = require.resolve('@skill-map/spec/index.json');\n const pkgPath = resolve(indexPath, '..', 'package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as { version: string };\n return pkg.version;\n}\n","/**\n * Kernel-side strings emitted by `kernel/adapters/plugin-loader.ts`.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation. See\n * `kernel/i18n/orchestrator.texts.ts` header for rationale.\n *\n * Reasons split by failure mode (per `IDiscoveredPlugin.status`):\n * - `invalid-manifest` → manifest JSON unreadable / schema mismatch /\n * directory name does not match manifest id\n * - `incompatible-spec` → `manifest.specCompat` does not satisfy the\n * installed spec version\n * - `load-error` → extension file missing, import failure,\n * wrong export shape, kind mismatch, schema\n * mismatch, OR import timeout\n * - `id-collision` → two plugins (any combination of roots, e.g.\n * project + global) declared the same `id`.\n * Both collided plugins are blocked; no\n * precedence rule applies.\n */\n\nexport const PLUGIN_LOADER_TEXTS = {\n invalidManifestJsonParse:\n '{{manifestPath}}: {{errDescription}}. Validate the JSON (e.g. `npx jsonlint plugin.json`).',\n\n invalidManifestAjv:\n '{{manifestPath}}: {{errors}}. See spec/schemas/plugins-registry.schema.json#/$defs/PluginManifest.',\n\n invalidSpecCompat:\n 'specCompat \"{{specCompat}}\" is not a valid semver range. Use a range like \"^1.0.0\".',\n\n incompatibleSpec:\n '@skill-map/spec {{installedSpecVersion}} does not satisfy specCompat \"{{specCompat}}\". ' +\n \"Either update the plugin's specCompat (and re-test) or pin sm to a compatible spec version.\",\n\n loadErrorFileNotFound:\n 'extension file not found: {{relEntry}} (resolved to {{abs}}). Check plugin.json#/extensions paths.',\n\n loadErrorImportFailed: '{{relEntry}}: import failed — {{errDescription}}',\n\n loadErrorMissingKind:\n '{{relEntry}}: default export missing a string `kind` field. Expected one of: {{knownKindsList}}.',\n\n loadErrorUnknownKind:\n '{{relEntry}}: unknown extension kind \"{{kindReceived}}\". Expected one of: {{knownKindsList}}.',\n\n loadErrorManifestInvalid:\n '{{relEntry}}: {{kind}} manifest invalid — {{errors}}. See spec/schemas/extensions/{{kind}}.schema.json.',\n\n importExceededTimeout:\n 'import exceeded {{timeoutMs}}ms — likely a top-level side effect ' +\n '(network call, infinite loop, large blocking work). Move side effects ' +\n 'into the runtime methods (`detect` / `evaluate` / `render` / etc.).',\n\n disabledByConfig: 'disabled by config_plugins or settings.json',\n\n invalidManifestDirMismatch:\n \"directory name '{{dirName}}' does not match manifest id '{{manifestId}}'. \" +\n 'Rename the directory to match the id, or update the manifest id to match the directory.',\n\n idCollision:\n \"Plugin '{{id}}' at {{pathA}} collides with the plugin at {{pathB}}. \" +\n 'Rename one and rerun.',\n\n loadErrorPluginIdMismatch:\n \"{{relEntry}}: extension declares pluginId '{{declared}}' but its plugin.json declares id '{{manifestId}}'. \" +\n 'Remove the explicit pluginId from the extension — the loader injects it from plugin.json#/id.',\n\n loadErrorStorageSchemaRead:\n \"plugin '{{pluginId}}' failed to load schema for table '{{table}}': {{schemaPath}} — {{errDescription}}\",\n\n loadErrorStorageSchemaCompile:\n \"plugin '{{pluginId}}' failed to compile schema for table '{{table}}': {{schemaPath}} — {{errDescription}}\",\n\n loadErrorStorageKvSchemaRead:\n \"plugin '{{pluginId}}' failed to load KV schema: {{schemaPath}} — {{errDescription}}\",\n\n loadErrorStorageKvSchemaCompile:\n \"plugin '{{pluginId}}' failed to compile KV schema: {{schemaPath}} — {{errDescription}}\",\n\n invalidManifestHookUnknownTrigger:\n \"Hook '{{hookId}}' declares unknown trigger '{{trigger}}'. Hookable triggers: {{hookableList}}.\",\n\n invalidManifestHookEmptyTriggers:\n \"Hook '{{hookId}}' declares no triggers. At least one entry from the curated set is required.\",\n\n loadErrorPathEscapesPlugin:\n \"extension entry '{{relEntry}}' resolves outside the plugin directory ({{pluginPath}}). Plugin entries must be relative paths inside the plugin tree.\",\n\n loadErrorSchemaPathEscapesPlugin:\n \"schema path '{{relPath}}' resolves outside the plugin directory ({{pluginPath}}). Plugin schemas must be relative paths inside the plugin tree.\",\n} as const;\n","/**\n * Plugin store wrappers — runtime injection for `ctx.store` per spec\n * § A.12 (opt-in `outputSchema` for plugin custom storage).\n *\n * Two shapes, mirroring the manifest's storage modes documented in\n * `spec/plugin-kv-api.md`:\n *\n * - Mode A — `KvStore.set(key, value)`. AJV-validates `value` against\n * the schema declared by `manifest.storage.schema` (single\n * value-shape) when present. Absent = permissive.\n * - Mode B — `DedicatedStore.write(table, row)`. AJV-validates `row`\n * against the per-table schema declared in `manifest.storage.schemas`\n * when present. Tables absent from the map accept any shape.\n *\n * Both wrappers are storage-engine agnostic — they accept a `persist`\n * callback the caller supplies. The persistence side (SQLite, in-memory,\n * mock) is the caller's concern; this wrapper's only job is the\n * AJV gate. That separation lets the test suite exercise the validator\n * without spinning up a real DB and lets the kernel adapter (future\n * `state_plugin_kvs` writer / dedicated-table writer) plug in\n * unchanged.\n *\n * Universal validation (`emitLink` against `link.schema.json`,\n * `enrichNode` against `node.schema.json`) is unaffected — it lives on\n * the orchestrator side and runs regardless of the plugin's\n * `outputSchema` opt-in.\n */\n\nimport type {\n IDiscoveredPlugin,\n IPluginStorageSchema,\n} from '../types/plugin.js';\nimport { tx } from '../util/tx.js';\nimport { PLUGIN_STORE_TEXTS } from '../i18n/plugin-store.texts.js';\n\n/**\n * Sentinel key under which Mode A stores its single value-shape schema\n * inside `IDiscoveredPlugin.storageSchemas`. The sentinel keeps the\n * shared `Record<string, IPluginStorageSchema>` map a single-typed\n * surface across both modes; consumers look up by sentinel for KV and\n * by table name for dedicated.\n */\nexport const KV_SCHEMA_KEY = '__kv__';\n\nexport interface IKvStorePersist {\n (key: string, value: unknown): void | Promise<void>;\n}\n\nexport interface IDedicatedStorePersist {\n (table: string, row: unknown): void | Promise<void>;\n}\n\n/**\n * Mode A wrapper. `set(key, value)` AJV-validates `value` against the\n * Mode A schema (sentinel key `__kv__`) when declared, then forwards\n * to `persist`. Validation failure throws with a message naming the\n * schema path and AJV errors; persistence is skipped on failure.\n *\n * `pluginId` is captured for diagnostics (the throw message names the\n * plugin). The wrapper does NOT itself scope by plugin id — that is\n * the persistence layer's job (the spec's `state_plugin_kvs` PK includes\n * `pluginId` and the kernel-side adapter prepends it before write).\n */\nexport interface IKvStoreWrapper {\n set(key: string, value: unknown): Promise<void>;\n}\n\n/**\n * Union shape exposed to extractors via `ctx.store`. Spec § A.12 — Mode A\n * (`kv`) returns a `set(key, value)` surface; Mode B (`dedicated`) returns\n * `write(table, row)`. Plugin authors narrow at the call site based on\n * the storage mode declared in their `plugin.json`.\n */\nexport type IPluginStore = IKvStoreWrapper | IDedicatedStoreWrapper;\n\nexport function makeKvStoreWrapper(opts: {\n pluginId: string;\n schema: IPluginStorageSchema | undefined;\n persist: IKvStorePersist;\n}): IKvStoreWrapper {\n const { pluginId, schema, persist } = opts;\n return {\n async set(key, value) {\n if (schema) {\n if (!schema.validate(value)) {\n throw new Error(\n tx(PLUGIN_STORE_TEXTS.kvValidationFailed, {\n pluginId,\n schemaPath: schema.schemaPath,\n key,\n errors: formatAjvErrors(schema.validate.errors ?? null),\n }),\n );\n }\n }\n await persist(key, value);\n },\n };\n}\n\n/**\n * Mode B wrapper. `write(table, row)` AJV-validates `row` against\n * `storageSchemas[table]` when declared, then forwards to `persist`.\n * Tables absent from the map are permissive — the wrapper forwards\n * straight to `persist` without validation.\n *\n * The wrapper accepts the full `storageSchemas` map (rather than a\n * single schema) so a plugin author can declare schemas for some\n * tables and leave others permissive in the same map without the\n * caller having to lookup-then-narrow.\n */\nexport interface IDedicatedStoreWrapper {\n write(table: string, row: unknown): Promise<void>;\n}\n\nexport function makeDedicatedStoreWrapper(opts: {\n pluginId: string;\n schemas: Record<string, IPluginStorageSchema> | undefined;\n persist: IDedicatedStorePersist;\n}): IDedicatedStoreWrapper {\n const { pluginId, schemas, persist } = opts;\n return {\n async write(table, row) {\n const schema = schemas?.[table];\n if (schema) {\n if (!schema.validate(row)) {\n throw new Error(\n tx(PLUGIN_STORE_TEXTS.dedicatedValidationFailed, {\n pluginId,\n table,\n schemaPath: schema.schemaPath,\n errors: formatAjvErrors(schema.validate.errors ?? null),\n }),\n );\n }\n }\n await persist(table, row);\n },\n };\n}\n\n/**\n * Convenience entry point: build whichever wrapper matches the\n * discovered plugin's storage mode. Returns `undefined` when the\n * plugin declared no storage at all (the orchestrator omits\n * `ctx.store` in that case, per the existing contract). Mode A\n * extracts the sentinel-keyed schema; Mode B forwards the full map.\n */\nexport function makePluginStore(opts: {\n plugin: IDiscoveredPlugin;\n persistKv?: IKvStorePersist;\n persistDedicated?: IDedicatedStorePersist;\n}): IPluginStore | undefined {\n const manifest = opts.plugin.manifest;\n if (!manifest?.storage) return undefined;\n const storageSchemas = opts.plugin.storageSchemas;\n\n if (manifest.storage.mode === 'kv') {\n if (!opts.persistKv) return undefined;\n const schema = storageSchemas?.[KV_SCHEMA_KEY];\n return makeKvStoreWrapper({\n pluginId: manifest.id,\n schema,\n persist: opts.persistKv,\n });\n }\n\n if (manifest.storage.mode === 'dedicated') {\n if (!opts.persistDedicated) return undefined;\n return makeDedicatedStoreWrapper({\n pluginId: manifest.id,\n schemas: storageSchemas,\n persist: opts.persistDedicated,\n });\n }\n\n return undefined;\n}\n\n/** Compact AJV error string suitable for the throw message. */\nfunction formatAjvErrors(\n errors: { instancePath: string; message?: string; keyword: string }[] | null,\n): string {\n if (!errors || errors.length === 0) return '(no AJV details)';\n return errors\n .map((e) => `${e.instancePath || '(root)'} ${e.message ?? e.keyword}`)\n .join('; ');\n}\n","/**\n * Hook runtime contract. The sixth plugin kind (spec § A.11).\n *\n * Hooks subscribe declaratively to a curated set of kernel lifecycle\n * events and react to them. Reaction-only by design: a hook cannot\n * mutate the pipeline, block emission, or alter outputs. Use cases\n * are notification (Slack on `job.completed`), integration glue (CI\n * webhook on `job.failed`), and bookkeeping (per-extractor metrics).\n *\n * The hookable trigger set is INTENTIONALLY SMALL — eight events. The\n * full `ProgressEmitterPort` catalog (per-node `scan.progress`,\n * `model.delta`, `run.*`, internal job lifecycle) is deliberately not\n * hookable: too verbose for a reactive surface, internal to the runner,\n * or covered elsewhere. Declaring a trigger outside the curated set\n * yields `invalid-manifest` at load time.\n *\n * Dual-mode (declared in manifest):\n *\n * - `deterministic` (default): `on(ctx)` runs in-process during the\n * dispatch of the matching event, synchronously between the\n * event's emission and the next pipeline step. Errors are caught\n * by the dispatcher, logged via `extension.error`, and never\n * block the main flow.\n * - `probabilistic`: the hook is enqueued as a job. Until the job\n * subsystem ships, probabilistic hooks load but skip dispatch\n * with a stderr advisory (Decision #114 in `ROADMAP.md`).\n *\n * Curated trigger set (per spec § A.11):\n *\n * 1. `scan.started` — pre-scan setup (one per scan).\n * 2. `scan.completed` — post-scan reaction (one per scan).\n * 3. `extractor.completed` — aggregated per-Extractor outputs.\n * 4. `rule.completed` — aggregated per-Rule outputs.\n * 5. `action.completed` — Action executed on a node.\n * 6. `job.spawning` — pre-spawn of runner subprocess.\n * 7. `job.completed` — most common trigger.\n * 8. `job.failed` — alerts, retry triggers.\n */\n\nimport type { IExtensionBase } from './base.js';\nimport type { Node, TExecutionMode } from '../types.js';\n\n/**\n * The eight hookable lifecycle events. Mirrors the `triggers[]` enum in\n * `spec/schemas/extensions/hook.schema.json`. Anything outside this set\n * is rejected at load time as `invalid-manifest`.\n */\nexport type THookTrigger =\n | 'scan.started'\n | 'scan.completed'\n | 'extractor.completed'\n | 'rule.completed'\n | 'action.completed'\n | 'job.spawning'\n | 'job.completed'\n | 'job.failed';\n\n/**\n * Frozen list mirror of `THookTrigger` for runtime introspection. The\n * loader validates `manifest.triggers[]` against this set; the\n * orchestrator's dispatcher iterates it in order when fanning an event\n * out to subscribed hooks.\n */\nexport const HOOK_TRIGGERS: readonly THookTrigger[] = Object.freeze([\n 'scan.started',\n 'scan.completed',\n 'extractor.completed',\n 'rule.completed',\n 'action.completed',\n 'job.spawning',\n 'job.completed',\n 'job.failed',\n] as const);\n\n/**\n * Context the dispatcher hands to `Hook.on()`. The shape is intentionally\n * narrow: a hook reacts to an event, it does not steer the pipeline.\n *\n * The `event` carries the raw `ProgressEvent` envelope (type, timestamp,\n * runId/jobId when applicable, data). Optional `node` / `extractorId`\n * / `ruleId` / `actionId` are extracted from the event payload by the\n * dispatcher when present so authors don't have to walk `event.data`.\n *\n * Probabilistic hooks additionally receive `runner` for LLM dispatch.\n * Deterministic hooks SHOULD ignore the field.\n */\nexport interface IHookContext {\n /** The raw event the dispatcher matched. */\n event: {\n type: THookTrigger;\n timestamp: string;\n runId?: string;\n jobId?: string;\n data?: unknown;\n };\n /**\n * Convenience extraction of the node payload when the event is\n * node-scoped (`action.completed`). Undefined for run-scoped or\n * scan-scoped events.\n */\n node?: Node;\n /**\n * Set on `extractor.completed` events. Qualified extension id of the\n * Extractor whose work the event aggregates.\n */\n extractorId?: string;\n /**\n * Set on `rule.completed` events. Qualified extension id of the Rule.\n */\n ruleId?: string;\n /**\n * Set on `action.completed` events. Qualified extension id of the\n * Action that just ran.\n */\n actionId?: string;\n /**\n * Set on `job.*` events once the job subsystem lands. Carries the\n * report payload for `job.completed`, the failure record for\n * `job.failed`, and the spawn metadata for `job.spawning`.\n */\n jobResult?: unknown;\n /**\n * `RunnerPort` injection for `probabilistic` hooks. `undefined` for\n * `deterministic` mode (the default). Probabilistic hooks land with\n * the job subsystem; the field is reserved here so the runtime\n * contract is forward-compatible without a major bump.\n */\n runner?: unknown;\n}\n\n/**\n * Optional declarative filter applied by the dispatcher BEFORE\n * invoking `on(ctx)`. Keys are payload field paths (top-level only in\n * v0.x); values are the literal expected match. The dispatcher walks\n * `event.data` for the field and short-circuits the invocation if the\n * value disagrees.\n *\n * Cross-field validation against declared `triggers` is best-effort\n * at load time: when none of the declared triggers carries a given\n * filter field, the loader surfaces `invalid-manifest`. The current\n * impl performs the basic enum check but defers full payload-shape\n * cross-validation to a follow-up — the dispatcher is permissive at\n * runtime (an unknown field never matches → the hook simply never\n * fires for that event, which is a correct interpretation of \"filter\n * by a field that doesn't exist\").\n */\nexport type THookFilter = Record<string, string | number | boolean>;\n\nexport interface IHook extends IExtensionBase {\n kind: 'hook';\n /**\n * Execution mode. Optional in the manifest with a default of\n * `deterministic` per `spec/schemas/extensions/hook.schema.json`.\n * Probabilistic hooks load but skip dispatch with a stderr advisory\n * until the job subsystem ships (Decision #114).\n */\n mode?: TExecutionMode;\n /**\n * Subset of the curated lifecycle trigger set this hook subscribes\n * to. MUST be non-empty; every entry MUST be a member of\n * `HOOK_TRIGGERS`. The loader validates both invariants and surfaces\n * `invalid-manifest` on violation.\n */\n triggers: THookTrigger[];\n /**\n * Optional declarative filter. Absent → invoke on every dispatched\n * event of every declared trigger.\n */\n filter?: THookFilter;\n /**\n * Hook entry point. Returns nothing; reactions are side effects.\n * Errors are caught by the dispatcher (logged as `extension.error`,\n * surfaced via `hook.failed` meta-event) and NEVER block the main\n * pipeline — a buggy hook degrades gracefully.\n */\n on(ctx: IHookContext): void | Promise<void>;\n}\n","/**\n * Layered config loader for `.skill-map/settings.json`. Walks the six\n * canonical layers (defaults → user → user-local → project → project-local\n * → overrides), deep-merges per key, validates each layer against the\n * `project-config` JSON schema, and skips offending keys (warning) or\n * fails fast (strict). The effective config plus a per-key sources map\n * are returned so `sm config show --source` can answer who set what.\n *\n * Layer semantics (low → high precedence):\n * 1. `defaults` — `src/config/defaults.json`, shipped in bundle.\n * 2. `user` — `~/.skill-map/settings.json`.\n * 3. `user-local` — `~/.skill-map/settings.local.json`.\n * 4. `project` — `<cwd>/.skill-map/settings.json`.\n * 5. `project-local` — `<cwd>/.skill-map/settings.local.json`.\n * 6. `override` — caller-supplied object (env vars / CLI flags).\n *\n * For scope === 'global', layers 4 and 5 resolve to the same files as 2/3\n * and are skipped to avoid double-merging the same source.\n *\n * Failure modes:\n * - missing file → silent skip (the layer is optional).\n * - malformed JSON → warning + skip whole layer (or throw if strict).\n * - schema violation → strip the offending key + warning (or throw\n * if strict). Per-key resilience: a single bad\n * value never invalidates the rest of the file.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { loadSchemaValidators, type ISchemaValidators } from '../adapters/schema-validators.js';\nimport { CONFIG_LOADER_TEXTS } from '../i18n/config-loader.texts.js';\nimport { tx } from '../util/tx.js';\n\nimport DEFAULTS_RAW from '../../config/defaults.json' with { type: 'json' };\n\n// -----------------------------------------------------------------------------\n// Public types\n// -----------------------------------------------------------------------------\n\nexport interface IRetentionConfig {\n completed: number | null;\n failed: number | null;\n}\n\nexport interface IJobsConfig {\n ttlSeconds: number;\n graceMultiplier: number;\n minimumTtlSeconds: number;\n perActionTtl: Record<string, number>;\n perActionPriority: Record<string, number>;\n retention: IRetentionConfig;\n}\n\nexport interface IPluginConfigEntry {\n enabled?: boolean;\n config?: Record<string, unknown>;\n}\n\nexport interface IScanWatchConfig {\n debounceMs: number;\n}\n\nexport interface IScanConfig {\n tokenize: boolean;\n strict: boolean;\n /**\n * Reserved for a future implementation. The walker (built-in `claude`\n * Provider, `walkMarkdown`) currently always skips symlinks, regardless\n * of this flag's value. Following a symlink also requires cycle detection\n * and a `realpath`-resolved containment check, which is out of scope for\n * the current security pass — see audit M7. The schema field stays so\n * a settings.json that already opts in keeps validating; flipping it\n * to `true` is a no-op until the walker is extended.\n */\n followSymlinks: boolean;\n maxFileSizeBytes: number;\n watch: IScanWatchConfig;\n}\n\nexport interface IEffectiveConfig {\n schemaVersion: 1;\n autoMigrate: boolean;\n tokenizer: string;\n providers: string[];\n roots: string[];\n ignore: string[];\n scan: IScanConfig;\n plugins: Record<string, IPluginConfigEntry>;\n history: { share: boolean };\n jobs: IJobsConfig;\n i18n: { locale: string };\n}\n\nexport type TConfigLayer =\n | 'defaults'\n | 'user'\n | 'user-local'\n | 'project'\n | 'project-local'\n | 'override';\n\nexport interface ILoadConfigOptions {\n /** Determines whether project-scoped layers are walked (`project`) or skipped (`global`). */\n scope: 'project' | 'global';\n /** Working directory used to resolve project-scoped config files. */\n cwd: string;\n /** User home directory used to resolve user-scoped config files. */\n homedir: string;\n /** Top layer applied after every file layer. Translates env vars / CLI flags into config keys. */\n overrides?: Record<string, unknown>;\n /** When true, every warning is thrown as an `Error` instead of being collected. */\n strict?: boolean;\n}\n\nexport interface ILoadedConfig {\n effective: IEffectiveConfig;\n /** Maps dot-path keys (e.g. `\"scan.strict\"`) to the layer that last wrote them. */\n sources: Map<string, TConfigLayer>;\n /** Accumulated warnings about malformed JSON, schema violations, or invalid values. */\n warnings: string[];\n}\n\n// -----------------------------------------------------------------------------\n// Public API\n// -----------------------------------------------------------------------------\n\nconst DEFAULTS = DEFAULTS_RAW as unknown as IEffectiveConfig;\n\nexport function loadConfig(opts: ILoadConfigOptions): ILoadedConfig {\n const cwd = opts.cwd;\n const home = opts.homedir;\n const strict = opts.strict ?? false;\n const warnings: string[] = [];\n const sources = new Map<string, TConfigLayer>();\n const validators = loadSchemaValidators();\n\n let effective = structuredClone(DEFAULTS);\n recordSources('', effective, sources, 'defaults');\n\n const filePairs: Array<{ path: string; layer: TConfigLayer }> = [\n { path: join(home, '.skill-map', 'settings.json'), layer: 'user' },\n { path: join(home, '.skill-map', 'settings.local.json'), layer: 'user-local' },\n ];\n if (opts.scope === 'project') {\n filePairs.push(\n { path: join(cwd, '.skill-map', 'settings.json'), layer: 'project' },\n { path: join(cwd, '.skill-map', 'settings.local.json'), layer: 'project-local' },\n );\n }\n\n for (const { path, layer } of filePairs) {\n if (!existsSync(path)) continue;\n const partial = readJsonSafe(path, layer, warnings, strict);\n if (partial === null) continue;\n const cleaned = validateAndStrip(validators, partial, layer, warnings, strict);\n effective = deepMerge(effective as unknown as Record<string, unknown>, cleaned) as unknown as IEffectiveConfig;\n recordSources('', cleaned, sources, layer);\n }\n\n if (opts.overrides && Object.keys(opts.overrides).length > 0) {\n const cleaned = validateAndStrip(validators, opts.overrides, 'override', warnings, strict);\n effective = deepMerge(effective as unknown as Record<string, unknown>, cleaned) as unknown as IEffectiveConfig;\n recordSources('', cleaned, sources, 'override');\n }\n\n return { effective, sources, warnings };\n}\n\n// -----------------------------------------------------------------------------\n// Internals\n// -----------------------------------------------------------------------------\n\nfunction readJsonSafe(\n path: string,\n layer: TConfigLayer,\n warnings: string[],\n strict: boolean,\n): unknown | null {\n let text: string;\n try {\n text = readFileSync(path, 'utf8');\n } catch (err) {\n return reportAndSkip(\n tx(CONFIG_LOADER_TEXTS.readFailure, { layer, path, message: (err as Error).message }),\n warnings,\n strict,\n );\n }\n try {\n return JSON.parse(text);\n } catch (err) {\n return reportAndSkip(\n tx(CONFIG_LOADER_TEXTS.invalidJson, { layer, path, message: (err as Error).message }),\n warnings,\n strict,\n );\n }\n}\n\nfunction reportAndSkip(msg: string, warnings: string[], strict: boolean): null {\n if (strict) throw new Error(msg);\n warnings.push(msg);\n return null;\n}\n\n/**\n * Validate `raw` against the project-config schema and return a copy with\n * any offending keys removed. Errors are accumulated as warnings (or thrown\n * in strict mode). Continues per-key so a single bad value never invalidates\n * the rest of the file.\n */\nfunction validateAndStrip(\n validators: ISchemaValidators,\n raw: unknown,\n layer: TConfigLayer,\n warnings: string[],\n strict: boolean,\n): Record<string, unknown> {\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n const msg = tx(CONFIG_LOADER_TEXTS.expectedObject, { layer, type: describeJsonType(raw) });\n if (strict) throw new Error(msg);\n warnings.push(msg);\n return {};\n }\n\n const cloned = structuredClone(raw) as Record<string, unknown>;\n const validator = validators.getValidator('project-config');\n if (validator(cloned)) return cloned;\n\n for (const err of validator.errors ?? []) {\n applyValidationError(cloned, err, layer, warnings, strict);\n }\n return cloned;\n}\n\n/**\n * Apply one AJV error to the cloned config object: drop the offending\n * key (additionalProperties or invalid-value), then either throw (in\n * strict mode) or push a human-readable warning. Mutates `cloned` and\n * `warnings` in place.\n */\nfunction applyValidationError(\n cloned: Record<string, unknown>,\n err: { instancePath?: string; keyword: string; message?: string; params?: unknown },\n layer: TConfigLayer,\n warnings: string[],\n strict: boolean,\n): void {\n const path = err.instancePath ?? '';\n if (err.keyword === 'additionalProperties') {\n const extra = (err.params as { additionalProperty: string }).additionalProperty;\n deleteAtPath(cloned, path, extra);\n const msg = tx(CONFIG_LOADER_TEXTS.unknownKey, { layer, key: joinSegments(path, extra) });\n if (strict) throw new Error(msg);\n warnings.push(msg);\n return;\n }\n const segments = path.split('/').filter(Boolean);\n if (segments.length > 0) {\n const last = segments.pop() as string;\n deleteAtPath(cloned, '/' + segments.join('/'), last);\n }\n const msg = tx(CONFIG_LOADER_TEXTS.invalidValue, {\n layer,\n path: path || '(root)',\n message: err.message ?? err.keyword,\n });\n if (strict) throw new Error(msg);\n warnings.push(msg);\n}\n\nfunction describeJsonType(v: unknown): string {\n if (v === null) return 'null';\n if (Array.isArray(v)) return 'array';\n return typeof v;\n}\n\n/**\n * Names that, when used as object keys, manipulate the prototype chain.\n * Filtered everywhere user-controlled config data is walked or merged so\n * a hostile config layer cannot pollute `Object.prototype` or replace\n * the merged config's `[[Prototype]]`.\n */\nconst FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\nfunction deleteAtPath(root: Record<string, unknown>, parentPath: string, key: string): void {\n if (containsForbidden(parentPath, key)) return;\n const segments = parentPath.split('/').filter(Boolean);\n let cur: unknown = root;\n for (const seg of segments) {\n if (!isPlainObject(cur)) return;\n cur = cur[seg];\n }\n if (isPlainObject(cur)) delete cur[key];\n}\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return v !== null && typeof v === 'object' && !Array.isArray(v);\n}\n\nfunction containsForbidden(parentPath: string, leaf: string): boolean {\n if (FORBIDDEN_KEYS.has(leaf)) return true;\n for (const seg of parentPath.split('/')) {\n if (FORBIDDEN_KEYS.has(seg)) return true;\n }\n return false;\n}\n\nfunction joinSegments(instancePath: string, leaf: string): string {\n const segments = instancePath.split('/').filter(Boolean);\n return [...segments, leaf].join('.');\n}\n\nfunction deepMerge(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = { ...target };\n for (const [k, v] of Object.entries(source)) {\n if (FORBIDDEN_KEYS.has(k)) continue;\n out[k] = mergeValue(out[k], v);\n }\n return out;\n}\n\nfunction mergeValue(target: unknown, source: unknown): unknown {\n if (source === null || typeof source !== 'object' || Array.isArray(source)) {\n return source;\n }\n // When the source is a plain object — recurse even if the target slot\n // is empty, so nested `__proto__` / `constructor` / `prototype` keys\n // are filtered. Skipping the recursion in the empty-target case\n // (early version of the H1 fix) leaked pollution keys verbatim into\n // the merged config.\n const targetSlot =\n target !== null && typeof target === 'object' && !Array.isArray(target)\n ? (target as Record<string, unknown>)\n : {};\n return deepMerge(targetSlot, source as Record<string, unknown>);\n}\n\n// eslint-disable-next-line complexity\nfunction recordSources(\n prefix: string,\n value: unknown,\n map: Map<string, TConfigLayer>,\n layer: TConfigLayer,\n): void {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n if (prefix) map.set(prefix, layer);\n return;\n }\n const entries = Object.entries(value as Record<string, unknown>);\n if (entries.length === 0 && prefix) {\n map.set(prefix, layer);\n return;\n }\n for (const [k, v] of entries) {\n const next = prefix ? `${prefix}.${k}` : k;\n recordSources(next, v, map, layer);\n }\n}\n","/**\n * Kernel-side strings emitted by the layered config loader\n * (`kernel/config/loader.ts`). Same `tx(template, vars)` convention as\n * every other `kernel/i18n/*.texts.ts` peer.\n *\n * These warnings are accumulated into `ILoadedConfig.warnings` and surface\n * to the user via `cli/commands/config.ts` (and any other call site that\n * dumps them to stderr). Keeping them in the catalog keeps every\n * user-facing string greppable in one place and unblocks a future\n * Transloco migration.\n *\n * Strict mode also throws these strings as `Error` messages — same text,\n * same template; the loader picks `throw` vs `push` based on the\n * `strict` flag.\n */\n\nexport const CONFIG_LOADER_TEXTS = {\n readFailure:\n '[config:{{layer}}] failed to read {{path}}: {{message}}',\n\n invalidJson:\n '[config:{{layer}}] invalid JSON in {{path}}: {{message}}',\n\n expectedObject:\n '[config:{{layer}}] expected a JSON object, got {{type}}; ignored',\n\n unknownKey:\n '[config:{{layer}}] unknown key {{key}} ignored',\n\n invalidValue:\n '[config:{{layer}}] invalid value at {{path}}: {{message}}',\n} as const;\n","{\n \"schemaVersion\": 1,\n \"autoMigrate\": true,\n \"tokenizer\": \"cl100k_base\",\n \"providers\": [],\n \"roots\": [],\n \"ignore\": [],\n \"scan\": {\n \"tokenize\": true,\n \"strict\": false,\n \"followSymlinks\": false,\n \"maxFileSizeBytes\": 1048576,\n \"watch\": {\n \"debounceMs\": 300\n }\n },\n \"plugins\": {},\n \"history\": {\n \"share\": false\n },\n \"jobs\": {\n \"ttlSeconds\": 3600,\n \"graceMultiplier\": 3,\n \"minimumTtlSeconds\": 60,\n \"perActionTtl\": {},\n \"perActionPriority\": {},\n \"retention\": {\n \"completed\": 2592000,\n \"failed\": null\n }\n },\n \"i18n\": {\n \"locale\": \"en\"\n }\n}\n","/**\n * Decide whether a plugin is enabled, given the layered inputs.\n *\n * Decision (recorded against the option-3 vote in the plan):\n *\n * `.skill-map/settings.json#/plugins/<id>/enabled` is the **team-shared\n * baseline** committed to the repo; `config_plugins.enabled` in the DB\n * is the **user override** that takes precedence locally without\n * requiring a commit.\n *\n * Effective order (highest precedence first):\n *\n * 1. DB override (`config_plugins` row, if present)\n * 2. settings.json (`cfg.plugins[id].enabled`, if defined)\n * 3. installed default — every plugin is enabled until told otherwise\n *\n * The same precedence applies whether the scope is `project` or\n * `global`; the caller picks which scope's DB to read.\n */\n\nimport type { IEffectiveConfig } from './loader.js';\n\nexport function resolvePluginEnabled(\n pluginId: string,\n cfg: Pick<IEffectiveConfig, 'plugins'>,\n dbOverrides: Map<string, boolean>,\n): boolean {\n if (dbOverrides.has(pluginId)) return dbOverrides.get(pluginId) === true;\n const settingsEntry = cfg.plugins[pluginId];\n if (settingsEntry?.enabled !== undefined) return settingsEntry.enabled;\n return true;\n}\n\n/**\n * Build a closure suitable for `IPluginLoaderOptions.resolveEnabled`.\n * Captures the layered settings and DB override map once so the\n * loader can ask per-plugin without re-reading anything.\n */\nexport function makeEnabledResolver(\n cfg: Pick<IEffectiveConfig, 'plugins'>,\n dbOverrides: Map<string, boolean>,\n): (pluginId: string) => boolean {\n return (pluginId: string) => resolvePluginEnabled(pluginId, cfg, dbOverrides);\n}\n","/**\n * Strings emitted by the plugin runtime loader (`cli/util/plugin-runtime.ts`).\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const PLUGIN_RUNTIME_TEXTS = {\n /**\n * Stderr-ready warning for one non-loaded plugin. Format keeps the\n * status word and the reason scannable so a user can grep\n * `incompatible-spec` / `invalid-manifest` / `load-error`.\n */\n warningRow: 'plugin {{id}}: {{status}} — {{reason}}',\n\n /** Placeholder when a non-loaded plugin record carries no `reason`. */\n warningReasonMissing: '(no reason recorded)',\n} as const;\n","/**\n * Small text helpers shared by table renderers in `cli/commands/*`.\n *\n * The kernel-side string sanitisers (`stripAnsi`, `sanitizeForTerminal`)\n * live in `kernel/util/safe-text.ts`; this module hosts the\n * presentation-only helpers that the CLI uses on top of them.\n */\n\n/**\n * Truncate `s` to at most `max` user-perceived characters, appending an\n * ellipsis (`…`) when the input was longer. UTF-8 safe: splits on the\n * code-point boundary (via `Array.from`), so a multi-byte rune cannot\n * be cut in half — protects table renderers from emitting half-bytes\n * that a terminal then renders as `?` / mojibake.\n *\n * The single-character `…` is intentional: it visually announces the\n * truncation while keeping the column width predictable. Callers that\n * need a different ellipsis style should compose their own helper.\n */\nexport function truncateHead(s: string, max: number): string {\n const chars = Array.from(s);\n if (chars.length <= max) return s;\n return chars.slice(0, max - 1).join('') + '…';\n}\n\n/**\n * Same as `truncateHead` but preserves the END of the string instead\n * of the start. Used by `sm list` to keep the file basename visible\n * when the directory prefix is long (e.g. `…ents/very/long/foo.md`).\n */\nexport function truncateTail(s: string, max: number): string {\n const chars = Array.from(s);\n if (chars.length <= max) return s;\n return '…' + chars.slice(chars.length - max + 1).join('');\n}\n","/**\n * `withSqlite` — open a `SqliteStorageAdapter`, hand it to the callback,\n * and guarantee `close()` even if the callback throws or returns early.\n *\n * Standardises the open/use/close idiom every read-side CLI command was\n * open-coding. Eliminates four classes of bugs the inline boilerplate\n * tended to produce:\n *\n * 1. Forgotten `await adapter.close()` in an early-return branch\n * (resource leak; on Linux WSL the WAL file lingers).\n * 2. Drift between `autoBackup: false` (read-side verbs) and the\n * default `autoBackup: true` — easy to flip the wrong way when\n * copying boilerplate across commands.\n * 3. Double-close on the error path (`jobs.ts` had two `await\n * adapter.close()` calls, one in the catch + one in the finally).\n * Idempotent today, but a wart.\n * 4. Forgetting to wrap the body in try/finally at all (the rare\n * error path leaves the DB open until process exit).\n *\n * The callback receives the adapter — not `adapter.db` — because a\n * minority of call sites pass the adapter itself to repository\n * helpers. The common case (`adapter.db.selectFrom(...)`) reads the\n * same.\n *\n * Migration policy reminder:\n * - Read-side verbs (`check`, `list`, `show`, `export`, `graph`,\n * `history`, `orphans` list, `plugins list/doctor`, scan prior\n * load) SHOULD pass `{ autoBackup: false }` so a transient schema\n * upgrade doesn't write an unsolicited backup.\n * - Write-side verbs (scan persist, init seed, watch writer,\n * orphans reconcile / undo-rename) leave defaults on so first-run\n * schema upgrades are guarded by an automatic backup.\n */\n\nimport { existsSync } from 'node:fs';\n\nimport { createSqliteStorage } from '../../kernel/adapters/sqlite/index.js';\nimport type { ISqliteStorageAdapterOptions } from '../../kernel/adapters/sqlite/index.js';\nimport type { StoragePort } from '../../kernel/ports/storage.js';\n\nexport async function withSqlite<T>(\n options: ISqliteStorageAdapterOptions,\n fn: (adapter: StoragePort) => Promise<T>,\n): Promise<T> {\n const adapter = createSqliteStorage(options);\n await adapter.init();\n try {\n return await fn(adapter);\n } finally {\n await adapter.close();\n }\n}\n\n/**\n * Open the DB only when it already exists on disk; return `null`\n * otherwise. Wraps the very common `if (existsSync(dbPath)) { withSqlite\n * ... }` chain that every read-side command was open-coding.\n *\n * The bare-`existsSync` + `withSqlite` pair was both noisy and a subtle\n * footgun: `withSqlite` opens the adapter unconditionally, and the\n * adapter's `init()` runs `mkdirSync(dirname(absolute), { recursive:\n * true })` before opening the file. That is benign for write-side verbs\n * (they intend to create the scope) but wrong for \"read-only-if-present\"\n * lookups, which would silently provision `.skill-map/` directories on\n * misuse. `tryWithSqlite` keeps the no-op semantics by short-circuiting\n * before the adapter is constructed.\n *\n * `:memory:` is treated as \"exists\" — useful for tests that want the\n * read path to run against a fresh in-memory DB instead of skipping.\n */\nexport async function tryWithSqlite<T>(\n options: ISqliteStorageAdapterOptions,\n fn: (adapter: StoragePort) => Promise<T>,\n): Promise<T | null> {\n if (options.databasePath !== ':memory:' && !existsSync(options.databasePath)) {\n return null;\n }\n return withSqlite(options, fn);\n}\n","/**\n * `SqliteStorageAdapter` — default `StoragePort` implementation. Opens a\n * `node:sqlite` database behind the bespoke Kysely dialect, configures\n * the mandatory PRAGMAs (WAL, foreign keys), runs pending kernel\n * migrations, and exposes the namespaced port surface plus the typed\n * Kysely instance.\n *\n * **Storage-port-promotion (Phase A).** The adapter exposes the\n * non-transactional namespaces (`scans`, `issues`, `history`, `jobs`,\n * `pluginConfig`, `migrations`, `pluginMigrations`) as direct\n * properties. `enrichments` is transactional-only by design — it lives\n * exclusively on the `ITransactionalStorage` subset returned by\n * `port.transaction(...)`, never as a top-level namespace, so writers\n * are forced to share a transaction with `scans.persist`. Adapters\n * fail to compile when their share is incomplete on their end.\n *\n * **camelCase ↔ snake_case bridging.** This adapter installs Kysely's\n * `CamelCasePlugin`, so the typed schema (`schema.ts`) speaks\n * camelCase (`linksOutCount`, `bodyHash`) while the on-disk SQL is\n * snake_case (`links_out_count`, `body_hash`). The plugin rewrites\n * identifiers automatically for every fluent query —\n * `db.selectFrom('scan_nodes').where('linksOutCount', '>', 0)`\n * resolves to `WHERE links_out_count > 0` at execution time.\n *\n * **Trap to avoid:** `sql.raw` / `sql\\`...\\`` template literals are NOT\n * processed by the plugin. If a future caller writes\n * `sql\\`SELECT linksOutCount FROM scan_nodes\\``, the query will fail\n * at runtime against a snake_case-only database. Always use\n * snake_case inside raw SQL fragments (matching the migrations in\n * `src/migrations/`), or stick to the typed fluent API.\n */\n\nimport { mkdirSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\n\nimport { CamelCasePlugin, Kysely, sql } from 'kysely';\nimport type { Transaction } from 'kysely';\n\nimport type {\n IEnrichmentRecord,\n IExtractorRunRecord,\n} from '../../orchestrator.js';\nimport type {\n ITransactionalStorage,\n StoragePort,\n} from '../../ports/storage.js';\nimport type {\n IIssueRow,\n INodeBundle,\n INodeCounts,\n INodeFilter,\n IPersistOptions,\n} from '../../types/storage.js';\nimport type { Issue, Node, ScanResult } from '../../types.js';\nimport { STORAGE_TEXTS } from '../../i18n/storage.texts.js';\nimport { tx } from '../../util/tx.js';\nimport { NodeSqliteDialect } from './dialect.js';\nimport {\n aggregateHistoryStats,\n listExecutions,\n migrateNodeFks,\n} from './history.js';\nimport type {\n IHistoryStatsRange,\n IListExecutionsFilter,\n THistoryStatsPeriod,\n} from './history.js';\nimport { pruneTerminalJobs, selectReferencedJobFilePaths } from './jobs.js';\nimport {\n applyMigrations,\n discoverMigrations,\n planMigrations,\n writeBackup,\n} from './migrations.js';\nimport {\n applyPluginMigrations,\n discoverPluginMigrations,\n planPluginMigrations,\n resolvePluginMigrationsDir,\n} from './plugin-migrations.js';\nimport {\n deletePluginOverride,\n getPluginEnabled,\n listPluginOverrides,\n loadPluginOverrideMap,\n setPluginEnabled,\n} from './plugins.js';\nimport {\n loadExtractorRuns,\n loadNodeEnrichments,\n loadScanResult,\n rowToIssue,\n rowToLink,\n rowToNode,\n} from './scan-load.js';\nimport { persistScanResult } from './scan-persistence.js';\nimport type { IDatabase } from './schema.js';\n\nexport interface ISqliteStorageAdapterOptions {\n /**\n * Absolute or relative path to the DB file. Parent directory is created\n * if missing. `:memory:` is supported for tests (no directory created).\n */\n databasePath: string;\n\n /**\n * When true (default), pending kernel migrations are applied on `init()`.\n * Set false to open the DB without touching schema — used by\n * `sm db migrate --dry-run` and by a future `autoMigrate: false` config.\n */\n autoMigrate?: boolean;\n\n /**\n * When true (default), auto-migration writes a pre-migration backup.\n * Set false to skip — used by `sm db migrate --no-backup`.\n */\n autoBackup?: boolean;\n}\n\n/**\n * Whitelist of `INodeFilter.sortBy` columns. The port rejects unknown\n * values with an error so a typo does not silently sort by `path`. The\n * CLI also validates upstream (`sm list --sort-by`); this is the\n * defensive second gate.\n */\nconst SORT_BY_COLUMNS: ReadonlySet<string> = new Set([\n 'path',\n 'kind',\n 'bytes_total',\n 'bytesTotal',\n 'links_out_count',\n 'linksOutCount',\n 'links_in_count',\n 'linksInCount',\n 'external_refs_count',\n 'externalRefsCount',\n]);\n\nconst SORT_BY_DEFAULT_DIRECTION: Record<string, 'asc' | 'desc'> = {\n path: 'asc',\n kind: 'asc',\n bytesTotal: 'desc',\n linksOutCount: 'desc',\n linksInCount: 'desc',\n externalRefsCount: 'desc',\n};\n\nexport class SqliteStorageAdapter implements StoragePort {\n #db: Kysely<IDatabase> | null = null;\n readonly #options: ISqliteStorageAdapterOptions;\n\n // The namespace objects below are lazily-initialised property bags\n // bound to `this` so `port.scans.persist(...)` works without the\n // caller having to chain through a method. They are constructed in\n // `init()` because they need the `Kysely<IDatabase>` instance.\n scans!: StoragePort['scans'];\n issues!: StoragePort['issues'];\n history!: StoragePort['history'];\n jobs!: StoragePort['jobs'];\n pluginConfig!: StoragePort['pluginConfig'];\n migrations!: StoragePort['migrations'];\n pluginMigrations!: StoragePort['pluginMigrations'];\n\n constructor(options: ISqliteStorageAdapterOptions) {\n this.#options = options;\n }\n\n async init(): Promise<void> {\n if (this.#db) return;\n\n const path = this.#options.databasePath;\n if (path !== ':memory:') {\n const absolute = resolve(path);\n mkdirSync(dirname(absolute), { recursive: true });\n }\n\n if (this.#options.autoMigrate !== false) {\n // Run migrations on a short-lived raw connection so we don't have to\n // coordinate with Kysely's single-connection lifecycle. The file-level\n // DB is the same either way.\n const files = discoverMigrations();\n if (files.length > 0) {\n const raw = new DatabaseSync(path);\n try {\n raw.exec('PRAGMA foreign_keys = ON');\n applyMigrations(\n raw,\n path,\n { backup: this.#options.autoBackup !== false },\n files,\n );\n } finally {\n raw.close();\n }\n }\n }\n\n this.#db = new Kysely<IDatabase>({\n dialect: new NodeSqliteDialect({\n databasePath: path,\n onCreateConnection: (db) => {\n // WAL journaling: concurrent readers + a single writer. Matches\n // spec/db-schema.md and survives hard crashes better than the\n // rollback journal. `:memory:` doesn't support WAL — skip it.\n if (path !== ':memory:') {\n db.exec('PRAGMA journal_mode = WAL');\n }\n db.exec('PRAGMA foreign_keys = ON');\n db.exec('PRAGMA synchronous = NORMAL');\n },\n }),\n plugins: [new CamelCasePlugin()],\n });\n\n this.#bindNamespaces();\n }\n\n async close(): Promise<void> {\n if (!this.#db) return;\n await this.#db.destroy();\n this.#db = null;\n }\n\n /**\n * Access the underlying Kysely instance.\n *\n * Test-only escape hatch (per AGENTS.md § Kernel boundaries — tests\n * are the documented exception). CLI commands MUST consume the\n * adapter through the namespaced port surfaces (`port.<namespace>.*`\n * or `port.transaction(...)`); reaching for this getter from a\n * command file is a layering violation.\n */\n get db(): Kysely<IDatabase> {\n if (!this.#db) throw new Error('SqliteStorageAdapter: init() not called');\n return this.#db;\n }\n\n async transaction<T>(fn: (tx: ITransactionalStorage) => Promise<T>): Promise<T> {\n return this.db.transaction().execute(async (trx) => fn(buildTxSubset(trx)));\n }\n\n // --- internal: bind namespace property bags ----------------------------\n\n #bindNamespaces(): void {\n this.scans = {\n persist: (result, opts) => persistScansThroughNonTx(this.db, result, opts),\n load: () => loadScanResult(this.db),\n loadExtractorRuns: () => loadExtractorRuns(this.db),\n loadNodeEnrichments: () => loadNodeEnrichments(this.db),\n countRows: () => countRows(this.db),\n findNodes: (filter) => findNodes(this.db, filter),\n findNode: (path) => findNode(this.db, path),\n };\n\n this.issues = {\n listAll: () => listAllIssues(this.db),\n findActive: (predicate) => findActiveIssues(this.db, predicate),\n };\n\n this.history = {\n list: (filter: IListExecutionsFilter) => listExecutions(this.db, filter),\n aggregateStats: (\n range: IHistoryStatsRange,\n period: THistoryStatsPeriod,\n topN: number,\n ) => aggregateHistoryStats(this.db, range, period, topN),\n };\n\n this.jobs = {\n pruneTerminal: (status, cutoffMs) =>\n pruneTerminalJobs(this.db, status, cutoffMs),\n listTerminalCandidates: (status, cutoffMs) =>\n listTerminalCandidates(this.db, status, cutoffMs),\n listReferencedFilePaths: () => selectReferencedJobFilePaths(this.db),\n };\n\n this.pluginConfig = {\n set: (pluginId, enabled) => setPluginEnabled(this.db, pluginId, enabled),\n get: (pluginId) => getPluginEnabled(this.db, pluginId),\n list: () => listPluginOverrides(this.db),\n delete: (pluginId) => deletePluginOverride(this.db, pluginId),\n loadOverrideMap: () => loadPluginOverrideMap(this.db),\n };\n\n const path = this.#options.databasePath;\n\n this.migrations = {\n discover: () => discoverMigrations(),\n plan: (files) => withRawDb(path, (raw) => planMigrations(raw, files)),\n apply: (options, files) =>\n withRawDb(path, (raw) => {\n raw.exec('PRAGMA foreign_keys = ON');\n return applyMigrations(raw, path, options, files);\n }),\n writeBackup: (destPath) => writeBackup(path, destPath),\n currentSchemaVersion: () =>\n withRawDb(path, (raw) => {\n const row = raw.prepare('PRAGMA user_version').get() as\n | { user_version?: number }\n | undefined;\n const v = row?.user_version;\n return typeof v === 'number' && Number.isFinite(v) ? v : null;\n }),\n };\n\n this.pluginMigrations = {\n resolveDir: (plugin) => resolvePluginMigrationsDir(plugin),\n discover: (plugin) => discoverPluginMigrations(plugin),\n plan: (plugin, files) =>\n withRawDb(path, (raw) => planPluginMigrations(raw, plugin, files)),\n apply: (plugin, options, files) =>\n withRawDb(path, (raw) => {\n raw.exec('PRAGMA foreign_keys = ON');\n return applyPluginMigrations(raw, plugin, options, files);\n }),\n };\n }\n}\n\n/**\n * Non-transactional `scans.persist` — opens its own transaction\n * underneath because `persistScanResult` already handles the\n * orchestration. The transactional variant lives inside\n * `buildTxSubset`.\n */\nasync function persistScansThroughNonTx(\n db: Kysely<IDatabase>,\n result: ScanResult,\n opts?: IPersistOptions,\n): Promise<void> {\n await persistScanResult(\n db,\n result,\n opts?.renameOps ?? [],\n opts?.extractorRuns ?? [],\n opts?.enrichments ?? [],\n );\n}\n\nasync function countRows(db: Kysely<IDatabase>): Promise<INodeCounts> {\n const [nodes, links, issues] = await Promise.all([\n db\n .selectFrom('scan_nodes')\n .select(({ fn }) => fn.countAll<number>().as('c'))\n .executeTakeFirst(),\n db\n .selectFrom('scan_links')\n .select(({ fn }) => fn.countAll<number>().as('c'))\n .executeTakeFirst(),\n db\n .selectFrom('scan_issues')\n .select(({ fn }) => fn.countAll<number>().as('c'))\n .executeTakeFirst(),\n ]);\n return {\n nodes: Number(nodes?.c ?? 0),\n links: Number(links?.c ?? 0),\n issues: Number(issues?.c ?? 0),\n };\n}\n\n/**\n * Validate a filter's `sortBy` + `limit` upstream of the query\n * builder so the main `findNodes` body stays a thin pipeline. Returns\n * the resolved column / direction or throws — the throw is the gate.\n */\nfunction resolveSortAndLimit(filter: INodeFilter): {\n sortBy: string;\n direction: 'asc' | 'desc';\n limit: number | undefined;\n} {\n let sortBy = 'path';\n let direction: 'asc' | 'desc' = 'asc';\n if (filter.sortBy !== undefined) {\n if (!SORT_BY_COLUMNS.has(filter.sortBy)) {\n throw new Error(\n tx(STORAGE_TEXTS.findNodesInvalidSortBy, {\n sortBy: filter.sortBy,\n allowed: [...SORT_BY_COLUMNS].join(', '),\n }),\n );\n }\n sortBy = filter.sortBy;\n direction =\n filter.sortDirection ?? SORT_BY_DEFAULT_DIRECTION[filter.sortBy] ?? 'asc';\n }\n let limit: number | undefined;\n if (filter.limit !== undefined) {\n if (!Number.isInteger(filter.limit) || filter.limit <= 0) {\n throw new Error(\n tx(STORAGE_TEXTS.findNodesInvalidLimit, { value: filter.limit }),\n );\n }\n limit = filter.limit;\n }\n return { sortBy, direction, limit };\n}\n\nasync function findNodes(\n db: Kysely<IDatabase>,\n filter: INodeFilter,\n): Promise<Node[]> {\n const { sortBy, direction, limit } = resolveSortAndLimit(filter);\n\n let query = db.selectFrom('scan_nodes').selectAll();\n\n if (filter.kind !== undefined) {\n // `kind` is open string post-`open-node-kinds` refactor; the cast\n // through `never` survives because Kysely's typed column accepts\n // any string literal regardless of TS narrowing.\n query = query.where('kind', '=', filter.kind as never);\n }\n if (filter.hasIssues === true) {\n // Subquery: keep only nodes whose path is referenced by any\n // `scan_issues.nodeIds` array. node:sqlite ships JSON1 enabled,\n // so json_each is available everywhere we run.\n query = query.where(({ exists, selectFrom, ref }) =>\n exists(\n selectFrom(\n sql<{ value: string }>`json_each(scan_issues.node_ids_json)`.as('je'),\n )\n .innerJoin('scan_issues', (j) => j.onTrue())\n .select(sql<number>`1`.as('one'))\n .whereRef(sql.ref('je.value'), '=', ref('scan_nodes.path')),\n ),\n );\n }\n\n query = query.orderBy(sortBy as never, direction);\n if (limit !== undefined) query = query.limit(limit);\n\n const rows = await query.execute();\n return rows.map(rowToNode);\n}\n\nasync function findNode(\n db: Kysely<IDatabase>,\n path: string,\n): Promise<INodeBundle | null> {\n const nodeRow = await db\n .selectFrom('scan_nodes')\n .selectAll()\n .where('path', '=', path)\n .executeTakeFirst();\n if (!nodeRow) return null;\n\n // Outgoing / incoming / issues fan-out in parallel. Same shape as the\n // current `sm show` handler.\n const [outRows, inRows, issueRows] = await Promise.all([\n db.selectFrom('scan_links').selectAll().where('sourcePath', '=', path).execute(),\n db.selectFrom('scan_links').selectAll().where('targetPath', '=', path).execute(),\n db.selectFrom('scan_issues').selectAll().execute(),\n ]);\n\n return {\n node: rowToNode(nodeRow),\n linksOut: outRows.map(rowToLink),\n linksIn: inRows.map(rowToLink),\n issues: issueRows.map(rowToIssue).filter((i) => i.nodeIds.includes(path)),\n };\n}\n\nasync function listAllIssues(db: Kysely<IDatabase>): Promise<Issue[]> {\n const rows = await db.selectFrom('scan_issues').selectAll().execute();\n return rows.map(rowToIssue);\n}\n\nasync function findActiveIssues(\n db: Kysely<IDatabase>,\n predicate: (issue: Issue) => boolean,\n): Promise<IIssueRow[]> {\n const rows = await db.selectFrom('scan_issues').selectAll().execute();\n const out: IIssueRow[] = [];\n for (const row of rows) {\n const issue = rowToIssue(row);\n if (predicate(issue)) out.push({ id: row.id, issue });\n }\n return out;\n}\n\nfunction buildTxSubset(trx: Transaction<IDatabase>): ITransactionalStorage {\n return {\n scans: {\n persist: (result, opts) =>\n persistScanResult(\n trx,\n result,\n opts?.renameOps ?? [],\n opts?.extractorRuns ?? [],\n opts?.enrichments ?? [],\n ).then(() => undefined),\n },\n issues: {\n deleteById: async (id) => {\n await trx.deleteFrom('scan_issues').where('id', '=', id).execute();\n },\n insert: async (issue) => {\n await trx\n .insertInto('scan_issues')\n .values({\n ruleId: issue.ruleId,\n severity: issue.severity,\n nodeIdsJson: JSON.stringify(issue.nodeIds),\n linkIndicesJson:\n issue.linkIndices !== undefined ? JSON.stringify(issue.linkIndices) : null,\n message: issue.message,\n detail: issue.detail ?? null,\n fixJson: issue.fix !== undefined ? JSON.stringify(issue.fix) : null,\n dataJson: issue.data !== undefined ? JSON.stringify(issue.data) : null,\n })\n .execute();\n },\n },\n enrichments: {\n upsertMany: async (records: IEnrichmentRecord[]) => {\n await upsertEnrichments(trx, records);\n },\n },\n history: {\n migrateNodeFks: (from: string, to: string) =>\n migrateNodeFks(trx, from, to),\n },\n };\n}\n\n/**\n * Upsert every fresh `IEnrichmentRecord` into `node_enrichments`.\n * Composite PK is `(nodePath, extractorId)`; conflict resolution is\n * \"replace\" so a fresh extractor run overwrites the prior\n * `valueJson` / `bodyHashAtEnrichment` / `enrichedAt` fields. Every\n * row lands with `stale = 0` (the caller just refreshed it).\n */\nasync function upsertEnrichments(\n trx: Transaction<IDatabase>,\n records: IEnrichmentRecord[],\n): Promise<void> {\n for (const r of records) {\n const valueJson = JSON.stringify(r.value ?? {});\n const isProbabilistic = r.isProbabilistic ? 1 : 0;\n await trx\n .insertInto('node_enrichments')\n .values({\n nodePath: r.nodePath,\n extractorId: r.extractorId,\n bodyHashAtEnrichment: r.bodyHashAtEnrichment,\n valueJson,\n stale: 0,\n enrichedAt: r.enrichedAt,\n isProbabilistic,\n })\n .onConflict((oc) =>\n oc.columns(['nodePath', 'extractorId']).doUpdateSet({\n bodyHashAtEnrichment: r.bodyHashAtEnrichment,\n valueJson,\n stale: 0,\n enrichedAt: r.enrichedAt,\n isProbabilistic,\n }),\n )\n .execute();\n }\n}\n\n/**\n * Read-only `state_jobs` filter mirroring the SELECT side of\n * `pruneTerminalJobs` — `sm job prune --dry-run` consumes this so the\n * preview names exactly the rows the live mode would delete.\n */\nasync function listTerminalCandidates(\n db: Kysely<IDatabase>,\n status: 'completed' | 'failed',\n cutoffMs: number,\n): Promise<{ deletedCount: number; filePaths: string[] }> {\n const rows = await db\n .selectFrom('state_jobs')\n .select(['id', 'filePath'])\n .where('status', '=', status)\n .where('finishedAt', 'is not', null)\n .where('finishedAt', '<', cutoffMs)\n .execute();\n return {\n deletedCount: rows.length,\n filePaths: rows\n .map((r) => r.filePath)\n .filter((p): p is string => p !== null),\n };\n}\n\n/**\n * Open a raw `node:sqlite` handle for migration runs, invoke `fn`,\n * and close it. Each port-method call gets its own handle (the\n * verb's per-method calls are infrequent, so the open/close\n * overhead is negligible). The synchronous `fn` matches the\n * underlying free functions, which run BEGIN/COMMIT on the raw\n * handle directly per `migrations.ts` / `plugin-migrations.ts`.\n */\nfunction withRawDb<T>(path: string, fn: (raw: DatabaseSync) => T): T {\n const raw = new DatabaseSync(path);\n try {\n return fn(raw);\n } finally {\n raw.close();\n }\n}\n\n// `IExtractorRunRecord` re-exported for adapter consumers that don't\n// want to chain through `kernel/orchestrator`. The port itself returns\n// the same type from `loadExtractorRuns` (per the storage namespace).\nexport type { IExtractorRunRecord };\n","/**\n * Kernel-side strings emitted by `kernel/adapters/sqlite/*` and the\n * scan-query parser (`kernel/scan/query.ts`). Same `tx(template, vars)`\n * convention as every other `kernel/i18n/*.texts.ts` peer.\n *\n * These are error messages from the storage adapter and the export-\n * query parser. Some of them surface as user-visible CLI errors via\n * `cli/commands/*` `formatErrorMessage(err)` paths; keeping them in\n * the catalog makes the future translator pipeline trivial.\n */\n\nexport const STORAGE_TEXTS = {\n scanPersistInvalidScannedAt:\n 'persistScanResult: invalid scannedAt {{value}} (expected non-negative integer ms)',\n\n findNodesInvalidSortBy:\n 'findNodes: invalid sortBy \"{{sortBy}}\". Allowed: {{allowed}}.',\n\n findNodesInvalidLimit:\n 'findNodes: invalid limit {{value}}; expected positive integer.',\n} as const;\n\nexport const QUERY_TEXTS = {\n exportQueryInvalidToken:\n 'invalid token \"{{token}}\": expected key=value (e.g. kind=skill, has=issues, path=foo/*).',\n\n exportQueryDuplicateKey:\n 'key \"{{key}}\" appears more than once; combine values with a comma instead (e.g. kind=skill,agent).',\n\n exportQueryEmptyValues: 'key \"{{key}}\" has no values.',\n\n exportQueryUnknownKey:\n 'unknown key \"{{key}}\". Valid keys: kind, has, path.',\n\n exportQueryEmptyKind:\n 'kind=\"\" is not a valid node kind (empty).',\n\n exportQueryUnsupportedHas:\n 'has=\"{{value}}\" is not supported. Valid: {{allowed}}. (findings / summary land at Steps 10 / 11.)',\n} as const;\n","/**\n * Custom Kysely `Dialect` for Node 24's built-in `node:sqlite` module.\n *\n * Kysely ships a `SqliteDialect` that wraps `better-sqlite3` (native dep,\n * forbidden by Decision #7 — runtime is Node 24+ with zero native deps).\n * We therefore reuse Kysely's SQLite `Adapter`, `Introspector`, and\n * `QueryCompiler` (pure-JS, dialect-shape-only) and plug a bespoke\n * `Driver` that translates Kysely's `CompiledQuery` into `node:sqlite`\n * prepared statements.\n *\n * Minimal by design: a single connection, serialised via an async mutex\n * (SQLite writers are effectively serial anyway) and `BEGIN / COMMIT /\n * ROLLBACK` transactions driven through the same prepared-statement path.\n */\n\nimport { DatabaseSync, type StatementSync } from 'node:sqlite';\n\nimport {\n SqliteAdapter,\n SqliteIntrospector,\n SqliteQueryCompiler,\n type CompiledQuery,\n type DatabaseConnection,\n type Dialect,\n type Driver,\n type Kysely,\n type QueryResult,\n type TransactionSettings,\n} from 'kysely';\n\nexport interface INodeSqliteDialectConfig {\n /**\n * Absolute path to the database file, or `:memory:` for an in-memory DB.\n * Created with write access; parent directory must exist.\n */\n databasePath: string;\n\n /**\n * Called once after the underlying `DatabaseSync` is opened — use to\n * configure PRAGMAs (journal_mode, foreign_keys, etc.). Runs synchronously.\n */\n onCreateConnection?: (db: DatabaseSync) => void;\n}\n\nexport class NodeSqliteDialect implements Dialect {\n readonly #config: INodeSqliteDialectConfig;\n\n constructor(config: INodeSqliteDialectConfig) {\n this.#config = config;\n }\n\n createAdapter(): SqliteAdapter {\n return new SqliteAdapter();\n }\n\n createDriver(): Driver {\n return new NodeSqliteDriver(this.#config);\n }\n\n createIntrospector(db: Kysely<unknown>): SqliteIntrospector {\n return new SqliteIntrospector(db);\n }\n\n createQueryCompiler(): SqliteQueryCompiler {\n return new SqliteQueryCompiler();\n }\n}\n\nclass NodeSqliteDriver implements Driver {\n readonly #config: INodeSqliteDialectConfig;\n #db: DatabaseSync | null = null;\n #connection: NodeSqliteConnection | null = null;\n #mutex = new AsyncMutex();\n\n constructor(config: INodeSqliteDialectConfig) {\n this.#config = config;\n }\n\n async init(): Promise<void> {\n this.#db = new DatabaseSync(this.#config.databasePath);\n this.#config.onCreateConnection?.(this.#db);\n this.#connection = new NodeSqliteConnection(this.#db);\n }\n\n async acquireConnection(): Promise<DatabaseConnection> {\n if (!this.#connection) throw new Error('node-sqlite driver not initialised');\n await this.#mutex.lock();\n return this.#connection;\n }\n\n async releaseConnection(): Promise<void> {\n this.#mutex.unlock();\n }\n\n async beginTransaction(conn: DatabaseConnection, _settings: TransactionSettings): Promise<void> {\n await (conn as NodeSqliteConnection).exec('BEGIN');\n }\n\n async commitTransaction(conn: DatabaseConnection): Promise<void> {\n await (conn as NodeSqliteConnection).exec('COMMIT');\n }\n\n async rollbackTransaction(conn: DatabaseConnection): Promise<void> {\n await (conn as NodeSqliteConnection).exec('ROLLBACK');\n }\n\n async destroy(): Promise<void> {\n this.#db?.close();\n this.#db = null;\n this.#connection = null;\n }\n}\n\nclass NodeSqliteConnection implements DatabaseConnection {\n readonly #db: DatabaseSync;\n\n constructor(db: DatabaseSync) {\n this.#db = db;\n }\n\n exec(sql: string): void {\n this.#db.exec(sql);\n }\n\n async executeQuery<R>(query: CompiledQuery): Promise<QueryResult<R>> {\n const stmt: StatementSync = this.#db.prepare(query.sql);\n const params = query.parameters as unknown[];\n\n const head = query.sql.trim().slice(0, 6).toUpperCase();\n const isSelect = head.startsWith('SELECT') || head.startsWith('WITH');\n\n if (isSelect) {\n const rows = stmt.all(...(params as never[])) as R[];\n return { rows };\n }\n\n const info = stmt.run(...(params as never[]));\n const numAffectedRows = info.changes !== undefined ? BigInt(info.changes) : undefined;\n const insertId =\n info.lastInsertRowid === undefined\n ? undefined\n : typeof info.lastInsertRowid === 'bigint'\n ? info.lastInsertRowid\n : BigInt(info.lastInsertRowid);\n return {\n rows: [],\n ...(numAffectedRows !== undefined ? { numAffectedRows } : {}),\n ...(insertId !== undefined ? { insertId } : {}),\n };\n }\n\n async *streamQuery<R>(query: CompiledQuery): AsyncIterableIterator<QueryResult<R>> {\n // node:sqlite does not expose a cursor API. Buffer then yield once —\n // acceptable for our scale (kernel tables are small) and consistent\n // with Kysely's contract for streamless backends.\n const result = await this.executeQuery<R>(query);\n yield result;\n }\n}\n\n/**\n * Bare-bones async mutex. node:sqlite is single-threaded; SQLite writers\n * serialise anyway, so this guards Kysely's request/release lifecycle\n * without a real connection pool.\n */\nclass AsyncMutex {\n #locked = false;\n #waiters: Array<() => void> = [];\n\n async lock(): Promise<void> {\n if (!this.#locked) {\n this.#locked = true;\n return;\n }\n await new Promise<void>((resolve) => this.#waiters.push(resolve));\n this.#locked = true;\n }\n\n unlock(): void {\n this.#locked = false;\n const next = this.#waiters.shift();\n if (next) next();\n }\n}\n","/**\n * History readers, writers, and FK-migration helpers for the `state_*`\n * zone. Backs `sm history`, `sm history stats`, the rename heuristic,\n * and `sm orphans`.\n *\n * Three responsibilities:\n * 1. `insertExecution` — write a single `state_executions` row. Used by\n * tests today; consumed by `sm record` / `sm job run` once those\n * verbs ship.\n * 2. `listExecutions` — read with filters (node, action, status, time\n * window). Backs `sm history`.\n * 3. `aggregateHistoryStats` — totals, per-action, per-period, top\n * nodes, error rates. Backs `sm history stats`.\n * 4. `migrateNodeFks` — repoint every `state_*` reference to a node\n * from `fromPath` to `toPath`. Used by the rename heuristic\n * (forward, inside the scan tx) and by `sm orphans reconcile` /\n * `sm orphans undo-rename`.\n *\n * All mutating operations accept a `Kysely<IDatabase>` *or* a\n * `Transaction<IDatabase>` so callers can compose them inside a larger\n * tx (the rename heuristic does this).\n */\n\nimport { sql, type Insertable, type Kysely, type Selectable, type Transaction } from 'kysely';\n\nimport type {\n ExecutionFailureReason,\n ExecutionRecord,\n HistoryStats,\n HistoryStatsExecutionsPerPeriod,\n HistoryStatsPerActionRate,\n HistoryStatsTokensPerAction,\n HistoryStatsTopNode,\n} from '../../types.js';\nimport type {\n IHistoryStatsRange,\n IListExecutionsFilter,\n IMigrateNodeFksReport,\n THistoryStatsPeriod,\n} from '../../types/storage.js';\nimport type { IDatabase, IStateExecutionsTable } from './schema.js';\n\nexport type {\n IHistoryStatsRange,\n IListExecutionsFilter,\n IMigrateNodeFksReport,\n THistoryStatsPeriod,\n} from '../../types/storage.js';\n\ntype DbOrTx = Kysely<IDatabase> | Transaction<IDatabase>;\n\nconst FAILURE_REASONS: readonly ExecutionFailureReason[] = [\n 'runner-error',\n 'report-invalid',\n 'timeout',\n 'abandoned',\n 'job-file-missing',\n 'user-cancelled',\n];\n\n// --- Inserts ---------------------------------------------------------------\n\nexport async function insertExecution(\n db: DbOrTx,\n exec: ExecutionRecord,\n): Promise<void> {\n await db.insertInto('state_executions').values(executionToRow(exec)).execute();\n}\n\n// eslint-disable-next-line complexity\nfunction executionToRow(exec: ExecutionRecord): Insertable<IStateExecutionsTable> {\n return {\n id: exec.id,\n kind: exec.kind,\n extensionId: exec.extensionId,\n extensionVersion: exec.extensionVersion,\n nodeIdsJson: JSON.stringify(exec.nodeIds ?? []),\n contentHash: exec.contentHash ?? null,\n status: exec.status,\n failureReason: exec.failureReason ?? null,\n exitCode: exec.exitCode ?? null,\n runner: exec.runner ?? null,\n startedAt: exec.startedAt,\n finishedAt: exec.finishedAt,\n durationMs: exec.durationMs ?? null,\n tokensIn: exec.tokensIn ?? null,\n tokensOut: exec.tokensOut ?? null,\n reportPath: exec.reportPath ?? null,\n jobId: exec.jobId ?? null,\n };\n}\n\n// --- Reads -----------------------------------------------------------------\n\n// eslint-disable-next-line complexity\nexport async function listExecutions(\n db: DbOrTx,\n filter: IListExecutionsFilter = {},\n): Promise<ExecutionRecord[]> {\n let query = db.selectFrom('state_executions').selectAll();\n\n if (filter.actionId !== undefined) {\n query = query.where('extensionId', '=', filter.actionId);\n }\n if (filter.statuses && filter.statuses.length > 0) {\n query = query.where('status', 'in', filter.statuses);\n }\n if (filter.sinceMs !== undefined) {\n query = query.where('startedAt', '>=', filter.sinceMs);\n }\n if (filter.untilMs !== undefined) {\n query = query.where('startedAt', '<', filter.untilMs);\n }\n if (filter.nodePath !== undefined) {\n // JSON1 containment via correlated EXISTS. Same pattern as\n // `sm list --issue` (see src/cli/commands/list.ts).\n const target = filter.nodePath;\n query = query.where(({ exists, selectFrom }) =>\n exists(\n selectFrom(\n sql<{ value: string }>`json_each(state_executions.node_ids_json)`.as('je'),\n )\n .select(sql<number>`1`.as('one'))\n .where(sql.ref('je.value'), '=', target),\n ),\n );\n }\n\n // Stable sort: most-recent first.\n query = query.orderBy('startedAt', 'desc').orderBy('id', 'desc');\n\n if (filter.limit !== undefined) query = query.limit(filter.limit);\n\n const rows = await query.execute();\n return rows.map(rowToExecution);\n}\n\nfunction rowToExecution(row: {\n id: string;\n kind: 'action';\n extensionId: string;\n extensionVersion: string;\n nodeIdsJson: string;\n contentHash: string | null;\n status: 'completed' | 'failed' | 'cancelled';\n failureReason: string | null;\n exitCode: number | null;\n runner: string | null;\n startedAt: number;\n finishedAt: number;\n durationMs: number | null;\n tokensIn: number | null;\n tokensOut: number | null;\n reportPath: string | null;\n jobId: string | null;\n}): ExecutionRecord {\n return {\n id: row.id,\n kind: row.kind,\n extensionId: row.extensionId,\n extensionVersion: row.extensionVersion,\n nodeIds: parseStringArray(row.nodeIdsJson),\n contentHash: row.contentHash,\n status: row.status,\n failureReason: row.failureReason as ExecutionFailureReason | null,\n exitCode: row.exitCode,\n runner: row.runner as 'cli' | 'skill' | 'in-process' | null,\n startedAt: row.startedAt,\n finishedAt: row.finishedAt,\n durationMs: row.durationMs,\n tokensIn: row.tokensIn,\n tokensOut: row.tokensOut,\n reportPath: row.reportPath,\n jobId: row.jobId,\n };\n}\n\nfunction parseStringArray(s: string): string[] {\n const parsed = JSON.parse(s) as unknown;\n return Array.isArray(parsed) ? (parsed as string[]) : [];\n}\n\n// --- Aggregations ----------------------------------------------------------\n\n/**\n * Compute the bucketed aggregations that back `sm history stats --json`.\n * The caller is responsible for `elapsedMs` and for serialising\n * `range.{since,until}` to ISO-8601 strings — this function returns the\n * window in Unix ms so callers can keep their boundaries exact.\n */\nexport async function aggregateHistoryStats(\n db: DbOrTx,\n range: IHistoryStatsRange,\n period: THistoryStatsPeriod,\n topN: number,\n): Promise<Omit<HistoryStats, 'elapsedMs' | 'range'> & { rangeMs: { sinceMs: number | null; untilMs: number } }> {\n let query = db.selectFrom('state_executions').selectAll();\n if (range.sinceMs !== null) {\n query = query.where('startedAt', '>=', range.sinceMs);\n }\n query = query.where('startedAt', '<', range.untilMs);\n const rows = await query.execute();\n\n // Totals\n let executionsCount = 0;\n let completedCount = 0;\n let failedCount = 0;\n let tokensInTotal = 0;\n let tokensOutTotal = 0;\n let durationMsTotal = 0;\n\n // Per-action accumulators.\n const perAction = new Map<\n string,\n {\n actionId: string;\n actionVersion: string;\n executionsCount: number;\n tokensIn: number;\n tokensOut: number;\n durations: number[];\n failedCount: number;\n }\n >();\n\n // Per-period buckets.\n const perPeriod = new Map<\n number,\n { tokensIn: number; tokensOut: number; executionsCount: number }\n >();\n\n // Per-node accumulators.\n const perNode = new Map<\n string,\n { executionsCount: number; lastExecutedAt: number }\n >();\n\n // Per failure-reason accumulators.\n const perFailureReason: Record<ExecutionFailureReason, number> = {\n 'runner-error': 0,\n 'report-invalid': 0,\n 'timeout': 0,\n 'abandoned': 0,\n 'job-file-missing': 0,\n 'user-cancelled': 0,\n };\n\n const totals = { executionsCount, completedCount, failedCount, tokensInTotal, tokensOutTotal, durationMsTotal };\n for (const row of rows) {\n accumulateExecutionRow(row, totals, perFailureReason, perAction, perPeriod, perNode, period);\n }\n // Re-bind locals from the mutated totals object.\n executionsCount = totals.executionsCount;\n completedCount = totals.completedCount;\n failedCount = totals.failedCount;\n tokensInTotal = totals.tokensInTotal;\n tokensOutTotal = totals.tokensOutTotal;\n durationMsTotal = totals.durationMsTotal;\n\n // tokensPerAction sorted desc by tokensIn + tokensOut.\n const tokensPerAction: HistoryStatsTokensPerAction[] = Array.from(perAction.values())\n .map((acc) => ({\n actionId: acc.actionId,\n actionVersion: acc.actionVersion,\n executionsCount: acc.executionsCount,\n tokensIn: acc.tokensIn,\n tokensOut: acc.tokensOut,\n durationMsMean: meanDuration(acc.durations),\n durationMsMedian: medianDuration(acc.durations),\n }))\n .sort((a, b) => b.tokensIn + b.tokensOut - (a.tokensIn + a.tokensOut));\n\n // executionsPerPeriod sorted asc by periodStart.\n const sortedBuckets = Array.from(perPeriod.entries()).sort((a, b) => a[0] - b[0]);\n const executionsPerPeriod: HistoryStatsExecutionsPerPeriod[] = sortedBuckets.map(\n ([startMs, acc]) => ({\n periodStart: new Date(startMs).toISOString(),\n periodUnit: period,\n executionsCount: acc.executionsCount,\n tokensIn: acc.tokensIn,\n tokensOut: acc.tokensOut,\n }),\n );\n\n // topNodes sorted desc by count, tie-break desc by lastExecutedAt.\n const topNodes: HistoryStatsTopNode[] = Array.from(perNode.entries())\n .map(([nodePath, acc]) => ({\n nodePath,\n executionsCount: acc.executionsCount,\n lastExecutedAt: acc.lastExecutedAt,\n }))\n .sort((a, b) => {\n if (b.executionsCount !== a.executionsCount) {\n return b.executionsCount - a.executionsCount;\n }\n return b.lastExecutedAt - a.lastExecutedAt;\n })\n .slice(0, topN);\n\n // Per-action error rate. Sorted desc by rate, tie-break asc by actionId.\n const perActionRates: HistoryStatsPerActionRate[] = Array.from(perAction.values())\n .map((acc) => ({\n actionId: acc.actionId,\n rate: acc.executionsCount === 0 ? 0 : acc.failedCount / acc.executionsCount,\n executionsCount: acc.executionsCount,\n failedCount: acc.failedCount,\n }))\n .sort((a, b) => {\n if (b.rate !== a.rate) return b.rate - a.rate;\n return a.actionId.localeCompare(b.actionId);\n });\n\n return {\n schemaVersion: 1,\n rangeMs: { sinceMs: range.sinceMs, untilMs: range.untilMs },\n totals: {\n executionsCount,\n completedCount,\n failedCount,\n tokensIn: tokensInTotal,\n tokensOut: tokensOutTotal,\n durationMsTotal,\n },\n tokensPerAction,\n executionsPerPeriod,\n topNodes,\n errorRates: {\n global: executionsCount === 0 ? 0 : failedCount / executionsCount,\n perAction: perActionRates,\n perFailureReason,\n },\n };\n}\n\n/**\n * UTC-bucketed start of the period containing `dateMs`. Returns Unix ms.\n *\n * - `day`: floor to YYYY-MM-DDT00:00:00.000Z\n * - `week`: floor to Monday 00:00 UTC\n * - `month`: floor to day-1 00:00 UTC\n */\nexport function bucketStartMs(dateMs: number, period: THistoryStatsPeriod): number {\n const d = new Date(dateMs);\n const y = d.getUTCFullYear();\n const m = d.getUTCMonth();\n const day = d.getUTCDate();\n\n if (period === 'month') {\n return Date.UTC(y, m, 1, 0, 0, 0, 0);\n }\n\n if (period === 'day') {\n return Date.UTC(y, m, day, 0, 0, 0, 0);\n }\n\n // week: floor to Monday. JS getUTCDay() returns 0=Sun..6=Sat.\n // Monday-based offset: (day-of-week + 6) % 7 days back.\n const dow = d.getUTCDay();\n const offset = (dow + 6) % 7;\n return Date.UTC(y, m, day - offset, 0, 0, 0, 0);\n}\n\ninterface IExecutionRowTotals {\n executionsCount: number;\n completedCount: number;\n failedCount: number;\n tokensInTotal: number;\n tokensOutTotal: number;\n durationMsTotal: number;\n}\n\ninterface IPerActionAcc {\n actionId: string;\n actionVersion: string;\n executionsCount: number;\n tokensIn: number;\n tokensOut: number;\n durations: number[];\n failedCount: number;\n}\n\n/**\n * Fold one `state_executions` row into every accumulator the\n * `aggregateHistoryStats` query needs: totals, per-failure-reason\n * counts, per-action rollup, per-period bucket, per-node rollup. Pure\n * mutation of the supplied containers — caller iterates rows and emits\n * the final stats from the same containers afterward.\n *\n * Cyclomatic count comes from folding into 5 distinct accumulators in\n * one pass; per-accumulator helpers would split state mutation across\n * more files without making the algorithm clearer.\n */\n// eslint-disable-next-line complexity\nfunction accumulateExecutionRow(\n row: Selectable<IStateExecutionsTable>,\n totals: IExecutionRowTotals,\n perFailureReason: Record<ExecutionFailureReason, number>,\n perAction: Map<string, IPerActionAcc>,\n perPeriod: Map<number, { tokensIn: number; tokensOut: number; executionsCount: number }>,\n perNode: Map<string, { executionsCount: number; lastExecutedAt: number }>,\n period: THistoryStatsPeriod,\n): void {\n totals.executionsCount += 1;\n const tIn = row.tokensIn ?? 0;\n const tOut = row.tokensOut ?? 0;\n totals.tokensInTotal += tIn;\n totals.tokensOutTotal += tOut;\n if (row.durationMs !== null) totals.durationMsTotal += row.durationMs;\n\n if (row.status === 'completed') totals.completedCount += 1;\n if (row.status === 'failed') totals.failedCount += 1;\n\n if (row.failureReason !== null) {\n const reason = row.failureReason as ExecutionFailureReason;\n if (FAILURE_REASONS.includes(reason)) perFailureReason[reason] += 1;\n }\n\n // Per-action rollup keyed by (id, version).\n const actionKey = `${row.extensionId}@${row.extensionVersion}`;\n let actionAcc = perAction.get(actionKey);\n if (!actionAcc) {\n actionAcc = {\n actionId: row.extensionId,\n actionVersion: row.extensionVersion,\n executionsCount: 0,\n tokensIn: 0,\n tokensOut: 0,\n durations: [],\n failedCount: 0,\n };\n perAction.set(actionKey, actionAcc);\n }\n actionAcc.executionsCount += 1;\n actionAcc.tokensIn += tIn;\n actionAcc.tokensOut += tOut;\n if (row.durationMs !== null) actionAcc.durations.push(row.durationMs);\n if (row.status === 'failed') actionAcc.failedCount += 1;\n\n // Per-period bucket.\n const bucketStart = bucketStartMs(row.startedAt, period);\n let periodAcc = perPeriod.get(bucketStart);\n if (!periodAcc) {\n periodAcc = { tokensIn: 0, tokensOut: 0, executionsCount: 0 };\n perPeriod.set(bucketStart, periodAcc);\n }\n periodAcc.executionsCount += 1;\n periodAcc.tokensIn += tIn;\n periodAcc.tokensOut += tOut;\n\n // Per-node rollup.\n for (const path of parseStringArray(row.nodeIdsJson)) {\n let nodeAcc = perNode.get(path);\n if (!nodeAcc) {\n nodeAcc = { executionsCount: 0, lastExecutedAt: 0 };\n perNode.set(path, nodeAcc);\n }\n nodeAcc.executionsCount += 1;\n if (row.startedAt > nodeAcc.lastExecutedAt) nodeAcc.lastExecutedAt = row.startedAt;\n }\n}\n\nfunction meanDuration(values: number[]): number | null {\n if (values.length === 0) return null;\n let sum = 0;\n for (const v of values) sum += v;\n return Math.round(sum / values.length);\n}\n\nfunction medianDuration(values: number[]): number | null {\n if (values.length === 0) return null;\n const sorted = values.slice().sort((a, b) => a - b);\n const mid = sorted.length >> 1;\n if ((sorted.length & 1) === 1) return sorted[mid]!;\n return Math.round((sorted[mid - 1]! + sorted[mid]!) / 2);\n}\n\n// --- Stranded reference detection ----------------------------------------\n\n/**\n * Find every node path referenced from the `state_*` zone that is NOT in\n * the live snapshot. Used by `persistScanResult` to keep `orphan` issues\n * surface-visible across scans: the per-scan rename heuristic\n * only sees paths in `prior \\ current` of the *immediately preceding*\n * scan, so a stale reference from two scans ago becomes invisible after\n * one more scan. This sweep catches any `state_*` row whose `node_id`\n * (or any element of the `node_ids_json` array) is not in `livePaths`.\n *\n * Returns paths in deterministic lex-asc order.\n */\n// eslint-disable-next-line complexity\nexport async function findStrandedStateOrphans(\n trx: DbOrTx,\n livePaths: Set<string>,\n): Promise<string[]> {\n const stranded = new Set<string>();\n\n // state_jobs.node_id (simple column).\n const jobRows = await trx\n .selectFrom('state_jobs')\n .select(['nodeId'])\n .distinct()\n .execute();\n for (const r of jobRows) {\n if (!livePaths.has(r.nodeId)) stranded.add(r.nodeId);\n }\n\n // state_executions.node_ids_json (JSON array). Use json_each to\n // explode the array and select the distinct values in one shot.\n const execRows = await trx\n .selectFrom(\n sql<{ value: string }>`(\n SELECT DISTINCT je.value AS value\n FROM state_executions, json_each(state_executions.node_ids_json) je\n )`.as('execNodeIds'),\n )\n .select(['value'])\n .execute();\n for (const r of execRows) {\n if (!livePaths.has(r.value)) stranded.add(r.value);\n }\n\n // state_summaries.node_id (composite PK part).\n const summRows = await trx\n .selectFrom('state_summaries')\n .select(['nodeId'])\n .distinct()\n .execute();\n for (const r of summRows) {\n if (!livePaths.has(r.nodeId)) stranded.add(r.nodeId);\n }\n\n // state_enrichments.node_id (composite PK part).\n const enrichRows = await trx\n .selectFrom('state_enrichments')\n .select(['nodeId'])\n .distinct()\n .execute();\n for (const r of enrichRows) {\n if (!livePaths.has(r.nodeId)) stranded.add(r.nodeId);\n }\n\n // state_plugin_kvs.node_id (skip the empty-string sentinel for\n // plugin-global keys — that's not a node reference).\n const kvRows = await trx\n .selectFrom('state_plugin_kvs')\n .select(['nodeId'])\n .where('nodeId', '!=', '')\n .distinct()\n .execute();\n for (const r of kvRows) {\n if (!livePaths.has(r.nodeId)) stranded.add(r.nodeId);\n }\n\n return [...stranded].sort();\n}\n\n// --- FK migration ---------------------------------------------------------\n\n/**\n * Migrate every `state_*` reference to `fromPath` over to `toPath`. Runs\n * inside whatever transaction the caller passes (the rename heuristic\n * passes the same `Transaction<IDatabase>` it uses to write `scan_*`).\n *\n * Composite-PK semantics for the three tables that key on `node_id`:\n * `state_summaries` keys on `(node_id, summarizer_action_id)`,\n * `state_enrichments` on `(node_id, provider_id)`, and `state_plugin_kvs`\n * on `(plugin_id, node_id, key)`. A naive UPDATE would explode if a row\n * already exists at the destination PK. The conservative resolution is:\n * keep the destination row (it represents the live node's history) and\n * drop the migrating row. Each drop is reported as a `collision` so\n * callers can surface a diagnostic.\n *\n * `state_plugin_kvs.node_id` defaults to '' (sentinel for plugin-global\n * keys); we explicitly skip the sentinel when migrating.\n */\n// eslint-disable-next-line complexity\nexport async function migrateNodeFks(\n trx: DbOrTx,\n fromPath: string,\n toPath: string,\n): Promise<IMigrateNodeFksReport> {\n if (fromPath === toPath) {\n return { jobs: 0, executions: 0, summaries: 0, enrichments: 0, pluginKvs: 0, collisions: [] };\n }\n\n const report: IMigrateNodeFksReport = {\n jobs: 0,\n executions: 0,\n summaries: 0,\n enrichments: 0,\n pluginKvs: 0,\n collisions: [],\n };\n\n // 1. state_jobs.node_id — simple column, simple UPDATE.\n const jobsResult = await trx\n .updateTable('state_jobs')\n .set({ nodeId: toPath })\n .where('nodeId', '=', fromPath)\n .executeTakeFirst();\n report.jobs = Number(jobsResult.numUpdatedRows ?? 0);\n\n // 2. state_executions.node_ids_json — JSON array; pull, replace, write.\n const execRows = await trx\n .selectFrom('state_executions')\n .select(['id', 'nodeIdsJson'])\n .where(({ exists, selectFrom }) =>\n exists(\n selectFrom(\n sql<{ value: string }>`json_each(state_executions.node_ids_json)`.as('je'),\n )\n .select(sql<number>`1`.as('one'))\n .where(sql.ref('je.value'), '=', fromPath),\n ),\n )\n .execute();\n\n for (const row of execRows) {\n const ids = parseStringArray(row.nodeIdsJson);\n let mutated = false;\n const updated = ids.map((p) => {\n if (p === fromPath) {\n mutated = true;\n return toPath;\n }\n return p;\n });\n if (mutated) {\n await trx\n .updateTable('state_executions')\n .set({ nodeIdsJson: JSON.stringify(updated) })\n .where('id', '=', row.id)\n .execute();\n report.executions += 1;\n }\n }\n\n // 3. state_summaries — composite PK (node_id, summarizer_action_id).\n const summaryRows = await trx\n .selectFrom('state_summaries')\n .selectAll()\n .where('nodeId', '=', fromPath)\n .execute();\n for (const row of summaryRows) {\n const collision = await trx\n .selectFrom('state_summaries')\n .select(['nodeId'])\n .where('nodeId', '=', toPath)\n .where('summarizerActionId', '=', row.summarizerActionId)\n .executeTakeFirst();\n await trx\n .deleteFrom('state_summaries')\n .where('nodeId', '=', fromPath)\n .where('summarizerActionId', '=', row.summarizerActionId)\n .execute();\n if (collision) {\n report.collisions.push({\n table: 'state_summaries',\n fromPath,\n toPath,\n keys: { summarizerActionId: row.summarizerActionId },\n });\n continue;\n }\n await trx\n .insertInto('state_summaries')\n .values({ ...row, nodeId: toPath })\n .execute();\n report.summaries += 1;\n }\n\n // 4. state_enrichments — composite PK (node_id, provider_id).\n const enrichmentRows = await trx\n .selectFrom('state_enrichments')\n .selectAll()\n .where('nodeId', '=', fromPath)\n .execute();\n for (const row of enrichmentRows) {\n const collision = await trx\n .selectFrom('state_enrichments')\n .select(['nodeId'])\n .where('nodeId', '=', toPath)\n .where('providerId', '=', row.providerId)\n .executeTakeFirst();\n await trx\n .deleteFrom('state_enrichments')\n .where('nodeId', '=', fromPath)\n .where('providerId', '=', row.providerId)\n .execute();\n if (collision) {\n report.collisions.push({\n table: 'state_enrichments',\n fromPath,\n toPath,\n keys: { providerId: row.providerId },\n });\n continue;\n }\n await trx\n .insertInto('state_enrichments')\n .values({ ...row, nodeId: toPath })\n .execute();\n report.enrichments += 1;\n }\n\n // 5. state_plugin_kvs — composite PK (plugin_id, node_id, key). Skip\n // the empty-string sentinel for plugin-global keys.\n if (fromPath !== '') {\n const kvRows = await trx\n .selectFrom('state_plugin_kvs')\n .selectAll()\n .where('nodeId', '=', fromPath)\n .execute();\n for (const row of kvRows) {\n const collision = await trx\n .selectFrom('state_plugin_kvs')\n .select(['nodeId'])\n .where('pluginId', '=', row.pluginId)\n .where('nodeId', '=', toPath)\n .where('key', '=', row.key)\n .executeTakeFirst();\n await trx\n .deleteFrom('state_plugin_kvs')\n .where('pluginId', '=', row.pluginId)\n .where('nodeId', '=', fromPath)\n .where('key', '=', row.key)\n .execute();\n if (collision) {\n report.collisions.push({\n table: 'state_plugin_kvs',\n fromPath,\n toPath,\n keys: { pluginId: row.pluginId, key: row.key },\n });\n continue;\n }\n await trx\n .insertInto('state_plugin_kvs')\n .values({ ...row, nodeId: toPath })\n .execute();\n report.pluginKvs += 1;\n }\n }\n\n return report;\n}\n","/**\n * Storage helpers for `state_jobs` retention GC. Powers `sm job prune`.\n *\n * Two operations, both DB-only — the storage layer never touches the\n * filesystem (kept portable across runner backends; the FS walk that\n * pairs with `selectReferencedJobFilePaths` lives in\n * `kernel/jobs/orphan-files.ts`):\n *\n * 1. **Retention GC** — delete `state_jobs` rows whose `status` is\n * terminal (`completed` or `failed`) and whose `finishedAt` is\n * older than the supplied cutoff. The matching MD job files in\n * `.skill-map/jobs/` are deleted by the CLI command using the\n * `filePath` returned by this helper.\n *\n * 2. **Referenced job-file paths** — return every `state_jobs.filePath`\n * that points at a real MD file, normalized through `resolve()`.\n * The CLI's `sm job prune --orphan-files` flow combines this set\n * with a directory walk (`findOrphanJobFiles`) to compute the\n * MD files on disk that no row references.\n *\n * Per `spec/job-lifecycle.md` §Retention and GC, this MUST NOT run\n * implicitly during normal verb execution. The helpers themselves are\n * pure side-effects on the DB; the policy decision lives in the CLI.\n *\n * Per `spec/db-schema.md`, `state_executions` is append-only through\n * `v1.0`. These helpers do NOT touch that table — pruning a job row\n * leaves the matching execution row in place so post-mortem queries\n * still work after a job's audit trail in `state_jobs` is gone.\n */\n\nimport { resolve } from 'node:path';\n\nimport type { Kysely, Transaction } from 'kysely';\n\nimport type { IDatabase, TJobStatus } from './schema.js';\nimport type { IPruneResult } from '../../types/storage.js';\n\nexport type { IPruneResult } from '../../types/storage.js';\n\ntype DbOrTx = Kysely<IDatabase> | Transaction<IDatabase>;\n\n/**\n * Delete `state_jobs` rows in terminal `status` whose `finishedAt` is\n * older than `cutoffMs` (Unix ms). Returns the row count plus every\n * non-null `filePath` so the caller can unlink the on-disk MD files.\n *\n * `cutoffMs` is computed by the caller from the configured retention:\n * `Date.now() - retentionSeconds * 1000`.\n *\n * Order:\n * 1. SELECT the file_paths of rows that match (small projection).\n * 2. DELETE the same rows.\n * Two queries instead of `DELETE ... RETURNING` because Kysely's\n * SQLite dialect has historically had spotty support for RETURNING\n * across versions; the two-step variant is portable and the table\n * is small enough that the extra round-trip is negligible.\n */\nexport async function pruneTerminalJobs(\n db: DbOrTx,\n status: 'completed' | 'failed',\n cutoffMs: number,\n): Promise<IPruneResult> {\n const rows = await db\n .selectFrom('state_jobs')\n .select(['id', 'filePath'])\n .where('status', '=', status as TJobStatus)\n .where('finishedAt', 'is not', null)\n .where('finishedAt', '<', cutoffMs)\n .execute();\n\n if (rows.length === 0) {\n return { deletedCount: 0, filePaths: [] };\n }\n\n const ids = rows.map((r) => r.id);\n await db\n .deleteFrom('state_jobs')\n .where('id', 'in', ids)\n .execute();\n\n const filePaths = rows\n .map((r) => r.filePath)\n .filter((p): p is string => p !== null);\n return { deletedCount: rows.length, filePaths };\n}\n\n/**\n * Read every `state_jobs.filePath` currently set, normalized through\n * `resolve()`. The CLI pairs this set with `findOrphanJobFiles` (in\n * `kernel/jobs/orphan-files.ts`) to compute the MD files on disk that\n * no row references — the storage layer stays FS-free so a future\n * Postgres / in-memory adapter inherits no `node:fs` dependency.\n */\nexport async function selectReferencedJobFilePaths(\n db: DbOrTx,\n): Promise<Set<string>> {\n const rows = await db\n .selectFrom('state_jobs')\n .select(['filePath'])\n .where('filePath', 'is not', null)\n .execute();\n const out = new Set<string>();\n for (const row of rows) {\n if (row.filePath !== null) out.add(resolve(row.filePath));\n }\n return out;\n}\n","/**\n * Kernel migrations runner.\n *\n * Discovers `NNN_snake_case.sql` files in a migrations directory, compares\n * them against the `config_schema_versions` ledger for scope=`kernel`,\n * owner=`kernel`, and applies the pending ones inside a single transaction\n * each. `PRAGMA user_version` is kept in sync with the latest applied\n * kernel migration; a mismatch is surfaced by `sm doctor`.\n *\n * Auto-backup: before any apply, a copy of the DB goes to\n * `<dbDir>/backups/skill-map-pre-migrate-v<N>.db` where N is the target\n * migration number. Skipped for `:memory:` and when the caller sets\n * `backup: false` (used by `sm db migrate --no-backup`).\n */\n\nimport { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\nimport { fileURLToPath } from 'node:url';\n\nimport { MIGRATIONS_TEXTS } from '../../i18n/migrations.texts.js';\nimport { tx } from '../../util/tx.js';\n\nimport type {\n IApplyOptions,\n IApplyResult,\n IMigrationFile,\n IMigrationPlan,\n IMigrationRecord,\n} from '../../types/storage.js';\n\nexport type {\n IApplyOptions,\n IApplyResult,\n IMigrationFile,\n IMigrationPlan,\n IMigrationRecord,\n} from '../../types/storage.js';\n\nconst FILE_RE = /^(\\d{3})_([a-z0-9_]+)\\.sql$/;\n\n/**\n * Default migrations directory — resolves the bundled `migrations/` folder\n * relative to this file so it works in both dev (tsx) and dist (tsup\n * output) as long as `package.json#files` ships the `migrations/` folder\n * alongside `dist/`.\n *\n * Two layouts to handle:\n * - dev (tsx, source files):\n * src/kernel/adapters/sqlite/migrations.ts → src/migrations/\n * (three levels up from `here`).\n * - dist (tsup bundle, single flat `cli.js`):\n * src/dist/cli.js → src/dist/migrations/\n * (one level up — `here` IS the dist root, not a nested\n * `kernel/adapters/sqlite/` path, because the bundle is flat).\n *\n * We probe the flat layout first, then fall back to the source-shaped\n * layout. Consumers may override via `discoverMigrations(dir)`.\n */\nexport function defaultMigrationsDir(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n const flatLayout = resolve(here, 'migrations');\n if (existsSync(flatLayout)) return flatLayout;\n return resolve(here, '..', '..', '..', 'migrations');\n}\n\nexport function discoverMigrations(dir: string = defaultMigrationsDir()): IMigrationFile[] {\n if (!existsSync(dir)) return [];\n const files = readdirSync(dir, { withFileTypes: true })\n .filter((e) => e.isFile())\n .map((e) => e.name)\n .filter((name) => FILE_RE.test(name))\n .sort();\n\n const out: IMigrationFile[] = [];\n for (const name of files) {\n const match = FILE_RE.exec(name);\n if (!match) continue;\n out.push({\n version: Number.parseInt(match[1]!, 10),\n description: match[2]!,\n filePath: join(dir, name),\n });\n }\n // Reject duplicate version numbers early — a sorted sequence with a\n // repeat is a developer error (two branches both numbered 002).\n for (let i = 1; i < out.length; i++) {\n if (out[i]!.version === out[i - 1]!.version) {\n throw new Error(\n tx(MIGRATIONS_TEXTS.duplicateVersion, {\n version: out[i]!.version,\n dir,\n firstPath: out[i - 1]!.filePath,\n secondPath: out[i]!.filePath,\n }),\n );\n }\n }\n return out;\n}\n\n/**\n * Read the ledger for kernel migrations. Returns an empty list if the\n * `config_schema_versions` table doesn't yet exist (fresh DB).\n */\nexport function readLedger(db: DatabaseSync): IMigrationRecord[] {\n const tableExists = db\n .prepare(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name='config_schema_versions'\",\n )\n .get() as { name: string } | undefined;\n if (!tableExists) return [];\n\n const rows = db\n .prepare(\n \"SELECT scope, owner_id, version, description, applied_at FROM config_schema_versions WHERE scope='kernel' AND owner_id='kernel' ORDER BY version ASC\",\n )\n .all() as Array<{\n scope: string;\n owner_id: string;\n version: number;\n description: string;\n applied_at: number;\n }>;\n\n return rows.map((r) => ({\n scope: r.scope,\n ownerId: r.owner_id,\n version: r.version,\n description: r.description,\n appliedAt: r.applied_at,\n }));\n}\n\nexport function planMigrations(\n db: DatabaseSync,\n files: IMigrationFile[] = discoverMigrations(),\n): IMigrationPlan {\n const applied = readLedger(db);\n const appliedVersions = new Set(applied.map((r) => r.version));\n const pending = files.filter((f) => !appliedVersions.has(f.version));\n return { applied, pending };\n}\n\n/**\n * Apply pending migrations up to (and including) `options.to` — defaults\n * to the latest discovered. Each file is executed inside its own\n * `BEGIN / COMMIT` transaction; failure rolls back and throws, leaving\n * the DB and ledger in the last good state.\n */\n// Migration runner with backup + dry-run + per-file transactional\n// apply. Each guard (`backup` / `dryRun` / `to` / per-file try/catch)\n// is one branch; the file-by-file loop with rollback is the natural\n// shape of \"apply N migrations safely\".\n// eslint-disable-next-line complexity\nexport function applyMigrations(\n db: DatabaseSync,\n dbPath: string,\n options: IApplyOptions = {},\n files: IMigrationFile[] = discoverMigrations(),\n): IApplyResult {\n const { backup = true, dryRun = false, to } = options;\n\n const plan = planMigrations(db, files);\n const target = to ?? (files.length > 0 ? files[files.length - 1]!.version : 0);\n const toApply = plan.pending.filter((f) => f.version <= target);\n\n if (toApply.length === 0 || dryRun) {\n return { applied: toApply, backupPath: null };\n }\n\n // Compose the pre-migrate backup path here so `writeBackup` stays\n // a generic \"copy DB to dest\" primitive — the per-target naming\n // is the migrations runner's concern, not the helper's. For\n // `:memory:` the path is meaningless but `writeBackup` short-\n // circuits before using it.\n const backupPath = backup\n ? writeBackup(\n dbPath,\n join(dirname(resolve(dbPath)), 'backups', `skill-map-pre-migrate-v${target}.db`),\n )\n : null;\n\n for (const migration of toApply) {\n const sql = readFileSync(migration.filePath, 'utf8');\n // `migration.version` is parsed from a 3-digit filename prefix and is\n // therefore always a finite non-negative integer in normal flows.\n // Guard against future code paths that might loosen the parser\n // before the value flows into a string-interpolated PRAGMA — better\n // to fail fast than to surface a SQL error from the engine.\n if (!Number.isInteger(migration.version) || migration.version < 0 || migration.version > 9999) {\n throw new Error(\n tx(MIGRATIONS_TEXTS.invalidVersion, { value: String(migration.version) }),\n );\n }\n try {\n db.exec('BEGIN');\n db.exec(sql);\n // Record in the ledger in the same transaction so partial success\n // can't leave the ledger out of sync.\n db.prepare(\n `INSERT INTO config_schema_versions (scope, owner_id, version, description, applied_at)\n VALUES ('kernel', 'kernel', ?, ?, ?)`,\n ).run(migration.version, migration.description, Date.now());\n db.exec(`PRAGMA user_version = ${migration.version}`);\n db.exec('COMMIT');\n } catch (err) {\n try {\n db.exec('ROLLBACK');\n } catch {\n // ignore rollback failures\n }\n const reason = err instanceof Error ? err.message : String(err);\n throw new Error(\n tx(MIGRATIONS_TEXTS.applyFailed, {\n name: `${String(migration.version).padStart(3, '0')}_${migration.description}`,\n reason,\n }),\n { cause: err },\n );\n }\n }\n\n return { applied: toApply, backupPath };\n}\n\n/**\n * WAL checkpoint + atomic file copy of `dbPath` to `destPath`. The\n * caller composes `destPath` (the migrations runner names its\n * pre-migrate copies `skill-map-pre-migrate-v<N>.db`; `sm db backup`\n * names its on-demand copies `<timestamp>.db`). `destPath`'s parent\n * directory is created on demand. `:memory:` is a no-op (no file to\n * copy) and returns `null`.\n */\nexport function writeBackup(dbPath: string, destPath: string): string | null {\n if (dbPath === ':memory:') return null;\n const absoluteSource = resolve(dbPath);\n const absoluteDest = resolve(destPath);\n mkdirSync(dirname(absoluteDest), { recursive: true });\n // Checkpoint WAL to the main file before copy so the backup is complete\n // without needing to also copy the `-wal` / `-shm` sidecars.\n const db = new DatabaseSync(absoluteSource);\n try {\n db.exec('PRAGMA wal_checkpoint(TRUNCATE)');\n } finally {\n db.close();\n }\n copyFileSync(absoluteSource, absoluteDest);\n return absoluteDest;\n}\n","/**\n * Kernel-side strings emitted by `kernel/adapters/sqlite/migrations.ts`.\n * Same `tx(template, vars)` convention as every other\n * `kernel/i18n/*.texts.ts` peer.\n *\n * These messages bubble up via `Error.message`. Some surface verbatim to\n * the user through `cli/commands/db.ts` (which formats them as\n * `{{reason}}` in its templates) and through any other consumer that\n * formats migration failures.\n */\n\nexport const MIGRATIONS_TEXTS = {\n duplicateVersion:\n 'Duplicate migration version {{version}} in {{dir}}: {{firstPath}} and {{secondPath}}',\n\n invalidVersion:\n 'Migration version must be a non-negative integer ≤ 9999, got {{value}}',\n\n applyFailed:\n 'Migration {{name}} failed: {{reason}}',\n} as const;\n","/**\n * Plugin migrations runner.\n *\n * Mirrors the kernel migrations runner (`migrations.ts`) but scopes its\n * ledger writes to `(scope='plugin', owner_id=<pluginId>)` and gates\n * every applied SQL through the triple-protection validator\n * (`plugin-migrations-validator.ts`).\n *\n * Scope rule: only plugins with `storage.mode === 'dedicated'` ship\n * migrations. Plugins with no storage or `storage.mode === 'kv'` are\n * skipped silently — the kernel-owned `state_plugin_kvs` table is\n * already there. A `dedicated` plugin without a `migrations/` folder is\n * a config error and surfaces as `0 pending` (the apply call is a no-op).\n */\n\nimport { existsSync, readFileSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport type { IDiscoveredPlugin } from '../../types/plugin.js';\nimport type {\n IPluginApplyOptions,\n IPluginApplyResult,\n IPluginMigrationFile,\n IPluginMigrationPlan,\n IPluginMigrationRecord,\n} from '../../types/storage.js';\nimport {\n detectCatalogIntrusion,\n normalizePluginId,\n snapshotCatalog,\n validatePluginMigrationSql,\n} from './plugin-migrations-validator.js';\n\nexport type {\n IPluginApplyOptions,\n IPluginApplyResult,\n IPluginMigrationFile,\n IPluginMigrationPlan,\n IPluginMigrationRecord,\n} from '../../types/storage.js';\n\nconst FILE_RE = /^(\\d{3})_([a-z0-9_]+)\\.sql$/;\n\n/**\n * Resolve the absolute migrations directory for a discovered plugin.\n * Returns `null` if the plugin is not in `dedicated` storage mode (which\n * is the only mode that ships migrations) or the directory doesn't\n * exist.\n */\nexport function resolvePluginMigrationsDir(plugin: IDiscoveredPlugin): string | null {\n const manifest = plugin.manifest;\n if (!manifest) return null;\n if (!manifest.storage) return null;\n if (manifest.storage.mode !== 'dedicated') return null;\n const dir = join(plugin.path, 'migrations');\n if (!existsSync(dir)) return null;\n return dir;\n}\n\n/**\n * Discover the migrations a plugin ships, sorted by version. Same\n * `NNN_snake_case.sql` convention as kernel migrations.\n */\nexport function discoverPluginMigrations(plugin: IDiscoveredPlugin): IPluginMigrationFile[] {\n const dir = resolvePluginMigrationsDir(plugin);\n if (!dir) return [];\n\n const files = readdirSync(dir, { withFileTypes: true })\n .filter((e) => e.isFile())\n .map((e) => e.name)\n .filter((name) => FILE_RE.test(name))\n .sort();\n\n const out: IPluginMigrationFile[] = [];\n for (const name of files) {\n const match = FILE_RE.exec(name);\n if (!match) continue;\n out.push({\n version: Number.parseInt(match[1]!, 10),\n description: match[2]!,\n filePath: join(dir, name),\n });\n }\n\n for (let i = 1; i < out.length; i++) {\n if (out[i]!.version === out[i - 1]!.version) {\n throw new Error(\n `Plugin ${plugin.id}: duplicate migration version ${out[i]!.version} ` +\n `(${out[i - 1]!.filePath} and ${out[i]!.filePath})`,\n );\n }\n }\n return out;\n}\n\n/**\n * Read the ledger for a single plugin's migrations. Empty array when\n * `config_schema_versions` is missing or has no rows for this plugin\n * (fresh DB, or a plugin that has never migrated).\n */\nexport function readPluginLedger(db: DatabaseSync, pluginId: string): IPluginMigrationRecord[] {\n const tableExists = db\n .prepare(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name='config_schema_versions'\",\n )\n .get() as { name: string } | undefined;\n if (!tableExists) return [];\n\n const rows = db\n .prepare(\n `SELECT version, description, applied_at\n FROM config_schema_versions\n WHERE scope='plugin' AND owner_id = ?\n ORDER BY version ASC`,\n )\n .all(pluginId) as Array<{ version: number; description: string; applied_at: number }>;\n\n return rows.map((r) => ({\n version: r.version,\n description: r.description,\n appliedAt: r.applied_at,\n }));\n}\n\n/**\n * Plan a plugin's migrations against its ledger.\n */\nexport function planPluginMigrations(\n db: DatabaseSync,\n plugin: IDiscoveredPlugin,\n files: IPluginMigrationFile[] = discoverPluginMigrations(plugin),\n): IPluginMigrationPlan {\n const applied = readPluginLedger(db, plugin.id);\n const appliedVersions = new Set(applied.map((r) => r.version));\n const pending = files.filter((f) => !appliedVersions.has(f.version));\n return { pluginId: plugin.id, applied, pending };\n}\n\n/**\n * Apply pending plugin migrations.\n *\n * Layer 1 (discovery): each pending file is read + validated against\n * the prefix rule before any of them run. A failure here aborts the\n * whole batch with no DB writes.\n *\n * Layer 2 (apply): the same SQL is re-validated immediately before\n * `db.exec(sql)`. Cheap defense against TOCTOU-style edits between\n * discovery and apply.\n *\n * Layer 3 (post-apply): after the batch commits, `sqlite_master` is\n * compared against the pre-batch snapshot. Any new object outside\n * the plugin's prefix is reported as an intrusion. The apply still\n * commits — the intrusion is surfaced to the caller, who decides\n * what to do (the CLI converts it into an error and refuses to\n * advance the ledger; the in-memory contract leaves intrusions\n * visible for richer reporting).\n *\n * Each migration runs inside its own transaction. The ledger row is\n * inserted in the same transaction so a partial failure rolls back\n * cleanly.\n */\n// Plugin migration runner — same shape as `applyMigrations` (per-file\n// transactional apply with rollback) plus the plugin-id ledger\n// scoping. Branching is intrinsic to the safe-apply contract.\n// eslint-disable-next-line complexity\nexport function applyPluginMigrations(\n db: DatabaseSync,\n plugin: IDiscoveredPlugin,\n options: IPluginApplyOptions = {},\n files: IPluginMigrationFile[] = discoverPluginMigrations(plugin),\n): IPluginApplyResult {\n const { dryRun = false } = options;\n const plan = planPluginMigrations(db, plugin, files);\n\n if (plan.pending.length === 0 || dryRun) {\n return { pluginId: plugin.id, applied: dryRun ? plan.pending : [], intrusions: [] };\n }\n\n const normalizedId = normalizePluginId(plugin.id);\n\n // --- Layer 1: validate every pending file BEFORE any run. ----------------\n const sources = new Map<string, string>();\n for (const m of plan.pending) {\n const sql = readFileSync(m.filePath, 'utf8');\n sources.set(m.filePath, sql);\n const result = validatePluginMigrationSql(sql, normalizedId);\n if (!result.ok) {\n throw new Error(\n `Plugin ${plugin.id}: migration ${formatMigrationName(m)} failed validation:\\n` +\n result.violations.map((v) => ` - ${v}`).join('\\n'),\n );\n }\n }\n\n // --- Layer 3 prep: snapshot the catalog. --------------------------------\n const before = snapshotCatalog(db);\n\n const applied: IPluginMigrationFile[] = [];\n for (const migration of plan.pending) {\n const sql = sources.get(migration.filePath)!;\n\n // --- Layer 2: re-validate. -------------------------------------------\n const result = validatePluginMigrationSql(sql, normalizedId);\n if (!result.ok) {\n throw new Error(\n `Plugin ${plugin.id}: migration ${formatMigrationName(migration)} failed Layer-2 validation:\\n` +\n result.violations.map((v) => ` - ${v}`).join('\\n'),\n );\n }\n\n try {\n db.exec('BEGIN');\n db.exec(sql);\n db.prepare(\n `INSERT INTO config_schema_versions (scope, owner_id, version, description, applied_at)\n VALUES ('plugin', ?, ?, ?, ?)`,\n ).run(plugin.id, migration.version, migration.description, Date.now());\n db.exec('COMMIT');\n applied.push(migration);\n } catch (err) {\n try {\n db.exec('ROLLBACK');\n } catch {\n // ignore\n }\n const reason = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Plugin ${plugin.id}: migration ${formatMigrationName(migration)} failed: ${reason}`,\n { cause: err },\n );\n }\n }\n\n // --- Layer 3: catalog assertion. ----------------------------------------\n const after = snapshotCatalog(db);\n const intrusions = detectCatalogIntrusion(before, after, normalizedId);\n\n return { pluginId: plugin.id, applied, intrusions };\n}\n\nfunction formatMigrationName(m: IPluginMigrationFile): string {\n return `${String(m.version).padStart(3, '0')}_${m.description}`;\n}\n","/**\n * Plugin migration SQL validator — triple protection layer.\n *\n * Plugins MAY ship their own SQL migrations (`<plugin-dir>/migrations/`).\n * To keep a malicious or buggy plugin from clobbering kernel state, every\n * DDL object a plugin creates MUST live in the namespace\n * `plugin_<normalizedId>_*`. This module enforces the rule on three\n * layers:\n *\n * Layer 1 — discovery: every migration file is parsed and validated\n * before any of them run. A bad file aborts the whole\n * plugin's migration batch with no side effects.\n * Layer 2 — apply: the same SQL is re-validated immediately before\n * `db.exec(sql)`, in case the file changed between discovery\n * and apply (long-running session, on-disk edit).\n * Layer 3 — post-apply catalog assertion: after each plugin's batch\n * commits, we sweep `sqlite_master` and verify no objects\n * live outside the prefix were created. This catches edge\n * cases the regex layers might miss (e.g. a SQL feature we\n * didn't anticipate that creates an object).\n *\n * Pragmatic regex implementation: per the Arquitecto's pick, this is a\n * whitelist of allowed DDL forms (CREATE / DROP / ALTER over TABLE,\n * INDEX, TRIGGER, VIEW, plus DML INSERT / UPDATE / DELETE for seed data),\n * with explicit denylist coverage for transaction control and pragmas.\n * Anything not on the whitelist is rejected. The grammar is intentionally\n * narrow because plugins are small and migrations should be auditable.\n *\n * Comment handling: SQL line comments (`-- ...`) and block comments\n * (`/* ... */`) are stripped before any other processing. The ZWSP\n * (U+200B) inside the close fence above is intentional — without it\n * the docstring's own block-comment delimiter would close prematurely.\n * A clever\n * attacker who hides DDL inside a comment is defeated by stripping\n * first; once stripped, the hidden DDL becomes visible to the regex.\n *\n * No external dependency. No SQL parser. No tokenizer. Heuristics only,\n * but defended by Layer 3 for everything the heuristics miss.\n */\n\nimport type { DatabaseSync } from 'node:sqlite';\n\n/**\n * Normalize a plugin id into the form used as a table-prefix segment.\n *\n * Rule (from `spec/db-schema.md`): lowercase, replace any character\n * outside `[a-z0-9]` with `_`, collapse runs of `_`, strip leading and\n * trailing `_`.\n *\n * Example: `My-Plugin@v2` → `my_plugin_v2`.\n *\n * Two distinct plugin ids that normalise to the same string are a\n * load-time error — see `assertNoNormalizationCollisions`.\n */\nexport function normalizePluginId(id: string): string {\n return id\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '');\n}\n\n/** Detects collisions when two distinct plugin ids share a normalized form. */\nexport function assertNoNormalizationCollisions(ids: string[]): void {\n const seen = new Map<string, string>();\n for (const id of ids) {\n const normalized = normalizePluginId(id);\n const prior = seen.get(normalized);\n if (prior !== undefined && prior !== id) {\n throw new Error(\n `Plugin id normalization collision: \"${prior}\" and \"${id}\" both normalize to \"${normalized}\"`,\n );\n }\n seen.set(normalized, id);\n }\n}\n\nexport interface IValidationResult {\n ok: boolean;\n /** Human-readable issues; empty when ok=true. */\n violations: string[];\n}\n\n/**\n * Strip SQL comments. Block comments first (greedy across lines), then\n * line comments to end-of-line.\n *\n * Note: this does not respect comments inside string literals, so an\n * unusual identifier like `\"foo--bar\"` (double-quoted with embedded\n * dashes) could lose characters. Plugin authors who need that level\n * of escaping are expected to file an issue; for the v0.5.0 surface,\n * we tolerate the limitation. The catalog assertion (Layer 3) catches\n * any object that slips through.\n */\nexport function stripComments(sql: string): string {\n return sql\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, ' ')\n .replace(/--[^\\n\\r]*/g, ' ');\n}\n\n/**\n * Detect SQL whose validator-visible text (post `stripComments`) would\n * differ from the engine-visible text because a `--` or `/*` sits inside\n * a string literal. Returns `null` when the SQL is safe; otherwise a\n * violation message that the validator surfaces verbatim.\n *\n * Closes the lane where a hostile plugin shifts the validator's view\n * of statement boundaries by smuggling comment markers inside literals\n * (the validator sees one stripped statement, `db.exec` runs the\n * original — see audit finding M5).\n */\n// Char-by-char state machine (4 quoting modes + 2 marker checks). Each\n// branch is one state transition; splitting per mode would scatter the\n// dispatcher and obscure the literal-tracking invariant.\n// eslint-disable-next-line complexity\nexport function detectCommentMarkerInLiteral(sql: string): string | null {\n let inSingle = false;\n let inDouble = false;\n let inBacktick = false;\n let inBracket = false;\n for (let i = 0; i < sql.length; i++) {\n const ch = sql[i]!;\n const next = sql[i + 1];\n if (inSingle) {\n if (ch === \"'\" && next === \"'\") { i++; continue; }\n if (ch === \"'\") { inSingle = false; continue; }\n if (ch === '-' && next === '-') {\n return \"string literal contains '--' (line comment marker). Reject — validator and engine would disagree on statement boundaries.\";\n }\n if (ch === '/' && next === '*') {\n return \"string literal contains '/*' (block comment marker). Reject — validator and engine would disagree on statement boundaries.\";\n }\n continue;\n }\n if (inDouble) {\n if (ch === '\"') { inDouble = false; continue; }\n if (ch === '-' && next === '-') {\n return \"double-quoted identifier contains '--' (line comment marker). Reject — validator and engine would disagree on statement boundaries.\";\n }\n if (ch === '/' && next === '*') {\n return \"double-quoted identifier contains '/*' (block comment marker). Reject — validator and engine would disagree on statement boundaries.\";\n }\n continue;\n }\n if (inBacktick) {\n if (ch === '`') inBacktick = false;\n continue;\n }\n if (inBracket) {\n if (ch === ']') inBracket = false;\n continue;\n }\n if (ch === \"'\") { inSingle = true; continue; }\n if (ch === '\"') { inDouble = true; continue; }\n if (ch === '`') { inBacktick = true; continue; }\n if (ch === '[') { inBracket = true; continue; }\n }\n return null;\n}\n\n/** Tokens that abort validation immediately — too dangerous in plugin space. */\nconst FORBIDDEN_KEYWORDS = [\n /\\bBEGIN\\b/i,\n /\\bCOMMIT\\b/i,\n /\\bROLLBACK\\b/i,\n /\\bSAVEPOINT\\b/i,\n /\\bATTACH\\b/i,\n /\\bDETACH\\b/i,\n /\\bPRAGMA\\b/i,\n /\\bVACUUM\\b/i,\n /\\bREINDEX\\b/i,\n /\\bANALYZE\\b/i,\n];\n\n/**\n * Allowed DDL / DML statement shapes. Each entry captures the object\n * name(s) the statement touches; the validator then checks each name\n * against the plugin's prefix.\n *\n * Object names tolerate the three SQLite identifier forms: bare,\n * double-quoted, backticked, square-bracketed. The capture group strips\n * the wrapping in `objectName()` below.\n *\n * Schema qualifiers (`main.`, `temp.`) are matched but rejected during\n * name normalization — a plugin migration MUST live in the default\n * `main` schema, qualified or not. `temp.*` and attached schemas are\n * rejected because they bypass the per-DB lifecycle.\n */\nconst STATEMENT_PATTERNS: Array<{ kind: string; re: RegExp; targets: ('first' | 'on')[] }> = [\n // CREATE [TEMP|TEMPORARY] [VIRTUAL] TABLE [IF NOT EXISTS] <name>\n // CREATE [TEMP|TEMPORARY] [UNIQUE] INDEX [IF NOT EXISTS] <name> ON <table>\n // CREATE [TEMP|TEMPORARY] TRIGGER [IF NOT EXISTS] <name>\n // CREATE [TEMP|TEMPORARY] VIEW [IF NOT EXISTS] <name>\n {\n kind: 'CREATE TABLE',\n re: /^\\s*CREATE(?:\\s+(?:TEMP|TEMPORARY))?(?:\\s+VIRTUAL)?\\s+TABLE(?:\\s+IF\\s+NOT\\s+EXISTS)?\\s+(\\S+)/i,\n targets: ['first'],\n },\n {\n kind: 'CREATE INDEX',\n re: /^\\s*CREATE(?:\\s+(?:TEMP|TEMPORARY))?(?:\\s+UNIQUE)?\\s+INDEX(?:\\s+IF\\s+NOT\\s+EXISTS)?\\s+(\\S+)\\s+ON\\s+(\\S+)/i,\n targets: ['first', 'on'],\n },\n {\n kind: 'CREATE TRIGGER',\n re: /^\\s*CREATE(?:\\s+(?:TEMP|TEMPORARY))?\\s+TRIGGER(?:\\s+IF\\s+NOT\\s+EXISTS)?\\s+(\\S+)/i,\n targets: ['first'],\n },\n {\n kind: 'CREATE VIEW',\n re: /^\\s*CREATE(?:\\s+(?:TEMP|TEMPORARY))?\\s+VIEW(?:\\s+IF\\s+NOT\\s+EXISTS)?\\s+(\\S+)/i,\n targets: ['first'],\n },\n // ALTER TABLE <name> RENAME / ADD / DROP ...\n {\n kind: 'ALTER TABLE',\n re: /^\\s*ALTER\\s+TABLE\\s+(\\S+)/i,\n targets: ['first'],\n },\n // DROP TABLE / INDEX / TRIGGER / VIEW [IF EXISTS] <name>\n {\n kind: 'DROP TABLE',\n re: /^\\s*DROP\\s+TABLE(?:\\s+IF\\s+EXISTS)?\\s+(\\S+)/i,\n targets: ['first'],\n },\n {\n kind: 'DROP INDEX',\n re: /^\\s*DROP\\s+INDEX(?:\\s+IF\\s+EXISTS)?\\s+(\\S+)/i,\n targets: ['first'],\n },\n {\n kind: 'DROP TRIGGER',\n re: /^\\s*DROP\\s+TRIGGER(?:\\s+IF\\s+EXISTS)?\\s+(\\S+)/i,\n targets: ['first'],\n },\n {\n kind: 'DROP VIEW',\n re: /^\\s*DROP\\s+VIEW(?:\\s+IF\\s+EXISTS)?\\s+(\\S+)/i,\n targets: ['first'],\n },\n // DML over plugin tables: seed inserts, defensive cleanups.\n // INSERT INTO <name> ... / UPDATE <name> ... / DELETE FROM <name>\n {\n kind: 'INSERT',\n re: /^\\s*INSERT(?:\\s+OR\\s+\\w+)?\\s+INTO\\s+(\\S+)/i,\n targets: ['first'],\n },\n {\n kind: 'UPDATE',\n re: /^\\s*UPDATE\\s+(\\S+)/i,\n targets: ['first'],\n },\n {\n kind: 'DELETE',\n re: /^\\s*DELETE\\s+FROM\\s+(\\S+)/i,\n targets: ['first'],\n },\n];\n\n/**\n * Strip identifier wrapping (double-quote / backtick / square bracket)\n * and any schema qualifier (`main.`, `temp.`, etc.). Returns the\n * normalized identifier or `null` if the schema qualifier is anything\n * other than the default `main`.\n */\n// eslint-disable-next-line complexity\nexport function objectName(token: string): { name: string; schema: string | null } | null {\n // Strip everything from the first opening paren onward — handles\n // `CREATE TABLE name(col INTEGER)` where the captured token has no\n // whitespace between the name and the column list.\n let raw = token;\n const parenIdx = raw.indexOf('(');\n if (parenIdx !== -1) raw = raw.slice(0, parenIdx);\n // Strip trailing punctuation that follows the identifier in some\n // grammars (e.g. `name,`, `name;`).\n raw = raw.replace(/[(),;]+$/g, '');\n let schema: string | null = null;\n\n // Look for a schema qualifier: `<schema>.<name>`.\n const dotIdx = raw.indexOf('.');\n if (dotIdx !== -1) {\n schema = raw.slice(0, dotIdx).toLowerCase();\n raw = raw.slice(dotIdx + 1);\n }\n\n // Strip wrappers.\n if (raw.startsWith('\"') && raw.endsWith('\"')) raw = raw.slice(1, -1);\n else if (raw.startsWith('`') && raw.endsWith('`')) raw = raw.slice(1, -1);\n else if (raw.startsWith('[') && raw.endsWith(']')) raw = raw.slice(1, -1);\n\n if (raw.length === 0) return null;\n return { name: raw, schema };\n}\n\n/**\n * Split a SQL string into statements on top-level semicolons.\n *\n * Respects single-quote strings (with `''` escape), double-quote\n * identifiers, backtick identifiers, and square-bracket identifiers.\n * Block comments and line comments must be stripped before calling\n * this function — `stripComments` does that.\n *\n * Trailing empty / whitespace-only statements are dropped so the caller\n * can iterate without filtering.\n */\n// Char-by-char state machine (5 quoting modes + ';' splitting). Each\n// branch is a single state transition; splitting per mode would make\n// the state machine harder to read, not easier.\n// eslint-disable-next-line complexity\nexport function splitStatements(sql: string): string[] {\n const out: string[] = [];\n let current = '';\n let inSingle = false;\n let inDouble = false;\n let inBacktick = false;\n let inBracket = false;\n\n for (let i = 0; i < sql.length; i++) {\n const ch = sql[i]!;\n\n if (inSingle) {\n current += ch;\n if (ch === \"'\" && sql[i + 1] === \"'\") {\n current += \"'\";\n i++;\n } else if (ch === \"'\") {\n inSingle = false;\n }\n continue;\n }\n if (inDouble) {\n current += ch;\n if (ch === '\"') inDouble = false;\n continue;\n }\n if (inBacktick) {\n current += ch;\n if (ch === '`') inBacktick = false;\n continue;\n }\n if (inBracket) {\n current += ch;\n if (ch === ']') inBracket = false;\n continue;\n }\n\n if (ch === \"'\") {\n inSingle = true;\n current += ch;\n continue;\n }\n if (ch === '\"') {\n inDouble = true;\n current += ch;\n continue;\n }\n if (ch === '`') {\n inBacktick = true;\n current += ch;\n continue;\n }\n if (ch === '[') {\n inBracket = true;\n current += ch;\n continue;\n }\n\n if (ch === ';') {\n const trimmed = current.trim();\n if (trimmed.length > 0) out.push(trimmed);\n current = '';\n continue;\n }\n\n current += ch;\n }\n\n const tail = current.trim();\n if (tail.length > 0) out.push(tail);\n return out;\n}\n\n/**\n * Validate one plugin migration's SQL against the prefix rule. Returns\n * a list of violation strings (empty when valid).\n *\n * Each statement must (a) match a whitelisted shape, (b) target only\n * objects whose name starts with `plugin_<normalizedId>_`, (c) live in\n * the default schema (no `temp.*`, no attached-DB references), and (d)\n * not contain a forbidden keyword (transaction control, pragma, etc.).\n */\n// 4 validation layers (forbidden keywords + per-statement shape + name\n// prefix + cross-schema references); each is its own branch.\n// eslint-disable-next-line complexity\nexport function validatePluginMigrationSql(sql: string, normalizedId: string): IValidationResult {\n const violations: string[] = [];\n const prefix = `plugin_${normalizedId}_`;\n\n // Pre-check: reject any literal that contains a comment marker. If we\n // skip this, `stripComments` mutates the validator's view of the\n // statement while `db.exec` still runs the original — opening a\n // boundary-shifting attack (see audit finding M5).\n const literalIssue = detectCommentMarkerInLiteral(sql);\n if (literalIssue) {\n return { ok: false, violations: [literalIssue] };\n }\n\n const stripped = stripComments(sql);\n\n for (const re of FORBIDDEN_KEYWORDS) {\n if (re.test(stripped)) {\n violations.push(\n `forbidden keyword: matches /${re.source}/. Plugin migrations cannot manage transactions, pragmas, or attached databases.`,\n );\n }\n }\n\n for (const stmt of splitStatements(stripped)) {\n let matched: { kind: string; tokens: string[] } | null = null;\n for (const pattern of STATEMENT_PATTERNS) {\n const m = pattern.re.exec(stmt);\n if (!m) continue;\n const tokens: string[] = [];\n for (let j = 1; j < m.length; j++) tokens.push(m[j]!);\n matched = { kind: pattern.kind, tokens };\n break;\n }\n\n if (!matched) {\n violations.push(`unsupported statement: ${truncate(stmt, 80)}`);\n continue;\n }\n\n for (const tok of matched.tokens) {\n const parsed = objectName(tok);\n if (!parsed) {\n violations.push(`${matched.kind}: could not parse object name from \"${tok}\"`);\n continue;\n }\n if (parsed.schema !== null && parsed.schema !== 'main') {\n violations.push(\n `${matched.kind}: schema qualifier \"${parsed.schema}.\" not allowed (must be unqualified or \"main.\")`,\n );\n continue;\n }\n if (!parsed.name.startsWith(prefix)) {\n violations.push(\n `${matched.kind}: object \"${parsed.name}\" is outside the plugin's namespace (\"${prefix}*\")`,\n );\n }\n }\n }\n\n return { ok: violations.length === 0, violations };\n}\n\n/**\n * Layer 3 — post-apply catalog assertion. After a plugin's migration\n * batch commits, sweep `sqlite_master` for any object NOT in the\n * `plugin_<normalizedId>_*` prefix that wasn't there before. We compare\n * against a snapshot taken before the batch ran.\n *\n * Returns an empty array when clean; otherwise a list of object names\n * that should not exist. The caller decides what to do (we recommend\n * raising an error and refusing to advance the ledger).\n */\nexport function detectCatalogIntrusion(\n before: Set<string>,\n after: Set<string>,\n normalizedId: string,\n): string[] {\n const prefix = `plugin_${normalizedId}_`;\n const intrusions: string[] = [];\n for (const name of after) {\n if (before.has(name)) continue; // pre-existing\n if (name.startsWith(prefix)) continue; // legitimate plugin object\n if (name.startsWith('sqlite_')) continue; // SQLite internal\n intrusions.push(name);\n }\n return intrusions;\n}\n\n/**\n * Read every user-visible object name from `sqlite_master`. Filters\n * out auto-generated indexes (those start with `sqlite_autoindex_`)\n * because they shadow whatever table they belong to and don't have an\n * independent author. Plugins that want their own indexes must `CREATE\n * INDEX` them explicitly.\n */\nexport function snapshotCatalog(db: DatabaseSync): Set<string> {\n const rows = db\n .prepare(\n `SELECT name FROM sqlite_master\n WHERE type IN ('table', 'index', 'trigger', 'view')\n AND name NOT LIKE 'sqlite_autoindex_%'`,\n )\n .all() as Array<{ name: string }>;\n return new Set(rows.map((r) => r.name));\n}\n\nfunction truncate(s: string, max: number): string {\n if (s.length <= max) return s.replace(/\\s+/g, ' ');\n return s.slice(0, max).replace(/\\s+/g, ' ') + '…';\n}\n","/**\n * Storage helpers for the `config_plugins` table — persists the user's\n * enable/disable overrides for discovered plugins. Read-side feeds\n * `sm plugins list/show/doctor`; write-side feeds\n * `sm plugins enable/disable`.\n *\n * The table schema is shipped in the kernel's initial migration (see\n * `src/migrations/001_initial.sql`). This module only adds the helpers.\n */\n\nimport type { Kysely, Transaction } from 'kysely';\n\nimport type { IDatabase } from './schema.js';\nimport type { IPluginConfigRow } from '../../types/storage.js';\n\nexport type { IPluginConfigRow } from '../../types/storage.js';\n\ntype DbOrTx = Kysely<IDatabase> | Transaction<IDatabase>;\n\n/**\n * Upsert a single `config_plugins` row. `now` defaults to `Date.now()`\n * when omitted.\n */\nexport async function setPluginEnabled(\n db: DbOrTx,\n pluginId: string,\n enabled: boolean,\n now: number = Date.now(),\n): Promise<void> {\n await db\n .insertInto('config_plugins')\n .values({\n pluginId,\n enabled: enabled ? 1 : 0,\n configJson: null,\n updatedAt: now,\n })\n .onConflict((oc) =>\n oc.column('pluginId').doUpdateSet({\n enabled: enabled ? 1 : 0,\n updatedAt: now,\n }),\n )\n .execute();\n}\n\n/**\n * Fetch the enabled override for one plugin id. Returns `undefined`\n * when the user has not set an override (the caller falls back to\n * `settings.json` → installed default).\n */\nexport async function getPluginEnabled(\n db: DbOrTx,\n pluginId: string,\n): Promise<boolean | undefined> {\n const row = await db\n .selectFrom('config_plugins')\n .select(['enabled'])\n .where('pluginId', '=', pluginId)\n .executeTakeFirst();\n if (!row) return undefined;\n return row.enabled === 1;\n}\n\n/** List every override row. Useful for `sm plugins list`. */\nexport async function listPluginOverrides(db: DbOrTx): Promise<IPluginConfigRow[]> {\n const rows = await db\n .selectFrom('config_plugins')\n .select(['pluginId', 'enabled', 'configJson', 'updatedAt'])\n .orderBy('pluginId', 'asc')\n .execute();\n return rows.map((r) => ({\n pluginId: r.pluginId,\n enabled: r.enabled === 1,\n configJson: r.configJson,\n updatedAt: r.updatedAt,\n }));\n}\n\n/**\n * Drop the user override for one plugin so the next resolution falls\n * back to `settings.json` → installed default. Idempotent — removing a\n * non-existent row is a no-op.\n */\nexport async function deletePluginOverride(\n db: DbOrTx,\n pluginId: string,\n): Promise<void> {\n await db\n .deleteFrom('config_plugins')\n .where('pluginId', '=', pluginId)\n .execute();\n}\n\n/**\n * Fetch every override at once and return a `Map<pluginId, enabled>`.\n * `PluginLoader` consumers use this once per process to avoid one\n * round-trip per plugin during discovery.\n */\nexport async function loadPluginOverrideMap(\n db: DbOrTx,\n): Promise<Map<string, boolean>> {\n const rows = await listPluginOverrides(db);\n const out = new Map<string, boolean>();\n for (const row of rows) out.set(row.pluginId, row.enabled);\n return out;\n}\n","/**\n * Runtime guards for the closed-enum domain types (`Stability`,\n * `LinkKind`, `Confidence`, `Severity`, `TExecutionMode`,\n * `ExecutionRunner`, `ExecutionFailureReason`). Used at the storage\n * boundary (`scan-load.ts` row → domain conversion) and by any other\n * adapter that needs to coerce raw column strings into the kernel's\n * union types.\n *\n * Two flavors per enum:\n *\n * - `is<Name>(s) → s is <Name>` — type guard that the input is a\n * valid member of the union. No throw.\n * - `parse<Name>(s, ctx) → <Name>` — narrowing parser. `s` MUST be a\n * known value; an unknown one throws with a clear diagnostic that\n * names the offending value, the allowed set, and the caller's\n * `ctx` (typically a row id / column / file path) so the error is\n * actionable. The caller decides whether `null` is allowed by\n * branching on it before invoking.\n *\n * Why throw instead of coercing to a default: the kernel's read path\n * is a faithful inverse of the write path. If a row carries a value\n * outside the closed union, either the DB was modified out-of-band by\n * a different tool (fail loud, the user wants to know) or the kernel\n * itself just shipped a bug that wrote a bad value (fail loud — the\n * sooner the better). Silent coercion masks both cases.\n *\n * `Node.kind` is intentionally NOT covered here — it is open by spec\n * (`node.schema.json` accepts any non-empty string), and external\n * Providers freely return their own kinds.\n */\n\nimport type {\n Confidence,\n ExecutionFailureReason,\n ExecutionRunner,\n ExecutionStatus,\n LinkKind,\n Severity,\n Stability,\n TExecutionMode,\n} from '../types.js';\n\nconst STABILITY_VALUES: readonly Stability[] = Object.freeze([\n 'experimental',\n 'stable',\n 'deprecated',\n]);\n\nconst LINK_KIND_VALUES: readonly LinkKind[] = Object.freeze([\n 'invokes',\n 'references',\n 'mentions',\n 'supersedes',\n]);\n\nconst CONFIDENCE_VALUES: readonly Confidence[] = Object.freeze([\n 'high',\n 'medium',\n 'low',\n]);\n\nconst SEVERITY_VALUES: readonly Severity[] = Object.freeze([\n 'error',\n 'warn',\n 'info',\n]);\n\nconst EXECUTION_MODE_VALUES: readonly TExecutionMode[] = Object.freeze([\n 'deterministic',\n 'probabilistic',\n]);\n\nconst EXECUTION_RUNNER_VALUES: readonly ExecutionRunner[] = Object.freeze([\n 'cli',\n 'skill',\n 'in-process',\n]);\n\nconst EXECUTION_STATUS_VALUES: readonly ExecutionStatus[] = Object.freeze([\n 'completed',\n 'failed',\n 'cancelled',\n]);\n\nconst EXECUTION_FAILURE_REASON_VALUES: readonly ExecutionFailureReason[] = Object.freeze([\n 'runner-error',\n 'report-invalid',\n 'timeout',\n 'abandoned',\n 'job-file-missing',\n 'user-cancelled',\n]);\n\nexport function isStability(s: unknown): s is Stability {\n return typeof s === 'string' && (STABILITY_VALUES as readonly string[]).includes(s);\n}\n\nexport function isLinkKind(s: unknown): s is LinkKind {\n return typeof s === 'string' && (LINK_KIND_VALUES as readonly string[]).includes(s);\n}\n\nexport function isConfidence(s: unknown): s is Confidence {\n return typeof s === 'string' && (CONFIDENCE_VALUES as readonly string[]).includes(s);\n}\n\nexport function isSeverity(s: unknown): s is Severity {\n return typeof s === 'string' && (SEVERITY_VALUES as readonly string[]).includes(s);\n}\n\nexport function isExecutionMode(s: unknown): s is TExecutionMode {\n return typeof s === 'string' && (EXECUTION_MODE_VALUES as readonly string[]).includes(s);\n}\n\nexport function isExecutionRunner(s: unknown): s is ExecutionRunner {\n return typeof s === 'string' && (EXECUTION_RUNNER_VALUES as readonly string[]).includes(s);\n}\n\nexport function isExecutionStatus(s: unknown): s is ExecutionStatus {\n return typeof s === 'string' && (EXECUTION_STATUS_VALUES as readonly string[]).includes(s);\n}\n\nexport function isExecutionFailureReason(s: unknown): s is ExecutionFailureReason {\n return typeof s === 'string' && (EXECUTION_FAILURE_REASON_VALUES as readonly string[]).includes(s);\n}\n\nexport function parseStability(s: unknown, ctx: string): Stability {\n if (isStability(s)) return s;\n throw new Error(\n `Invalid Stability value ${formatValue(s)} at ${ctx}. Allowed: ${STABILITY_VALUES.join(' | ')}.`,\n );\n}\n\nexport function parseLinkKind(s: unknown, ctx: string): LinkKind {\n if (isLinkKind(s)) return s;\n throw new Error(\n `Invalid LinkKind value ${formatValue(s)} at ${ctx}. Allowed: ${LINK_KIND_VALUES.join(' | ')}.`,\n );\n}\n\nexport function parseConfidence(s: unknown, ctx: string): Confidence {\n if (isConfidence(s)) return s;\n throw new Error(\n `Invalid Confidence value ${formatValue(s)} at ${ctx}. Allowed: ${CONFIDENCE_VALUES.join(' | ')}.`,\n );\n}\n\nexport function parseSeverity(s: unknown, ctx: string): Severity {\n if (isSeverity(s)) return s;\n throw new Error(\n `Invalid Severity value ${formatValue(s)} at ${ctx}. Allowed: ${SEVERITY_VALUES.join(' | ')}.`,\n );\n}\n\nexport function parseExecutionRunner(s: unknown, ctx: string): ExecutionRunner {\n if (isExecutionRunner(s)) return s;\n throw new Error(\n `Invalid ExecutionRunner value ${formatValue(s)} at ${ctx}. Allowed: ${EXECUTION_RUNNER_VALUES.join(' | ')}.`,\n );\n}\n\nexport function parseExecutionFailureReason(s: unknown, ctx: string): ExecutionFailureReason {\n if (isExecutionFailureReason(s)) return s;\n throw new Error(\n `Invalid ExecutionFailureReason value ${formatValue(s)} at ${ctx}. Allowed: ${EXECUTION_FAILURE_REASON_VALUES.join(' | ')}.`,\n );\n}\n\nfunction formatValue(s: unknown): string {\n if (typeof s === 'string') return JSON.stringify(s);\n if (s === null) return 'null';\n if (s === undefined) return 'undefined';\n return String(s);\n}\n","/**\n * `loadScanResult` — driving inverse of `persistScanResult`. Reads the\n * `scan_*` tables and reconstructs a `ScanResult` shape so the\n * orchestrator can run an incremental scan (`sm scan --changed`) on\n * top of a prior snapshot.\n *\n * The reconstruction is faithful for everything that was actually\n * persisted: nodes (with triple-split bytes / tokens, denormalised\n * counts, JSON frontmatter), internal links (with regrouped\n * `trigger` / `location`, parsed `sources[]`), and issues\n * (with parsed `nodeIds` / `linkIndices` / `fix` / `data`).\n *\n * **Documented omission**: external pseudo-links (those whose target is\n * an `http://` / `https://` URL emitted by the external-url-counter\n * extractor) are NEVER persisted to `scan_links` — only their per-node\n * count survives in `scan_nodes.external_refs_count`. Therefore the\n * `result.links` returned by `loadScanResult` contains only internal\n * graph links, and `node.externalRefsCount` is the authoritative count\n * carried over from the prior scan. The orchestrator's incremental path\n * preserves that count for \"unchanged\" nodes and re-derives it for\n * new / modified nodes from a fresh extractor pass.\n *\n * Meta envelope: the `scan_meta` table persists `scope` / `roots` /\n * `scannedAt` / `scannedBy` / `providers` / `stats.filesWalked` /\n * `stats.filesSkipped` / `stats.durationMs`. When the row exists,\n * those fields come back authoritatively. When it does not (DB\n * freshly migrated but never scanned, or a legacy DB never\n * re-persisted), the loader degrades to a synthetic envelope:\n *\n * - `scannedAt` ← max(`scan_nodes.scanned_at`); falls back to `Date.now()`\n * for empty snapshots so the field stays a positive integer.\n * - `scope` ← `'project'`.\n * - `roots` ← `['.']` to satisfy spec's `minItems: 1`. NOT\n * load-bearing: the orchestrator's incremental path only reads\n * `nodes` / `links` / `issues` from the prior; it never reuses the\n * prior `roots`.\n * - `providers` ← `[]`.\n * - `stats` ← zeros for `filesWalked` / `filesSkipped` /\n * `durationMs`; the three count fields derive from row counts.\n *\n * Both branches keep `nodesCount` / `linksCount` / `issuesCount` derived\n * from `COUNT(*)` of the loaded rows — never persisted, always recomputed.\n */\n\nimport type { Kysely } from 'kysely';\n\nimport type { IPersistedEnrichment } from '../../orchestrator.js';\nimport type {\n Issue,\n IssueFix,\n Link,\n LinkLocation,\n LinkTrigger,\n Node,\n ScanResult,\n ScanScannedBy,\n TripleSplit,\n} from '../../types.js';\nimport type {\n IDatabase,\n IScanIssuesTable,\n IScanLinksTable,\n IScanMetaTable,\n IScanNodesTable,\n TScanScope,\n} from './schema.js';\nimport type { Selectable } from 'kysely';\nimport {\n isStability,\n parseConfidence,\n parseLinkKind,\n parseSeverity,\n parseStability,\n} from '../../util/enum-parsers.js';\n\nexport async function loadScanResult(\n db: Kysely<IDatabase>,\n): Promise<ScanResult> {\n const [nodeRows, linkRows, issueRows, metaRow] = await Promise.all([\n db.selectFrom('scan_nodes').selectAll().execute(),\n db.selectFrom('scan_links').selectAll().execute(),\n db.selectFrom('scan_issues').selectAll().execute(),\n db.selectFrom('scan_meta').selectAll().executeTakeFirst(),\n ]);\n\n const nodes = nodeRows.map(rowToNode);\n const links = linkRows.map(rowToLink);\n const issues = issueRows.map(rowToIssue);\n\n if (metaRow) {\n const scannedBy: ScanScannedBy = {\n name: metaRow.scannedByName,\n version: metaRow.scannedByVersion,\n specVersion: metaRow.scannedBySpecVersion,\n };\n return {\n schemaVersion: 1,\n scannedAt: metaRow.scannedAt,\n scope: metaRow.scope as TScanScope,\n roots: parseJsonArray<string>(metaRow.rootsJson),\n providers: parseJsonArray<string>(metaRow.providersJson),\n scannedBy,\n nodes,\n links,\n issues,\n stats: {\n filesWalked: metaRow.statsFilesWalked,\n filesSkipped: metaRow.statsFilesSkipped,\n nodesCount: nodes.length,\n linksCount: links.length,\n issuesCount: issues.length,\n durationMs: metaRow.statsDurationMs,\n },\n };\n }\n\n // Synthetic fallback: pre-5.1 DB or never-scanned scope.\n let scannedAt = 0;\n for (const row of nodeRows) {\n if (row.scannedAt > scannedAt) scannedAt = row.scannedAt;\n }\n if (scannedAt === 0) scannedAt = Date.now();\n\n return {\n schemaVersion: 1,\n scannedAt,\n scope: 'project',\n roots: ['.'],\n providers: [],\n nodes,\n links,\n issues,\n stats: {\n filesWalked: 0,\n filesSkipped: 0,\n nodesCount: nodes.length,\n linksCount: links.length,\n issuesCount: issues.length,\n durationMs: 0,\n },\n };\n}\n\n/**\n * Convert a `scan_nodes` row to its `Node` domain shape. Exported so\n * read-side commands (`sm list`, `sm show`) can reuse the exact mapping\n * used by the incremental scan loader — keeping the two paths byte-aligned\n * with the spec's `node.schema.json`.\n */\nexport function rowToNode(row: Selectable<IScanNodesTable>): Node {\n const bytes: TripleSplit = {\n frontmatter: row.bytesFrontmatter,\n body: row.bytesBody,\n total: row.bytesTotal,\n };\n const stability = row.stability === null\n ? null\n : isStability(row.stability)\n ? row.stability\n : parseStability(row.stability, `scan_nodes.path=${row.path}.stability`);\n const node: Node = {\n path: row.path,\n kind: row.kind,\n provider: row.provider,\n bodyHash: row.bodyHash,\n frontmatterHash: row.frontmatterHash,\n bytes,\n linksOutCount: row.linksOutCount,\n linksInCount: row.linksInCount,\n externalRefsCount: row.externalRefsCount,\n title: row.title,\n description: row.description,\n stability,\n version: row.version,\n author: row.author,\n frontmatter: parseJsonObject(row.frontmatterJson),\n };\n if (\n row.tokensFrontmatter !== null &&\n row.tokensBody !== null &&\n row.tokensTotal !== null\n ) {\n node.tokens = {\n frontmatter: row.tokensFrontmatter,\n body: row.tokensBody,\n total: row.tokensTotal,\n };\n }\n return node;\n}\n\n/**\n * Convert a `scan_links` row to its `Link` domain shape. Exported for\n * read-side reuse (`sm show` lists in/out edges).\n */\nexport function rowToLink(row: Selectable<IScanLinksTable>): Link {\n const ctx = `scan_links source=${row.sourcePath} target=${row.targetPath}`;\n const link: Link = {\n source: row.sourcePath,\n target: row.targetPath,\n kind: parseLinkKind(row.kind, `${ctx}.kind`),\n confidence: parseConfidence(row.confidence, `${ctx}.confidence`),\n sources: parseJsonArray<string>(row.sourcesJson),\n };\n if (row.originalTrigger !== null && row.normalizedTrigger !== null) {\n const trigger: LinkTrigger = {\n originalTrigger: row.originalTrigger,\n normalizedTrigger: row.normalizedTrigger,\n };\n link.trigger = trigger;\n }\n if (row.locationLine !== null) {\n const location: LinkLocation = { line: row.locationLine };\n if (row.locationColumn !== null) location.column = row.locationColumn;\n if (row.locationOffset !== null) location.offset = row.locationOffset;\n link.location = location;\n }\n if (row.raw !== null) link.raw = row.raw;\n return link;\n}\n\n/**\n * Convert a `scan_issues` row to its `Issue` domain shape. Exported for\n * read-side reuse (`sm check` and `sm show`).\n */\nexport function rowToIssue(row: Selectable<IScanIssuesTable>): Issue {\n const issue: Issue = {\n ruleId: row.ruleId,\n severity: parseSeverity(row.severity, `scan_issues ruleId=${row.ruleId}.severity`),\n nodeIds: parseJsonArray<string>(row.nodeIdsJson),\n message: row.message,\n };\n if (row.linkIndicesJson !== null) {\n issue.linkIndices = parseJsonArray<number>(row.linkIndicesJson);\n }\n if (row.detail !== null) issue.detail = row.detail;\n if (row.fixJson !== null) {\n issue.fix = JSON.parse(row.fixJson) as IssueFix;\n }\n if (row.dataJson !== null) {\n issue.data = JSON.parse(row.dataJson) as Record<string, unknown>;\n }\n return issue;\n}\n\n/**\n * Spec § A.9 — load the fine-grained Extractor cache as a per-node map\n * from qualified extractor id (`<pluginId>/<id>`) to the body hash that\n * extractor saw on its last run. Empty map is the default when the table\n * is empty (fresh DB, never-scanned scope, or every extractor has been\n * uninstalled since the last scan).\n *\n * Returned shape: `Map<nodePath, Map<extractorId, bodyHashAtRun>>`. The\n * orchestrator consults it during the walk to decide per-(node, extractor)\n * whether a fresh `extract()` is needed.\n */\nexport async function loadExtractorRuns(\n db: Kysely<IDatabase>,\n): Promise<Map<string, Map<string, string>>> {\n const rows = await db\n .selectFrom('scan_extractor_runs')\n .select(['nodePath', 'extractorId', 'bodyHashAtRun'])\n .execute();\n const result = new Map<string, Map<string, string>>();\n for (const row of rows) {\n let perNode = result.get(row.nodePath);\n if (!perNode) {\n perNode = new Map<string, string>();\n result.set(row.nodePath, perNode);\n }\n perNode.set(row.extractorId, row.bodyHashAtRun);\n }\n return result;\n}\n\n/**\n * Spec § A.8 — load enrichment rows from `node_enrichments`.\n *\n * Returned in the order required by `mergeNodeWithEnrichments` callers:\n * grouped by `nodePath`, then sorted by `enrichedAt` ASC so a spread\n * merge yields last-write-wins per field. Stale rows are included by\n * default — the read-time merge filters them out (the helper takes\n * `includeStale` for the rare UI case that wants to display them).\n *\n * Pass `nodePath` to filter to a single node's enrichments — used by\n * `sm refresh <node>` to read only the rows it intends to refresh, and\n * by `sm show` to render a single node's overlay.\n */\nexport async function loadNodeEnrichments(\n db: Kysely<IDatabase>,\n nodePath?: string,\n): Promise<IPersistedEnrichment[]> {\n let query = db\n .selectFrom('node_enrichments')\n .select([\n 'nodePath',\n 'extractorId',\n 'bodyHashAtEnrichment',\n 'valueJson',\n 'stale',\n 'enrichedAt',\n 'isProbabilistic',\n ])\n .orderBy('nodePath', 'asc')\n .orderBy('enrichedAt', 'asc');\n if (nodePath !== undefined) {\n query = query.where('nodePath', '=', nodePath);\n }\n const rows = await query.execute();\n return rows.map((row) => ({\n nodePath: row.nodePath,\n extractorId: row.extractorId,\n bodyHashAtEnrichment: row.bodyHashAtEnrichment,\n value: parseJsonObject(row.valueJson) as Partial<Node>,\n stale: row.stale === 1,\n enrichedAt: row.enrichedAt,\n isProbabilistic: row.isProbabilistic === 1,\n }));\n}\n\nfunction parseJsonObject(s: string): Record<string, unknown> {\n const parsed = JSON.parse(s) as unknown;\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n return {};\n}\n\nfunction parseJsonArray<T>(s: string): T[] {\n const parsed = JSON.parse(s) as unknown;\n return Array.isArray(parsed) ? (parsed as T[]) : [];\n}\n","/**\n * `persistScanResult` — driven adapter that writes a `ScanResult` into the\n * `scan_*` tables. Replace-all semantics: every scan is a fresh snapshot,\n * so prior rows are deleted before insert. The whole write happens inside\n * a single transaction so a partial failure leaves the DB on the previous\n * snapshot.\n *\n * Incremental scans (`sm scan --changed`) load the prior snapshot,\n * merge unchanged nodes back in, recompute counts, and call this with\n * the merged ScanResult. The replace-all stays — the merge\n * happens upstream.\n *\n * After the transaction commits we run `PRAGMA wal_checkpoint(TRUNCATE)`\n * to force the WAL contents into the main `.db` file and truncate\n * `<db>-wal` to zero bytes. SQLite only auto-checkpoints once the WAL\n * crosses `wal_autocheckpoint` (default 1000 pages); for typical small\n * scans the WAL never crosses that threshold, so the main `.db` lags\n * arbitrarily far behind and external read-only tools (sqlitebrowser,\n * DBeaver) opening the file see stale state. `sm scan` is a single-\n * writer one-shot, so the truncate cost is negligible (~ms on small\n * DBs) and there are no concurrent readers to contend with.\n */\n\nimport { sql, type Insertable, type Kysely, type Transaction } from 'kysely';\n\nimport type {\n IEnrichmentRecord,\n IExtractorRunRecord,\n RenameOp,\n} from '../../orchestrator.js';\nimport type { Issue, Link, Node, ScanResult } from '../../types.js';\nimport { STORAGE_TEXTS } from '../../i18n/storage.texts.js';\nimport { tx } from '../../util/tx.js';\nimport {\n findStrandedStateOrphans,\n migrateNodeFks,\n type IMigrateNodeFksReport,\n} from './history.js';\nimport type {\n IDatabase,\n INodeEnrichmentsTable,\n IScanExtractorRunsTable,\n IScanIssuesTable,\n IScanLinksTable,\n IScanMetaTable,\n IScanNodesTable,\n} from './schema.js';\n\nexport async function persistScanResult(\n db: Kysely<IDatabase>,\n result: ScanResult,\n renameOps: RenameOp[] = [],\n extractorRuns: IExtractorRunRecord[] = [],\n enrichments: IEnrichmentRecord[] = [],\n): Promise<{ renames: IMigrateNodeFksReport[] }> {\n // Spec contract (`scan-result.schema.json#/properties/scannedAt`):\n // Unix milliseconds, integer ≥ 0. The DB column is INTEGER too, so\n // there's nothing to convert — just guard against malformed callers.\n const scannedAt = result.scannedAt;\n if (!Number.isInteger(scannedAt) || scannedAt < 0) {\n throw new Error(\n tx(STORAGE_TEXTS.scanPersistInvalidScannedAt, { value: JSON.stringify(scannedAt) }),\n );\n }\n\n const renames: IMigrateNodeFksReport[] = [];\n await db.transaction().execute(async (trx) => {\n // Migrate state_* FKs FIRST so a failure here rolls back BEFORE the\n // scan zone is wiped. Rename heuristic guarantees ops are all-or-\n // nothing (per `spec/db-schema.md` §Rename detection: \"either all\n // renames land or none do\") — the same tx wraps the whole sequence.\n for (const op of renameOps) {\n const report = await migrateNodeFks(trx, op.from, op.to);\n renames.push(report);\n }\n\n // Orphan persistence. Sweep `state_*` for any node_id\n // not in the new live set and emit an `orphan` issue for it (unless\n // the per-scan rename heuristic already covered it). Without this\n // sweep, a state row stranded by a deletion 2+ scans ago becomes\n // invisible (the `orphan` issue from the deletion-scan disappears\n // with the next replace-all on `scan_issues`), making\n // `sm orphans reconcile` impossible to invoke. Spec language is\n // \"the kernel emits an issue (...) until the user runs `sm orphans\n // reconcile` or accepts the orphan\" — accomplished by re-emitting\n // on every scan as long as the stranded refs persist.\n const livePaths = new Set(result.nodes.map((n) => n.path));\n const knownOrphanPaths = new Set<string>();\n for (const issue of result.issues) {\n if (issue.ruleId !== 'orphan') continue;\n const dataPath = issue.data?.['path'];\n if (typeof dataPath === 'string') knownOrphanPaths.add(dataPath);\n }\n const stranded = await findStrandedStateOrphans(trx, livePaths);\n for (const path of stranded) {\n if (knownOrphanPaths.has(path)) continue;\n result.issues.push({\n ruleId: 'orphan',\n severity: 'info',\n nodeIds: [path],\n message: `Orphan history: ${path} has stranded state_* references but no live node.`,\n data: { path },\n });\n }\n // Keep stats in sync with the augmented issue list so the\n // ScanResult returned by callers (and emitted via `sm scan --json`)\n // reflects what's actually persisted.\n result.stats.issuesCount = result.issues.length;\n\n await replaceAllScanZone(trx, result, scannedAt, extractorRuns);\n\n // --- A.8 enrichment layer -----------------------------------------------\n // Universal enrichment table is NOT replace-all — probabilistic rows\n // must survive across scans (preserving the LLM cost). The flow is:\n //\n // 1. Drop rows whose `node_path` is no longer in the live set\n // (the file disappeared and rename migration didn't claim it —\n // replace-all already handled the equivalent on `scan_nodes`).\n // 2. Migrate `node_path` for high/medium-confidence renames so the\n // enrichment audit trail tracks the file like `state_*` rows do.\n // 3. Upsert one row per `(nodePath, extractorId)` pair from this\n // scan's `enrichments[]`. Conflict on the PRIMARY KEY pisar the\n // prior row (body / value / stale all refresh to current).\n // 4. Sweep probabilistic rows: any prob row whose\n // `body_hash_at_enrichment` no longer equals the live node's\n // `body_hash` AND was NOT just upserted → flag `stale = 1`.\n // Deterministic rows are never stale-flagged: they regenerate\n // via the A.9 cache on the next scan and pisar via PK conflict.\n await upsertEnrichmentLayer(trx, result, renameOps, enrichments);\n await flagStaleProbabilisticEnrichments(trx, result, enrichments);\n });\n\n // Force the WAL into the main `.db` file so external read-only tools\n // see the snapshot immediately. Run on the top-level handle, NOT inside\n // the transaction — `wal_checkpoint` is meaningless mid-transaction.\n // `:memory:` doesn't use WAL, so the pragma is a no-op there.\n await sql`PRAGMA wal_checkpoint(TRUNCATE)`.execute(db);\n\n return { renames };\n}\n\n/**\n * Replace-all on the four `scan_*` tables — issues, links, nodes, meta\n * — plus the fine-grained `scan_extractor_runs` cache. Order: deletes\n * in a fixed sequence (no FKs across these tables today, so the order\n * is just for stable query plans), then inserts. `scan_extractor_runs`\n * is reset together so rows for extractors uninstalled since the last\n * scan disappear automatically; the insert below carries forward only\n * the pairs the orchestrator decided to keep (cached) or freshly ran.\n */\nasync function replaceAllScanZone(\n trx: Transaction<IDatabase>,\n result: ScanResult,\n scannedAt: number,\n extractorRuns: IExtractorRunRecord[],\n): Promise<void> {\n await trx.deleteFrom('scan_issues').execute();\n await trx.deleteFrom('scan_links').execute();\n await trx.deleteFrom('scan_nodes').execute();\n await trx.deleteFrom('scan_meta').execute();\n await trx.deleteFrom('scan_extractor_runs').execute();\n\n if (result.nodes.length > 0) {\n await trx\n .insertInto('scan_nodes')\n .values(result.nodes.map((n) => nodeToRow(n, scannedAt)))\n .execute();\n }\n if (result.links.length > 0) {\n await trx\n .insertInto('scan_links')\n .values(result.links.map(linkToRow))\n .execute();\n }\n if (result.issues.length > 0) {\n await trx\n .insertInto('scan_issues')\n .values(result.issues.map(issueToRow))\n .execute();\n }\n await trx.insertInto('scan_meta').values(metaToRow(result)).execute();\n if (extractorRuns.length > 0) {\n await trx\n .insertInto('scan_extractor_runs')\n .values(extractorRuns.map(extractorRunToRow))\n .execute();\n }\n}\n\n/**\n * Steps 2 + 1 + 3 of the A.8 enrichment layer: migrate `node_path` for\n * renames first (so step 1 doesn't delete what step 2 would have\n * preserved), then drop enrichments whose node disappeared, then upsert\n * the fresh enrichment records carried by this scan.\n *\n * Stale-flagging of probabilistic rows is deliberately a separate\n * helper so this function stays focused on the pisar-the-row path.\n */\nasync function upsertEnrichmentLayer(\n trx: Transaction<IDatabase>,\n result: ScanResult,\n renameOps: RenameOp[],\n enrichments: IEnrichmentRecord[],\n): Promise<void> {\n const enrichmentLivePaths = new Set(result.nodes.map((n) => n.path));\n\n // Step 2 — migrate renames before step 1 would delete them.\n for (const op of renameOps) {\n await trx\n .updateTable('node_enrichments')\n .set({ nodePath: op.to })\n .where('nodePath', '=', op.from)\n .execute();\n }\n\n // Step 1 — drop enrichments whose node disappeared.\n if (enrichmentLivePaths.size > 0) {\n const liveList = [...enrichmentLivePaths];\n await trx\n .deleteFrom('node_enrichments')\n .where('nodePath', 'not in', liveList)\n .execute();\n } else {\n await trx.deleteFrom('node_enrichments').execute();\n }\n\n // Step 3 — upsert fresh enrichments. Composite-PK conflict refreshes\n // every non-key column.\n for (const enrichment of enrichments) {\n const row = enrichmentToRow(enrichment);\n await trx\n .insertInto('node_enrichments')\n .values(row)\n .onConflict((oc) =>\n oc.columns(['nodePath', 'extractorId']).doUpdateSet({\n bodyHashAtEnrichment: row.bodyHashAtEnrichment,\n valueJson: row.valueJson,\n stale: row.stale,\n enrichedAt: row.enrichedAt,\n isProbabilistic: row.isProbabilistic,\n }),\n )\n .execute();\n }\n}\n\n/**\n * Step 4 of the A.8 enrichment layer — flag every probabilistic row\n * whose `body_hash_at_enrichment` no longer matches the live node body\n * AND was NOT just upserted by `upsertEnrichmentLayer`. Deterministic\n * rows are never stale-flagged (they regenerate via the A.9 cache on\n * the next scan).\n */\nasync function flagStaleProbabilisticEnrichments(\n trx: Transaction<IDatabase>,\n result: ScanResult,\n enrichments: IEnrichmentRecord[],\n): Promise<void> {\n const refreshedKeys = new Set<string>();\n for (const e of enrichments) {\n refreshedKeys.add(`${e.nodePath}\\x00${e.extractorId}`);\n }\n\n // Probs are sparse (one per LLM-extractor per node), so fetch all\n // and decide in JS — cheap at any practical project size.\n const probRows = await trx\n .selectFrom('node_enrichments')\n .select(['nodePath', 'extractorId', 'bodyHashAtEnrichment', 'stale'])\n .where('isProbabilistic', '=', 1)\n .execute();\n const liveBodyHashByPath = new Map<string, string>();\n for (const node of result.nodes) liveBodyHashByPath.set(node.path, node.bodyHash);\n\n for (const row of probRows) {\n if (refreshedKeys.has(`${row.nodePath}\\x00${row.extractorId}`)) continue;\n const liveBody = liveBodyHashByPath.get(row.nodePath);\n // No live body → already swept by upsertEnrichmentLayer step 1.\n if (liveBody === undefined) continue;\n const shouldBeStale = liveBody !== row.bodyHashAtEnrichment;\n const alreadyStale = row.stale === 1;\n if (shouldBeStale && !alreadyStale) {\n await trx\n .updateTable('node_enrichments')\n .set({ stale: 1 })\n .where('nodePath', '=', row.nodePath)\n .where('extractorId', '=', row.extractorId)\n .execute();\n }\n }\n}\n\n// Pure column mapping: every `??` adds one to the cyclomatic count, so\n// the limit reads as 13 here despite there being zero branching logic.\n// Splitting would replace clarity with ceremony.\n// eslint-disable-next-line complexity\nfunction nodeToRow(node: Node, scannedAt: number): Insertable<IScanNodesTable> {\n return {\n path: node.path,\n kind: node.kind,\n provider: node.provider,\n title: node.title ?? null,\n description: node.description ?? null,\n stability: node.stability ?? null,\n version: node.version ?? null,\n author: node.author ?? null,\n frontmatterJson: JSON.stringify(node.frontmatter ?? {}),\n bodyHash: node.bodyHash,\n frontmatterHash: node.frontmatterHash,\n bytesFrontmatter: node.bytes.frontmatter,\n bytesBody: node.bytes.body,\n bytesTotal: node.bytes.total,\n tokensFrontmatter: node.tokens?.frontmatter ?? null,\n tokensBody: node.tokens?.body ?? null,\n tokensTotal: node.tokens?.total ?? null,\n linksOutCount: node.linksOutCount,\n linksInCount: node.linksInCount,\n externalRefsCount: node.externalRefsCount,\n scannedAt,\n };\n}\n\n// Same rationale as `nodeToRow` — pure column mapping, no branches.\n// eslint-disable-next-line complexity\nfunction linkToRow(link: Link): Insertable<IScanLinksTable> {\n return {\n sourcePath: link.source,\n targetPath: link.target,\n kind: link.kind,\n confidence: link.confidence,\n sourcesJson: JSON.stringify(link.sources),\n originalTrigger: link.trigger?.originalTrigger ?? null,\n normalizedTrigger: link.trigger?.normalizedTrigger ?? null,\n locationLine: link.location?.line ?? null,\n locationColumn: link.location?.column ?? null,\n locationOffset: link.location?.offset ?? null,\n raw: link.raw ?? null,\n };\n}\n\nfunction metaToRow(result: ScanResult): Insertable<IScanMetaTable> {\n return {\n id: 1,\n scope: result.scope,\n rootsJson: JSON.stringify(result.roots),\n scannedAt: result.scannedAt,\n scannedByName: result.scannedBy?.name ?? 'skill-map',\n scannedByVersion: result.scannedBy?.version ?? 'unknown',\n scannedBySpecVersion: result.scannedBy?.specVersion ?? 'unknown',\n providersJson: JSON.stringify(result.providers),\n statsFilesWalked: result.stats.filesWalked,\n statsFilesSkipped: result.stats.filesSkipped,\n statsDurationMs: result.stats.durationMs,\n };\n}\n\nfunction extractorRunToRow(\n record: IExtractorRunRecord,\n): Insertable<IScanExtractorRunsTable> {\n return {\n nodePath: record.nodePath,\n extractorId: record.extractorId,\n bodyHashAtRun: record.bodyHashAtRun,\n ranAt: record.ranAt,\n };\n}\n\nfunction enrichmentToRow(\n record: IEnrichmentRecord,\n): Insertable<INodeEnrichmentsTable> {\n return {\n nodePath: record.nodePath,\n extractorId: record.extractorId,\n bodyHashAtEnrichment: record.bodyHashAtEnrichment,\n valueJson: JSON.stringify(record.value ?? {}),\n stale: 0,\n enrichedAt: record.enrichedAt,\n isProbabilistic: record.isProbabilistic ? 1 : 0,\n };\n}\n\nfunction issueToRow(issue: Issue): Insertable<IScanIssuesTable> {\n return {\n ruleId: issue.ruleId,\n severity: issue.severity,\n nodeIdsJson: JSON.stringify(issue.nodeIds),\n linkIndicesJson:\n issue.linkIndices && issue.linkIndices.length > 0\n ? JSON.stringify(issue.linkIndices)\n : null,\n message: issue.message,\n detail: issue.detail ?? null,\n fixJson: issue.fix ? JSON.stringify(issue.fix) : null,\n dataJson: issue.data ? JSON.stringify(issue.data) : null,\n };\n}\n","import type { StoragePort } from '../../ports/storage.js';\nimport { SqliteStorageAdapter } from './storage-adapter.js';\nimport type { ISqliteStorageAdapterOptions } from './storage-adapter.js';\n\nexport { NodeSqliteDialect } from './dialect.js';\nexport type { INodeSqliteDialectConfig } from './dialect.js';\nexport { SqliteStorageAdapter };\nexport type { ISqliteStorageAdapterOptions };\n\n/**\n * Factory — preferred entry point for production callers (CLI). Returns\n * the `StoragePort` shape so the consumer is pinned to the abstract\n * contract, not the concrete `SqliteStorageAdapter`. Tests that need to\n * access adapter internals continue to use `new SqliteStorageAdapter(...)`\n * directly per the `*-architect` agent's documented exception.\n */\nexport function createSqliteStorage(options: ISqliteStorageAdapterOptions): StoragePort {\n return new SqliteStorageAdapter(options);\n}\n\n/**\n * Adapter-internal Kysely schema types. Re-exported here only for\n * test scaffolding that asserts against raw rows / pragma values\n * (`src/test/storage.test.ts`). CLI consumers MUST go through the\n * `StoragePort` shape — reaching for these is a boundary leak. Tests\n * keep the explicit exception per `AGENTS.md` § Kernel boundaries.\n *\n * Per-table interfaces and the column unions ship from `./schema.ts`\n * directly; test files that need more than `IDatabase` import them\n * from the schema module.\n */\nexport type { IDatabase } from './schema.js';\n","/**\n * `sm config list/get/set/reset/show` — read + mutate `.skill-map/settings.json`.\n *\n * sm config list [--json] [-g] [--strict]\n * sm config get <key.dot.path> [--json] [-g] [--strict]\n * sm config set <key> <value> [-g] — writes to project (default) or user (-g)\n * sm config reset <key> [-g] — removes the key from the same target\n * sm config show <key> [--source] [--json] [-g] [--strict]\n *\n * `--strict` (here and on `sm scan` / `sm init`) escalates every layered-\n * loader warning (malformed JSON, schema violation, unknown key) into a\n * fatal error — the verb exits 2 with a clean stderr line instead of\n * skipping the offending value. Same flag, same semantics across verbs.\n *\n * Read verbs (`list / get / show`) are exempt from elapsed-time per\n * `spec/cli-contract.md` §Elapsed time. Write verbs (`set / reset`) emit\n * `done in <…>` to stderr like every other in-scope verb.\n *\n * `-g` semantics:\n * - on read verbs: loads with scope=global (skips project layers).\n * - on write verbs: writes to `~/.skill-map/settings.json` instead of\n * `<cwd>/.skill-map/settings.json`.\n *\n * Value coercion in `set`: the raw CLI string is JSON-parsed first so the\n * user can pass `true`, `42`, `null`, arrays, and objects naturally;\n * unparseable input falls through as a plain string. The merged file is\n * then re-validated against `project-config.schema.json` — invalid values\n * are rejected (exit 2) without touching the file.\n */\n\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nimport { Command, Option } from 'clipanion';\n\nimport { loadSchemaValidators } from '../../kernel/adapters/schema-validators.js';\nimport {\n loadConfig,\n type IEffectiveConfig,\n type ILoadConfigOptions,\n type ILoadedConfig,\n type TConfigLayer,\n} from '../../kernel/config/loader.js';\nimport { emitDoneStderr, startElapsed } from '../util/elapsed.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport { formatErrorMessage } from '../util/error-reporter.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { CONFIG_TEXTS } from '../i18n/config.texts.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\n\n// -----------------------------------------------------------------------------\n// shared helpers\n// -----------------------------------------------------------------------------\n\ntype TWriteTarget = 'project' | 'user';\n\nfunction targetSettingsPath(target: TWriteTarget, cwd: string, home: string): string {\n const root = target === 'user' ? home : cwd;\n return join(root, '.skill-map', 'settings.json');\n}\n\n/**\n * Path segments that, if walked, would mutate the prototype chain of the\n * current process or the resulting object. Rejected uniformly across\n * `getAtPath` / `setAtPath` / `deleteAtPath` so `sm config <verb>` cannot\n * be coerced into prototype pollution via a hostile dot-path argument.\n */\nconst FORBIDDEN_SEGMENTS = new Set(['__proto__', 'constructor', 'prototype']);\n\nclass ForbiddenSegmentError extends Error {\n constructor(public readonly segment: string, public readonly key: string) {\n super(`forbidden config key segment \"${segment}\" in \"${key}\"`);\n }\n}\n\nfunction assertSafeSegments(segments: string[], key: string): void {\n for (const seg of segments) {\n if (FORBIDDEN_SEGMENTS.has(seg)) throw new ForbiddenSegmentError(seg, key);\n }\n}\n\nfunction getAtPath(obj: unknown, dotPath: string): unknown {\n const segments = dotPath.split('.').filter(Boolean);\n assertSafeSegments(segments, dotPath);\n let cur: unknown = obj;\n for (const seg of segments) {\n if (cur && typeof cur === 'object' && !Array.isArray(cur)) {\n cur = (cur as Record<string, unknown>)[seg];\n continue;\n }\n return undefined;\n }\n return cur;\n}\n\nfunction setAtPath(\n obj: Record<string, unknown>,\n dotPath: string,\n value: unknown,\n): void {\n const segments = dotPath.split('.').filter(Boolean);\n assertSafeSegments(segments, dotPath);\n if (segments.length === 0) return;\n let cur: Record<string, unknown> = obj;\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i]!;\n const next = cur[seg];\n if (!next || typeof next !== 'object' || Array.isArray(next)) {\n cur[seg] = {};\n }\n cur = cur[seg] as Record<string, unknown>;\n }\n cur[segments[segments.length - 1]!] = value;\n}\n\nfunction deleteAtPath(obj: Record<string, unknown>, dotPath: string): boolean {\n const segments = dotPath.split('.').filter(Boolean);\n assertSafeSegments(segments, dotPath);\n if (segments.length === 0) return false;\n let cur: Record<string, unknown> = obj;\n for (let i = 0; i < segments.length - 1; i++) {\n const next = cur[segments[i]!];\n if (!next || typeof next !== 'object' || Array.isArray(next)) return false;\n cur = next as Record<string, unknown>;\n }\n const last = segments[segments.length - 1]!;\n if (!(last in cur)) return false;\n delete cur[last];\n // Walk back up and prune now-empty parent objects so the file stays tidy.\n pruneEmptyAncestors(obj, segments.slice(0, -1));\n return true;\n}\n\nfunction pruneEmptyAncestors(root: Record<string, unknown>, parents: string[]): void {\n while (parents.length > 0) {\n let cur: Record<string, unknown> = root;\n for (let i = 0; i < parents.length - 1; i++) {\n cur = cur[parents[i]!] as Record<string, unknown>;\n }\n const tail = parents[parents.length - 1]!;\n const child = cur[tail];\n if (\n child\n && typeof child === 'object'\n && !Array.isArray(child)\n && Object.keys(child).length === 0\n ) {\n delete cur[tail];\n parents.pop();\n } else {\n break;\n }\n }\n}\n\nfunction parseCliValue(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n\nfunction readJsonObjectOrEmpty(path: string): Record<string, unknown> {\n if (!existsSync(path)) return {};\n try {\n const raw = JSON.parse(readFileSync(path, 'utf8'));\n if (raw && typeof raw === 'object' && !Array.isArray(raw)) {\n return raw as Record<string, unknown>;\n }\n } catch {\n /* fall through to {} */\n }\n return {};\n}\n\n/**\n * Write `content` to `path` atomically. The body is staged into a sibling\n * `<path>.tmp.<pid>` file (same directory so the rename never crosses\n * filesystems) and `renameSync`'d into place — POSIX guarantees rename\n * is atomic on the same fs, so a crash mid-write leaves the destination\n * either at its prior content or at the new content, never half-written.\n *\n * The pre-rename stage is owner-only (`writeFileSync` defaults to the\n * process umask; we do not chmod here because settings.json is not\n * security-critical, and tightening would diverge from `sm init`'s\n * behaviour).\n *\n * On failure the temp file is best-effort removed so we do not leak\n * `<path>.tmp.<pid>` siblings if e.g. the rename target is read-only.\n */\nfunction writeJsonAtomic(path: string, content: Record<string, unknown>): void {\n mkdirSync(dirname(path), { recursive: true });\n const tmp = `${path}.tmp.${process.pid}`;\n try {\n writeFileSync(tmp, JSON.stringify(content, null, 2) + '\\n', 'utf8');\n renameSync(tmp, path);\n } catch (err) {\n try {\n unlinkSync(tmp);\n } catch {\n // Best effort — the staged file may not exist (writeFileSync\n // could have failed before the inode was created).\n }\n throw err;\n }\n}\n\n/**\n * Load layered config catching `--strict` throws so the user sees a\n * clean stderr line + exit 2 instead of Clipanion's default \"Internal\n * Error\" stack trace. Used by every `sm config` read verb.\n */\nfunction tryLoadConfig(\n opts: ILoadConfigOptions,\n stderr: NodeJS.WritableStream,\n): { ok: true; loaded: ILoadedConfig } | { ok: false; exitCode: number } {\n try {\n return { ok: true, loaded: loadConfig(opts) };\n } catch (err) {\n const message = formatErrorMessage(err);\n stderr.write(tx(CONFIG_TEXTS.loadFailure, { message }));\n return { ok: false, exitCode: ExitCode.Error };\n }\n}\n\n// eslint-disable-next-line complexity\nfunction* iterDotPaths(\n obj: unknown,\n prefix = '',\n): Generator<[string, unknown]> {\n if (obj === null || typeof obj !== 'object' || Array.isArray(obj)) {\n if (prefix) yield [prefix, obj];\n return;\n }\n const entries = Object.entries(obj as Record<string, unknown>);\n if (entries.length === 0 && prefix) {\n yield [prefix, obj];\n return;\n }\n for (const [k, v] of entries) {\n const next = prefix ? `${prefix}.${k}` : k;\n yield* iterDotPaths(v, next);\n }\n}\n\nfunction formatValueHuman(v: unknown): string {\n if (v === null) return 'null';\n if (Array.isArray(v) || (typeof v === 'object' && v !== null)) return JSON.stringify(v);\n return String(v);\n}\n\n// -----------------------------------------------------------------------------\n// commands\n// -----------------------------------------------------------------------------\n\nexport class ConfigListCommand extends Command {\n static override paths = [['config', 'list']];\n static override usage = Command.Usage({\n category: 'Config',\n description: 'Print the effective config after layered merge.',\n details: `\n Walks defaults → user → user-local → project → project-local and prints the merged result.\n With --json emits the JSON object; otherwise prints flat dot-path = value lines (sorted).\n Exempt from \"done in <…>\" per spec/cli-contract.md §Elapsed time.\n `,\n });\n\n json = Option.Boolean('--json', false);\n global = Option.Boolean('-g,--global', false);\n strict = Option.Boolean('--strict', false);\n\n async execute(): Promise<number> {\n const result = tryLoadConfig(\n { scope: this.global ? 'global' : 'project', strict: this.strict, ...defaultRuntimeContext() },\n this.context.stderr,\n );\n if (!result.ok) return result.exitCode;\n const { effective, warnings } = result.loaded;\n for (const w of warnings) this.context.stderr.write(w + '\\n');\n if (this.json) {\n this.context.stdout.write(JSON.stringify(effective, null, 2) + '\\n');\n return ExitCode.Ok;\n }\n const lines: string[] = [];\n for (const [k, v] of iterDotPaths(effective)) {\n lines.push(`${k} = ${formatValueHuman(v)}`);\n }\n lines.sort();\n for (const line of lines) this.context.stdout.write(line + '\\n');\n return ExitCode.Ok;\n }\n}\n\nexport class ConfigGetCommand extends Command {\n static override paths = [['config', 'get']];\n static override usage = Command.Usage({\n category: 'Config',\n description: 'Read a single config value by dot-path key.',\n details: `\n Loads the layered config and prints the final value. Unknown key → exit 5.\n Exempt from \"done in <…>\".\n `,\n });\n\n key = Option.String({ required: true });\n json = Option.Boolean('--json', false);\n global = Option.Boolean('-g,--global', false);\n strict = Option.Boolean('--strict', false);\n\n async execute(): Promise<number> {\n const result = tryLoadConfig(\n { scope: this.global ? 'global' : 'project', strict: this.strict, ...defaultRuntimeContext() },\n this.context.stderr,\n );\n if (!result.ok) return result.exitCode;\n const { effective, warnings } = result.loaded;\n for (const w of warnings) this.context.stderr.write(w + '\\n');\n let value: unknown;\n try {\n value = getAtPath(effective, this.key);\n } catch (err) {\n if (err instanceof ForbiddenSegmentError) {\n this.context.stderr.write(tx(CONFIG_TEXTS.forbiddenKeySegment, { segment: err.segment, key: err.key }));\n return ExitCode.Error;\n }\n throw err;\n }\n if (value === undefined) {\n this.context.stderr.write(tx(CONFIG_TEXTS.unknownKey, { key: this.key }));\n return ExitCode.NotFound;\n }\n if (this.json) {\n this.context.stdout.write(JSON.stringify(value) + '\\n');\n return ExitCode.Ok;\n }\n this.context.stdout.write(formatValueHuman(value) + '\\n');\n return ExitCode.Ok;\n }\n}\n\nexport class ConfigShowCommand extends Command {\n static override paths = [['config', 'show']];\n static override usage = Command.Usage({\n category: 'Config',\n description: 'Show a config value with the layer that set it (--source).',\n details: `\n Identical to \"sm config get\" plus optional --source which prefixes the layer\n (defaults / user / user-local / project / project-local / override).\n With --json emits { value, source } when --source is set.\n Exempt from \"done in <…>\".\n `,\n });\n\n key = Option.String({ required: true });\n source = Option.Boolean('--source', false);\n json = Option.Boolean('--json', false);\n global = Option.Boolean('-g,--global', false);\n strict = Option.Boolean('--strict', false);\n\n // CLI orchestrator: each branch (load failure, forbidden segment,\n // unknown key, --json + --source 2x2 dispatch) is one validation gate\n // or output-format pick. Splitting per branch scatters the gate from\n // the value it gates.\n // eslint-disable-next-line complexity\n async execute(): Promise<number> {\n const result = tryLoadConfig(\n { scope: this.global ? 'global' : 'project', strict: this.strict, ...defaultRuntimeContext() },\n this.context.stderr,\n );\n if (!result.ok) return result.exitCode;\n const { effective, sources, warnings } = result.loaded;\n for (const w of warnings) this.context.stderr.write(w + '\\n');\n let value: unknown;\n try {\n value = getAtPath(effective, this.key);\n } catch (err) {\n if (err instanceof ForbiddenSegmentError) {\n this.context.stderr.write(tx(CONFIG_TEXTS.forbiddenKeySegment, { segment: err.segment, key: err.key }));\n return ExitCode.Error;\n }\n throw err;\n }\n if (value === undefined) {\n this.context.stderr.write(tx(CONFIG_TEXTS.unknownKey, { key: this.key }));\n return ExitCode.NotFound;\n }\n const layer = resolveSource(this.key, value, sources);\n if (this.json) {\n const payload = this.source ? { value, source: layer } : value;\n this.context.stdout.write(JSON.stringify(payload) + '\\n');\n return ExitCode.Ok;\n }\n if (this.source) {\n this.context.stdout.write(tx(CONFIG_TEXTS.valueWithLayer, { value: formatValueHuman(value), layer }));\n } else {\n this.context.stdout.write(formatValueHuman(value) + '\\n');\n }\n return ExitCode.Ok;\n }\n}\n\n/**\n * For nested objects (e.g. `scan`), the `sources` map only stores leaf\n * paths. When the user asks about an intermediate path, surface the most\n * \"recent\" layer that touched any descendant (highest precedence wins).\n */\nfunction resolveSource(\n key: string,\n value: unknown,\n sources: Map<string, TConfigLayer>,\n): TConfigLayer {\n const direct = sources.get(key);\n if (direct) return direct;\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n const prefix = key + '.';\n let best: TConfigLayer = 'defaults';\n let bestRank = LAYER_RANK.defaults;\n for (const [k, layer] of sources) {\n if (!k.startsWith(prefix)) continue;\n const rank = LAYER_RANK[layer];\n if (rank > bestRank) {\n bestRank = rank;\n best = layer;\n }\n }\n return best;\n }\n return 'defaults';\n}\n\nconst LAYER_RANK: Record<TConfigLayer, number> = {\n defaults: 0,\n user: 1,\n 'user-local': 2,\n project: 3,\n 'project-local': 4,\n override: 5,\n};\n\nexport class ConfigSetCommand extends Command {\n static override paths = [['config', 'set']];\n static override usage = Command.Usage({\n category: 'Config',\n description: 'Write a config key. Project file by default; -g writes to user.',\n details: `\n Reads the target file (creating it if absent), sets the key at the dot-path,\n validates the result against project-config.schema.json, and writes back.\n Value coercion: JSON-parses the raw string first (\"true\" → true, \"42\" → 42,\n \"null\" → null, arrays / objects natural); unparseable falls through as string.\n Schema violation → exit 2, no write performed.\n `,\n });\n\n key = Option.String({ required: true });\n value = Option.String({ required: true });\n global = Option.Boolean('-g,--global', false);\n\n async execute(): Promise<number> {\n const elapsed = startElapsed();\n const ctx = defaultRuntimeContext();\n const target: TWriteTarget = this.global ? 'user' : 'project';\n const path = targetSettingsPath(target, ctx.cwd, ctx.homedir);\n\n const current = readJsonObjectOrEmpty(path);\n const value = parseCliValue(this.value);\n try {\n setAtPath(current, this.key, value);\n } catch (err) {\n if (err instanceof ForbiddenSegmentError) {\n this.context.stderr.write(tx(CONFIG_TEXTS.forbiddenKeySegment, { segment: err.segment, key: err.key }));\n emitDoneStderr(this.context.stderr, elapsed);\n return ExitCode.Error;\n }\n throw err;\n }\n\n const validators = loadSchemaValidators();\n const result = validators.validate('project-config', current);\n if (!result.ok) {\n this.context.stderr.write(tx(CONFIG_TEXTS.invalidAfterSet, { errors: result.errors }));\n emitDoneStderr(this.context.stderr, elapsed);\n return ExitCode.Error;\n }\n\n writeJsonAtomic(path, current);\n this.context.stdout.write(tx(CONFIG_TEXTS.setWritten, { key: this.key, value: formatValueHuman(value), path }));\n emitDoneStderr(this.context.stderr, elapsed);\n return ExitCode.Ok;\n }\n}\n\nexport class ConfigResetCommand extends Command {\n static override paths = [['config', 'reset']];\n static override usage = Command.Usage({\n category: 'Config',\n description: 'Remove a config key from the target file (project default; -g for user).',\n details: `\n Strips the key from the target settings.json (lower layers still apply).\n Idempotent — running twice is safe; absent key prints an info note and exits 0.\n `,\n });\n\n key = Option.String({ required: true });\n global = Option.Boolean('-g,--global', false);\n\n async execute(): Promise<number> {\n const elapsed = startElapsed();\n const ctx = defaultRuntimeContext();\n const target: TWriteTarget = this.global ? 'user' : 'project';\n const path = targetSettingsPath(target, ctx.cwd, ctx.homedir);\n\n if (!existsSync(path)) {\n this.context.stdout.write(tx(CONFIG_TEXTS.unsetNoOverride, { path, key: this.key }));\n emitDoneStderr(this.context.stderr, elapsed);\n return ExitCode.Ok;\n }\n const current = readJsonObjectOrEmpty(path);\n let removed: boolean;\n try {\n removed = deleteAtPath(current, this.key);\n } catch (err) {\n if (err instanceof ForbiddenSegmentError) {\n this.context.stderr.write(tx(CONFIG_TEXTS.forbiddenKeySegment, { segment: err.segment, key: err.key }));\n emitDoneStderr(this.context.stderr, elapsed);\n return ExitCode.Error;\n }\n throw err;\n }\n if (!removed) {\n this.context.stdout.write(tx(CONFIG_TEXTS.unsetNoOverride, { path, key: this.key }));\n emitDoneStderr(this.context.stderr, elapsed);\n return ExitCode.Ok;\n }\n\n writeJsonAtomic(path, current);\n this.context.stdout.write(tx(CONFIG_TEXTS.unsetRemoved, { key: this.key, path }));\n emitDoneStderr(this.context.stderr, elapsed);\n return ExitCode.Ok;\n }\n}\n\nexport const CONFIG_COMMANDS = [\n ConfigListCommand,\n ConfigGetCommand,\n ConfigShowCommand,\n ConfigSetCommand,\n ConfigResetCommand,\n];\n","/**\n * Elapsed-time helpers per `spec/cli-contract.md` §Elapsed time.\n *\n * Two output channels:\n *\n * - **Object outputs (`--json` whose schema is an object)** include a\n * top-level `elapsedMs` field. The schema declares it as required.\n * - **Stderr** receives `done in <formatted>` after every verb, except\n * when `--quiet` is passed.\n *\n * Format rules:\n * - `< 1000ms` → `34ms`\n * - `≥ 1s and < 60s` → `2.4s`\n * - `≥ 60s` → `1m 42s`\n */\n\nimport { tx } from '../../kernel/util/tx.js';\nimport { UTIL_TEXTS } from '../i18n/util.texts.js';\n\nexport interface IElapsed {\n /** Wall-clock ms since `startElapsed()` was called. */\n ms(): number;\n /** Same as `ms()` but pre-formatted for stderr / human display. */\n formatted(): string;\n}\n\nexport function startElapsed(): IElapsed {\n const startNs = process.hrtime.bigint();\n return {\n ms() {\n const elapsedNs = Number(process.hrtime.bigint() - startNs);\n return Math.round(elapsedNs / 1_000_000);\n },\n formatted() {\n return formatElapsed(this.ms());\n },\n };\n}\n\nexport function formatElapsed(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`;\n const minutes = Math.floor(ms / 60_000);\n const seconds = Math.round((ms % 60_000) / 1000);\n return `${minutes}m ${seconds}s`;\n}\n\n/**\n * Emit `done in <formatted>` to the supplied stderr stream unless\n * `quiet` is true. Trailing newline included.\n */\nexport function emitDoneStderr(\n stderr: NodeJS.WritableStream,\n elapsed: IElapsed,\n quiet = false,\n): void {\n if (quiet) return;\n stderr.write(tx(UTIL_TEXTS.doneIn, { elapsed: elapsed.formatted() }));\n}\n","/**\n * Tiny helpers for the recurring `catch (err) { const message = ... }`\n * dance. Every CLI command writes the same shape:\n *\n * try { ... }\n * catch (err) {\n * const message = err instanceof Error ? err.message : String(err);\n * this.context.stderr.write(tx(<VERB>_TEXTS.failed, { message }));\n * return ExitCode.Error;\n * }\n *\n * Twenty-plus duplicates of the same `instanceof Error ? ... : String(...)`\n * line drift over time (one site adds a stack-trace branch, another\n * doesn't) — the kind of inconsistency the L3 review flagged. Routing\n * through `formatErrorMessage` collapses the variance without forcing\n * every handler through a heavier reporter API.\n *\n * The surface is intentionally small — adding a `--verbose` stack\n * mode, a JSON envelope, or a sentinel-based exit code is the right\n * job for this module if those needs surface; today they don't.\n */\n\n/**\n * Compact error → string conversion.\n *\n * - `Error` → `err.message` verbatim. The caller is responsible for\n * wrapping with a verb-specific context line via `tx(*_TEXTS.x,\n * { message })`; we don't add one here so error catalogues stay\n * greppable.\n * - Anything else → `String(value)`. Catches the rare throw-a-string\n * / throw-an-object path without exploding on `null` (`String(null)`\n * = `'null'`).\n */\nexport function formatErrorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n","/**\n * Strings emitted by `cli/commands/config.ts`.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const CONFIG_TEXTS = {\n unknownKey: 'Unknown config key: {{key}}\\n',\n valueWithLayer: '{{value}} (from {{layer}})\\n',\n invalidAfterSet: 'Invalid config after set: {{errors}}\\n',\n setWritten: '{{key}} = {{value}} (wrote {{path}})\\n',\n unsetNoOverride: 'No override at {{path}} for {{key}}\\n',\n unsetRemoved: 'Removed {{key}} from {{path}}\\n',\n loadFailure: 'sm config: {{message}}\\n',\n forbiddenKeySegment: 'sm config: forbidden key segment \"{{segment}}\" in \"{{key}}\" (rejects __proto__ / constructor / prototype)\\n',\n} as const;\n","/**\n * `sm conformance run [--scope spec|provider:<id>|all]` — kernel-side CLI\n * verb for the conformance suite (Phase 5 / A.13).\n *\n * The verb is a thin orchestration layer over `runConformanceCase` (in\n * `src/conformance/index.ts`) and the scope registry at\n * `cli/util/conformance-scopes.ts`. It:\n *\n * 1. Resolves the requested scope set (`spec`, `provider:<id>`, or\n * `all` — default).\n * 2. For each scope, enumerates `cases/*.json` and runs them one by\n * one against the same `sm` binary that hosts the verb.\n * 3. Prints a pass/fail line per case + a summary per scope + a\n * grand total.\n *\n * Why dispatch to a child `sm` instead of calling the orchestrator\n * directly: the runner already exec's `bin/sm.js` for assertion\n * symmetry — it is the contract every conforming impl must satisfy.\n * Reusing it keeps `sm conformance run` honest (the verb passes the\n * same gate any third-party reviewer would run).\n *\n * Exit codes (per `spec/cli-contract.md` §Exit codes):\n * 0 every case in every selected scope passed\n * 1 one or more cases failed\n * 2 configuration error (unknown `--scope`, missing binary, ...)\n *\n * Stub caveats — the surface beyond the dispatch loop is intentionally\n * thin in this bump:\n *\n * - No `--json` mode yet. The verb prints human-readable summaries\n * to stdout; failures detail to stderr. JSON output lands when the\n * conformance reporter shape stabilises (Step 2's full runner pass).\n * - No parallelism. Cases run sequentially per scope; the runner\n * already provisions an isolated tmp directory per case so this is\n * a perf knob, not a correctness one.\n * - The `file-matches-schema` assertion is still stubbed in the\n * runner itself (lands with Step 2's AJV wiring). Cases relying on\n * it report `not yet implemented` per case, not per verb.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { Command, Option } from 'clipanion';\n\nimport { runConformanceCase } from '../../conformance/index.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { sanitizeForTerminal } from '../../kernel/util/safe-text.js';\nimport { CONFORMANCE_TEXTS } from '../i18n/conformance.texts.js';\nimport { ExitCode, type TExitCode } from '../util/exit-codes.js';\nimport { formatErrorMessage } from '../util/error-reporter.js';\nimport { truncateHead } from '../util/text.js';\nimport {\n listCaseFiles,\n selectConformanceScopes,\n type IConformanceScope,\n} from '../util/conformance-scopes.js';\n\n// Cap for assertion `reason` strings before they reach stderr. The\n// runner can splice subprocess `stderr` content (`stepChild.stderr`) into\n// the reason payload, which is unbounded — a runaway impl could emit\n// kilobytes that drown the user's terminal. Mirrors the cap policy used\n// for plugin-warning interpolation in `cli/util/plugin-runtime.ts`.\nconst ASSERTION_REASON_DISPLAY_CAP = 1000;\n\n/**\n * Render one failed-assertion line for stderr. The `reason` flows from\n * the conformance runner — some assertion variants splice the\n * impl-under-test's stderr verbatim into it (`runtime-error` carries\n * subprocess output as-is) — sanitize + cap before emitting so a\n * hostile or buggy impl cannot smuggle ANSI escapes into the user's\n * terminal via its own failure output.\n *\n * Exported for the audit M1 unit tests in\n * `test/conformance-cli.test.ts` — production callers reach this\n * through `ConformanceRunCommand.execute`.\n */\nexport function formatAssertionFailureDetail(\n type: string,\n reason: string,\n): string {\n return tx(CONFORMANCE_TEXTS.caseFailureDetail, {\n type,\n reason: sanitizeForTerminal(\n truncateHead(reason, ASSERTION_REASON_DISPLAY_CAP),\n ),\n });\n}\n\n/**\n * Resolve the absolute path to `bin/sm.js` relative to this module's\n * location. Works in both the source-tree layout\n * (`src/cli/commands/conformance.ts` → `src/bin/sm.js`) and the bundled\n * dist layout (`dist/cli.js` → `dist/../bin/sm.js`). The dev flow runs\n * `tsx` directly so module identity is fine; the build flow re-exports\n * via `dist/cli.js`, also next to `bin/`.\n */\nfunction resolveBinary(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n // walk up looking for a sibling `bin/sm.js`\n let cursor = here;\n for (let depth = 0; depth < 6; depth += 1) {\n const candidate = resolve(cursor, 'bin', 'sm.js');\n if (existsSync(candidate)) return candidate;\n const parent = dirname(cursor);\n if (parent === cursor) break;\n cursor = parent;\n }\n return resolve(here, '..', '..', 'bin', 'sm.js');\n}\n\nexport class ConformanceRunCommand extends Command {\n static override paths = [['conformance', 'run']];\n\n static override usage = Command.Usage({\n category: 'Introspection',\n description:\n 'Run the conformance suite — spec-owned cases plus every built-in Provider.',\n details: `\n Drives the conformance runner shipped at\n \\`@skill-map/cli/conformance\\` against the cases bundled with\n this CLI install. Each case provisions an isolated tmp scope,\n seeds the appropriate fixture, runs an \\`sm\\` invocation, and\n asserts the requested predicates.\n\n Scope selection:\n\n --scope spec only spec-owned, kernel-agnostic cases\n (default fixture: \\`preamble-v1.txt\\`,\n case: \\`kernel-empty-boot\\`).\n --scope provider:<id> only the named built-in Provider's\n cases. Today: \\`provider:claude\\`\n (\\`basic-scan\\`, \\`rename-high\\`,\n \\`orphan-detection\\`).\n --scope all (default) every scope, in registry order.\n\n Exit codes mirror the rest of the verb catalog: 0 on a clean\n sweep, 1 if any case failed, 2 on a configuration error\n (unknown scope, missing binary).\n `,\n examples: [\n ['Run every conformance suite', '$0 conformance run'],\n ['Run only the spec suite', '$0 conformance run --scope spec'],\n [\n 'Run only the Claude Provider suite',\n '$0 conformance run --scope provider:claude',\n ],\n ],\n });\n\n scope = Option.String('--scope', {\n required: false,\n description:\n \"Suite selector: 'all' (default), 'spec', or 'provider:<id>'.\",\n });\n\n // CLI orchestrator: scope resolution + per-case run loop +\n // per-result render branches + global pass/fail decision.\n // eslint-disable-next-line complexity\n async execute(): Promise<TExitCode> {\n let scopes: IConformanceScope[];\n try {\n scopes = selectConformanceScopes(this.scope);\n } catch (err) {\n const message = formatErrorMessage(err);\n this.context.stderr.write(tx(CONFORMANCE_TEXTS.unknownScope, { message }));\n return ExitCode.Error;\n }\n\n const binary = resolveBinary();\n if (!existsSync(binary)) {\n this.context.stderr.write(\n tx(CONFORMANCE_TEXTS.noBinary, { binary }),\n );\n return ExitCode.Error;\n }\n\n let totalPass = 0;\n let totalCases = 0;\n let anyFailure = false;\n\n for (const scope of scopes) {\n const cases = listCaseFiles(scope);\n if (cases.length === 0) {\n this.context.stdout.write(\n tx(CONFORMANCE_TEXTS.scopeEmpty, { label: scope.label }),\n );\n continue;\n }\n this.context.stdout.write(\n tx(CONFORMANCE_TEXTS.scopeHeader, {\n label: scope.label,\n caseCount: cases.length,\n }),\n );\n\n let scopePass = 0;\n for (const casePath of cases) {\n const caseId = readCaseId(casePath);\n try {\n const result = runConformanceCase({\n binary,\n specRoot: scope.specRoot,\n casePath,\n fixturesRoot: scope.fixturesDir,\n });\n if (result.passed) {\n this.context.stdout.write(\n tx(CONFORMANCE_TEXTS.caseOk, { caseId: result.caseId }),\n );\n scopePass += 1;\n } else {\n anyFailure = true;\n this.context.stdout.write(\n tx(CONFORMANCE_TEXTS.caseFail, { caseId: result.caseId }),\n );\n for (const a of result.assertions) {\n if (a.ok) continue;\n // `a.reason` flows from the conformance runner. Some\n // assertion variants splice the impl-under-test's stderr\n // into the reason payload (`runtime-error` carries\n // subprocess output verbatim) — sanitize + cap before\n // emitting so a hostile or buggy impl cannot smuggle\n // ANSI escapes into the user's terminal via its own\n // failure output.\n this.context.stderr.write(\n formatAssertionFailureDetail(a.type, a.reason),\n );\n }\n writeStreamSnippet(\n this.context.stderr,\n CONFORMANCE_TEXTS.caseFailureStdoutHeader,\n result.stdout,\n );\n writeStreamSnippet(\n this.context.stderr,\n CONFORMANCE_TEXTS.caseFailureStderrHeader,\n result.stderr,\n );\n }\n } catch (err) {\n anyFailure = true;\n const message = formatErrorMessage(err);\n this.context.stderr.write(\n tx(CONFORMANCE_TEXTS.runtimeError, { message }),\n );\n this.context.stdout.write(tx(CONFORMANCE_TEXTS.caseFail, { caseId }));\n }\n }\n\n this.context.stdout.write(\n tx(CONFORMANCE_TEXTS.scopeSummary, {\n label: scope.label,\n passCount: scopePass,\n caseCount: cases.length,\n }),\n );\n totalPass += scopePass;\n totalCases += cases.length;\n }\n\n this.context.stdout.write(\n tx(CONFORMANCE_TEXTS.totalSummary, {\n passCount: totalPass,\n caseCount: totalCases,\n scopeCount: scopes.length,\n }),\n );\n\n if (anyFailure) return ExitCode.Issues;\n return ExitCode.Ok;\n }\n}\n\nfunction readCaseId(casePath: string): string {\n try {\n const raw = readFileSync(casePath, 'utf8');\n const parsed = JSON.parse(raw) as { id?: unknown };\n if (typeof parsed.id === 'string') return parsed.id;\n } catch {\n /* fall through */\n }\n return casePath;\n}\n\nfunction writeStreamSnippet(\n stream: { write: (s: string) => boolean | unknown },\n header: string,\n text: string,\n): void {\n const trimmed = text.trim();\n if (trimmed.length === 0) return;\n stream.write(header);\n for (const line of trimmed.split(/\\r?\\n/)) {\n stream.write(tx(CONFORMANCE_TEXTS.caseFailureStreamLine, { line: sanitizeForTerminal(line) }));\n }\n}\n\nexport const CONFORMANCE_COMMANDS = [ConformanceRunCommand];\n","/**\n * Contract runner — executes the conformance cases shipped with\n * `@skill-map/spec` against an installed binary and emits a pass/fail result\n * per case.\n *\n * Implements the six assertion types from `spec/schemas/conformance-case.schema.json`.\n * Provisions a clean tmp scope per case, optionally pre-populated with the\n * referenced fixture corpus.\n *\n * Step 0b scope: single-case dispatch. Suite-level runner + reporter land\n * alongside Step 2 extensions.\n */\n\nimport { spawnSync } from 'node:child_process';\nimport { cpSync, existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { isAbsolute, join, relative, resolve } from 'node:path';\n\nimport { tx } from '../kernel/util/tx.js';\nimport { CONFORMANCE_RUNNER_TEXTS } from './i18n/runner.texts.js';\n\nexport type IAssertionResult =\n | { ok: true; type: string }\n | { ok: false; type: string; reason: string };\n\nexport interface IRunCaseResult {\n caseId: string;\n passed: boolean;\n exitCode: number;\n stdout: string;\n stderr: string;\n assertions: IAssertionResult[];\n}\n\nexport interface IRunCaseOptions {\n /** Absolute path to the binary wrapper (e.g. `bin/sm.js`). */\n binary: string;\n /** Absolute path to the `@skill-map/spec` root. */\n specRoot: string;\n /** Absolute path to the case JSON under `<conformance-root>/cases/`. */\n casePath: string;\n /**\n * Absolute path to the `<conformance-root>/fixtures/` directory backing\n * this case (or the parent conformance suite).\n *\n * Phase 5 / A.13 introduced per-Provider conformance directories that\n * live outside the spec tree (Claude-specific cases moved to\n * `src/extensions/providers/claude/conformance/`). Cases reference\n * fixtures by directory name; the runner resolves them under\n * `fixturesRoot` so the spec-agnostic kernel-empty-boot case and the\n * Claude `basic-scan` / `rename-high` / `orphan-detection` cases can\n * coexist without colliding fixture namespaces. Defaults to\n * `<specRoot>/conformance/fixtures` for the legacy spec layout.\n */\n fixturesRoot?: string;\n /** Extra env vars passed to the child. */\n env?: NodeJS.ProcessEnv;\n}\n\ninterface IConformanceCase {\n id: string;\n description: string;\n fixture?: string;\n setup?: {\n disableAllProviders?: boolean;\n disableAllExtractors?: boolean;\n disableAllRules?: boolean;\n priorScans?: Array<{ fixture: string; flags?: string[] }>;\n };\n invoke: {\n verb: string;\n sub?: string;\n args?: string[];\n flags?: string[];\n };\n assertions: IAssertion[];\n}\n\n/**\n * Build the env-var bag a case's `setup.disableAll*` toggles inject into\n * every child invocation (priorScans + the main `invoke`). The CLI's scan\n * composer (`composeScanExtensions`) reads these vars and drops every\n * extension of the matching kind from the in-scan pipeline.\n */\nfunction disableEnv(setup: IConformanceCase['setup']): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = {};\n if (setup?.disableAllProviders) env['SKILL_MAP_DISABLE_ALL_PROVIDERS'] = '1';\n if (setup?.disableAllExtractors) env['SKILL_MAP_DISABLE_ALL_EXTRACTORS'] = '1';\n if (setup?.disableAllRules) env['SKILL_MAP_DISABLE_ALL_RULES'] = '1';\n return env;\n}\n\nexport type IAssertion =\n | { type: 'exit-code'; value: number }\n | {\n type: 'json-path';\n path: string;\n equals?: unknown;\n greaterThan?: number;\n lessThan?: number;\n matches?: string;\n }\n | { type: 'file-exists'; path: string }\n | { type: 'file-contains-verbatim'; path: string; fixture: string }\n | { type: 'file-matches-schema'; path: string; schema: string }\n | { type: 'stderr-matches'; pattern: string };\n\n// eslint-disable-next-line complexity\nexport function runConformanceCase(options: IRunCaseOptions): IRunCaseResult {\n const raw = readFileSync(options.casePath, 'utf8');\n const c: IConformanceCase = JSON.parse(raw);\n\n const fixturesRoot = options.fixturesRoot ?? join(options.specRoot, 'conformance', 'fixtures');\n\n // Defence in depth (audit L5): the conformance case id is JSON-author-\n // controlled. Replace anything that isn't a safe filesystem char and\n // cap the length so an over-long id (or one carrying path separators\n // / control bytes) can't escape `tmpdir()` or grow the prefix beyond\n // a reasonable bound.\n const safeId = c.id.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 32);\n const scope = mkdtempSync(join(tmpdir(), `sm-conformance-${safeId}-`));\n const setupEnv = disableEnv(c.setup);\n try {\n // 1. Replay every `setup.priorScans` step into the scope DB before\n // the main invoke runs. Returns the failure result early if any\n // step exits non-zero.\n const priorFailure = runPriorScansSetup(c, options, scope, fixturesRoot, setupEnv);\n if (priorFailure) return priorFailure;\n\n // 2. Copy the main fixture (replacing prior fixture content but\n // preserving the DB), then run the case's `invoke`.\n if (c.fixture) {\n replaceFixture(scope, fixturesRoot, c.fixture);\n }\n\n const argv = [c.invoke.verb];\n if (c.invoke.sub) argv.push(c.invoke.sub);\n if (c.invoke.args) argv.push(...c.invoke.args);\n if (c.invoke.flags) argv.push(...c.invoke.flags);\n\n const child = spawnSync(process.execPath, [options.binary, ...argv], {\n cwd: scope,\n env: { ...process.env, ...options.env, ...setupEnv },\n encoding: 'utf8',\n });\n\n const stdout = child.stdout ?? '';\n const stderr = child.stderr ?? '';\n const exitCode = child.status ?? 0;\n\n const assertions = c.assertions.map((a) =>\n evaluateAssertion(a, {\n exitCode,\n stdout,\n stderr,\n scope,\n specRoot: options.specRoot,\n fixturesRoot,\n }),\n );\n const passed = assertions.every((a) => a.ok);\n\n return { caseId: c.id, passed, exitCode, stdout, stderr, assertions };\n } finally {\n rmSync(scope, { recursive: true, force: true });\n }\n}\n\n/**\n * Phase 1 of `runConformanceCase` — replay every `setup.priorScans`\n * step in order. Each step replaces every non-`.skill-map/` directory\n * with the named fixture, then runs `sm scan` so the snapshot persists\n * into the scope DB. The scope DB survives across steps (we never\n * delete `.skill-map/`).\n *\n * Returns `null` on success (caller continues) or a `IRunCaseResult`\n * with a single `priorScan` failure assertion (caller returns it\n * unchanged).\n */\n// Per-step replay: replace fixture, spawn `sm scan`, check exit. The\n// failure-result construction is verbose because it carries every\n// stream the caller reports back.\n// eslint-disable-next-line complexity\nfunction runPriorScansSetup(\n c: IConformanceCase,\n options: IRunCaseOptions,\n scope: string,\n fixturesRoot: string,\n setupEnv: NodeJS.ProcessEnv,\n): IRunCaseResult | null {\n for (const step of c.setup?.priorScans ?? []) {\n replaceFixture(scope, fixturesRoot, step.fixture);\n const stepArgv = ['scan', ...(step.flags ?? [])];\n const stepChild = spawnSync(process.execPath, [options.binary, ...stepArgv], {\n cwd: scope,\n env: { ...process.env, ...options.env, ...setupEnv },\n encoding: 'utf8',\n });\n if ((stepChild.status ?? 0) !== 0) {\n return {\n caseId: c.id,\n passed: false,\n exitCode: stepChild.status ?? 0,\n stdout: stepChild.stdout ?? '',\n stderr: stepChild.stderr ?? '',\n assertions: [\n {\n ok: false,\n type: 'priorScan',\n reason: tx(CONFORMANCE_RUNNER_TEXTS.priorScanFailed, {\n fixture: step.fixture,\n exit: stepChild.status ?? 0,\n stderr: stepChild.stderr ?? '',\n }),\n },\n ],\n };\n }\n }\n return null;\n}\n\n/**\n * Replace every top-level entry in `scope` EXCEPT `.skill-map/` (which\n * holds the kernel DB and persists across staging steps), then copy\n * the fixture's contents on top. Used by `priorScans` and the main\n * fixture phase to swap Provider content while keeping the DB stable.\n *\n * `fixturesRoot` is the absolute path to the `fixtures/` directory of\n * the conformance suite hosting the case (spec-owned for kernel cases,\n * Provider-owned for Provider cases — see `IRunCaseOptions.fixturesRoot`).\n */\nfunction replaceFixture(scope: string, fixturesRoot: string, fixture: string): void {\n assertContained(fixturesRoot, fixture, 'fixture');\n for (const entry of readdirSync(scope)) {\n if (entry === '.skill-map') continue;\n rmSync(join(scope, entry), { recursive: true, force: true });\n }\n const src = join(fixturesRoot, fixture);\n cpSync(src, scope, { recursive: true });\n}\n\n/**\n * Reject case-supplied path strings that escape the directory tree they\n * are anchored to. A hostile case JSON would otherwise be able to copy\n * arbitrary filesystem content into the tmp scope (`fixture: \"../..\"`)\n * or read files outside the conformance sandbox via `file-exists` /\n * `file-contains-verbatim` assertions.\n */\nfunction assertContained(root: string, rel: string, label: string): void {\n if (isAbsolute(rel)) {\n throw new Error(\n tx(CONFORMANCE_RUNNER_TEXTS.pathMustBeRelative, { label, path: rel, anchor: root }),\n );\n }\n const abs = resolve(root, rel);\n const r = relative(root, abs);\n if (r.startsWith('..') || isAbsolute(r)) {\n throw new Error(\n tx(CONFORMANCE_RUNNER_TEXTS.pathEscapesAnchor, { label, path: rel, anchor: root }),\n );\n }\n}\n\ninterface IAssertionContext {\n exitCode: number;\n stdout: string;\n stderr: string;\n scope: string;\n specRoot: string;\n fixturesRoot: string;\n}\n\n// Switch over assertion types (`exit-code` / `stdout-matches` /\n// `file-exists` / `file-contains-verbatim` / `file-matches-schema` /\n// `stderr-matches` / `json-path`) with one branch per type. Splitting\n// per type would scatter the discriminated-union dispatch.\n// eslint-disable-next-line complexity\nfunction evaluateAssertion(a: IAssertion, ctx: IAssertionContext): IAssertionResult {\n switch (a.type) {\n case 'exit-code':\n return ctx.exitCode === a.value\n ? { ok: true, type: a.type }\n : {\n ok: false,\n type: a.type,\n reason: tx(CONFORMANCE_RUNNER_TEXTS.expectedExitCode, {\n expected: a.value,\n actual: ctx.exitCode,\n }),\n };\n case 'json-path':\n return evaluateJsonPath(a, ctx);\n case 'file-exists': {\n try {\n assertContained(ctx.scope, a.path, 'file-exists');\n } catch (err) {\n return { ok: false, type: a.type, reason: (err as Error).message };\n }\n const abs = resolve(ctx.scope, a.path);\n return existsSync(abs)\n ? { ok: true, type: a.type }\n : {\n ok: false,\n type: a.type,\n reason: tx(CONFORMANCE_RUNNER_TEXTS.fileNotFound, { path: a.path }),\n };\n }\n case 'file-contains-verbatim': {\n try {\n assertContained(ctx.fixturesRoot, a.fixture, 'file-contains-verbatim/fixture');\n assertContained(ctx.scope, a.path, 'file-contains-verbatim/path');\n } catch (err) {\n return { ok: false, type: a.type, reason: (err as Error).message };\n }\n const fixturePath = join(ctx.fixturesRoot, a.fixture);\n const targetPath = resolve(ctx.scope, a.path);\n if (!existsSync(targetPath)) {\n return {\n ok: false,\n type: a.type,\n reason: tx(CONFORMANCE_RUNNER_TEXTS.targetNotFound, { path: a.path }),\n };\n }\n const needle = readFileSync(fixturePath);\n const haystack = readFileSync(targetPath);\n return haystack.includes(needle)\n ? { ok: true, type: a.type }\n : {\n ok: false,\n type: a.type,\n reason: tx(CONFORMANCE_RUNNER_TEXTS.targetMissingFixture, { fixture: a.fixture }),\n };\n }\n case 'file-matches-schema':\n return {\n ok: false,\n type: a.type,\n reason: CONFORMANCE_RUNNER_TEXTS.fileMatchesSchemaUnimplemented,\n };\n case 'stderr-matches': {\n const re = new RegExp(a.pattern);\n return re.test(ctx.stderr)\n ? { ok: true, type: a.type }\n : {\n ok: false,\n type: a.type,\n reason: tx(CONFORMANCE_RUNNER_TEXTS.stderrDidNotMatch, { pattern: a.pattern }),\n };\n }\n }\n}\n\n/**\n * Minimal JSONPath evaluator — supports only the subset used by the stub\n * conformance suite: `$.foo`, `$.foo.bar`, `$.foo.length`, `$[0]`.\n * The full RFC 9535 implementation lands with Step 2.\n */\nfunction evaluateJsonPath(\n a: Extract<IAssertion, { type: 'json-path' }>,\n ctx: IAssertionContext,\n): IAssertionResult {\n let doc: unknown;\n try {\n doc = JSON.parse(ctx.stdout);\n } catch (err) {\n return {\n ok: false,\n type: a.type,\n reason: tx(CONFORMANCE_RUNNER_TEXTS.stdoutNotJson, { message: (err as Error).message }),\n };\n }\n\n const segments = parsePath(a.path);\n if (!segments) {\n return {\n ok: false,\n type: a.type,\n reason: tx(CONFORMANCE_RUNNER_TEXTS.unsupportedJsonPath, { path: a.path }),\n };\n }\n\n const walked = traverseJsonPath(doc, segments, a.path);\n if (!walked.ok) return { ok: false, type: a.type, reason: walked.reason };\n\n return applyJsonPathComparator(a, walked.value);\n}\n\n/**\n * Walk a parsed JSONPath segment list against a JSON document. Returns\n * the resolved value or a structured failure (caller maps to\n * `IAssertionResult`). Pure — no IO, no shared state.\n */\nfunction traverseJsonPath(\n doc: unknown,\n segments: Array<string | number>,\n path: string,\n): { ok: true; value: unknown } | { ok: false; reason: string } {\n let current: unknown = doc;\n for (const seg of segments) {\n if (typeof seg === 'number') {\n if (!Array.isArray(current)) {\n return { ok: false, reason: tx(CONFORMANCE_RUNNER_TEXTS.expectedArrayAtPath, { path }) };\n }\n current = current[seg];\n } else if (seg === 'length' && Array.isArray(current)) {\n current = current.length;\n } else if (typeof current === 'object' && current !== null) {\n current = (current as Record<string, unknown>)[seg];\n } else {\n return {\n ok: false,\n reason: tx(CONFORMANCE_RUNNER_TEXTS.cannotTraverseSegment, {\n type: typeof current,\n segment: String(seg),\n }),\n };\n }\n }\n return { ok: true, value: current };\n}\n\n/**\n * Apply the comparator clause (`equals` / `greaterThan` / `lessThan` /\n * `matches`) of a `json-path` assertion against the value resolved at\n * the requested path. Returns the final `IAssertionResult` directly.\n *\n * Complexity from the four parallel comparator branches; splitting into\n * one helper per comparator would be ceremony.\n */\n// eslint-disable-next-line complexity\nfunction applyJsonPathComparator(\n a: Extract<IAssertion, { type: 'json-path' }>,\n current: unknown,\n): IAssertionResult {\n if ('equals' in a && a.equals !== undefined) {\n return deepEqual(current, a.equals)\n ? { ok: true, type: a.type }\n : {\n ok: false,\n type: a.type,\n reason: tx(CONFORMANCE_RUNNER_TEXTS.jsonPathEqualsMismatch, {\n path: a.path,\n actual: JSON.stringify(current),\n expected: JSON.stringify(a.equals),\n }),\n };\n }\n if ('greaterThan' in a && typeof a.greaterThan === 'number') {\n return typeof current === 'number' && current > a.greaterThan\n ? { ok: true, type: a.type }\n : {\n ok: false,\n type: a.type,\n reason: tx(CONFORMANCE_RUNNER_TEXTS.jsonPathNotGreaterThan, {\n path: a.path,\n value: a.greaterThan,\n }),\n };\n }\n if ('lessThan' in a && typeof a.lessThan === 'number') {\n return typeof current === 'number' && current < a.lessThan\n ? { ok: true, type: a.type }\n : {\n ok: false,\n type: a.type,\n reason: tx(CONFORMANCE_RUNNER_TEXTS.jsonPathNotLessThan, {\n path: a.path,\n value: a.lessThan,\n }),\n };\n }\n if ('matches' in a && typeof a.matches === 'string') {\n const re = new RegExp(a.matches);\n return typeof current === 'string' && re.test(current)\n ? { ok: true, type: a.type }\n : {\n ok: false,\n type: a.type,\n reason: tx(CONFORMANCE_RUNNER_TEXTS.jsonPathDidNotMatch, {\n path: a.path,\n pattern: a.matches,\n }),\n };\n }\n return { ok: false, type: a.type, reason: CONFORMANCE_RUNNER_TEXTS.jsonPathNoComparator };\n}\n\nfunction parsePath(path: string): Array<string | number> | null {\n if (!path.startsWith('$')) return null;\n const tail = path.slice(1);\n const segments: Array<string | number> = [];\n const re = /\\.([a-zA-Z_][a-zA-Z0-9_-]*)|\\[(\\d+)\\]/g;\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = re.exec(tail)) !== null) {\n if (match.index !== lastIndex) return null;\n if (match[1] !== undefined) segments.push(match[1]);\n else if (match[2] !== undefined) segments.push(Number.parseInt(match[2], 10));\n lastIndex = re.lastIndex;\n }\n if (lastIndex !== tail.length) return null;\n return segments;\n}\n\n// eslint-disable-next-line complexity\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a !== typeof b) return false;\n if (a && b && typeof a === 'object' && typeof b === 'object') {\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n const ak = Object.keys(a as object);\n const bk = Object.keys(b as object);\n if (ak.length !== bk.length) return false;\n for (const k of ak) {\n if (\n !deepEqual(\n (a as Record<string, unknown>)[k],\n (b as Record<string, unknown>)[k],\n )\n )\n {return false;}\n }\n return true;\n }\n return false;\n}\n\n/** Verifies the spec root looks sane (contains `index.json`). */\nexport function assertSpecRoot(specRoot: string): void {\n const indexPath = join(specRoot, 'index.json');\n if (!existsSync(indexPath) || !statSync(indexPath).isFile()) {\n throw new Error(tx(CONFORMANCE_RUNNER_TEXTS.specRootMissingIndex, { specRoot }));\n }\n}\n","/**\n * Strings emitted by the conformance runner (`conformance/index.ts`).\n * Same `tx(template, vars)` convention as every other `*.texts.ts` peer.\n *\n * Reasons surface in `IAssertionResult.reason` — visible to anyone\n * reading the runner output (CI logs, `sm conformance run --json`).\n * Keeping them in the catalog unblocks a future Transloco migration and\n * keeps the wording in one place.\n */\n\nexport const CONFORMANCE_RUNNER_TEXTS = {\n priorScanFailed:\n 'setup.priorScans step `{{fixture}}` failed with exit {{exit}}: {{stderr}}',\n\n pathMustBeRelative:\n 'conformance: {{label}} path \"{{path}}\" must be relative to its anchor ({{anchor}})',\n\n pathEscapesAnchor:\n 'conformance: {{label}} path \"{{path}}\" escapes its anchor ({{anchor}})',\n\n expectedExitCode:\n 'expected exit {{expected}}, got {{actual}}',\n\n fileNotFound:\n 'file not found: {{path}}',\n\n targetNotFound:\n 'target not found: {{path}}',\n\n targetMissingFixture:\n 'target does not contain fixture {{fixture}} verbatim',\n\n fileMatchesSchemaUnimplemented:\n 'file-matches-schema not yet implemented (requires ajv; lands with Step 2)',\n\n stderrDidNotMatch:\n 'stderr did not match /{{pattern}}/',\n\n stdoutNotJson:\n 'stdout is not valid JSON: {{message}}',\n\n unsupportedJsonPath:\n 'unsupported jsonpath: {{path}}',\n\n expectedArrayAtPath:\n 'expected array at {{path}}',\n\n cannotTraverseSegment:\n \"cannot traverse {{type}} at segment '{{segment}}'\",\n\n jsonPathEqualsMismatch:\n '{{path}} = {{actual}}, expected {{expected}}',\n\n jsonPathNotGreaterThan:\n '{{path}} not > {{value}}',\n\n jsonPathNotLessThan:\n '{{path}} not < {{value}}',\n\n jsonPathDidNotMatch:\n '{{path}} did not match /{{pattern}}/',\n\n jsonPathNoComparator:\n 'no comparator on json-path assertion',\n\n specRootMissingIndex:\n 'spec root missing index.json at {{specRoot}}',\n} as const;\n","/**\n * CLI strings emitted by `sm conformance run` (`cli/commands/conformance.ts`).\n *\n * `sm conformance run` is the external-facing entry point for the\n * conformance suite — both the spec-owned cases (under `@skill-map/spec`)\n * and the per-Provider suites bundled with the reference impl (today\n * just `provider:claude`). Phase 5 / A.13 introduced the verb so\n * alt-impl authors and Provider authors can drive the suite without\n * reaching into bespoke scripts.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const CONFORMANCE_TEXTS = {\n // --- top-level summary ----------------------------------------------------\n scopeHeader:\n 'Running conformance scope {{label}} ({{caseCount}} case(s)) ...\\n',\n\n scopeEmpty:\n 'Conformance scope {{label}} has no cases. Skipping.\\n',\n\n caseOk: ' ok {{caseId}}\\n',\n caseFail: ' FAIL {{caseId}}\\n',\n\n caseFailureDetail: ' - [{{type}}] {{reason}}\\n',\n\n caseFailureStdoutHeader: ' --- stdout ---\\n',\n caseFailureStreamLine: ' {{line}}\\n',\n caseFailureStderrHeader: ' --- stderr ---\\n',\n\n scopeSummary:\n '{{label}}: {{passCount}}/{{caseCount}} passed.\\n',\n\n totalSummary:\n 'sm conformance: {{passCount}}/{{caseCount}} passed across {{scopeCount}} scope(s).\\n',\n\n // --- failures -------------------------------------------------------------\n unknownScope: 'sm conformance: {{message}}\\n',\n\n noBinary:\n 'sm conformance: cannot locate the sm binary at {{binary}}. ' +\n 'Run `npm run build --workspace=@skill-map/cli` first.\\n',\n\n runtimeError: 'sm conformance: {{message}}\\n',\n} as const;\n","/**\n * Conformance scope registry — single source of truth for `sm\n * conformance run` (`cli/commands/conformance.ts`) and the in-process\n * `src/test/conformance.test.ts` reference suite.\n *\n * Phase 5 / A.13 split the conformance suite in two:\n *\n * - **Spec-owned scope** — `spec/conformance/` ships with\n * `@skill-map/spec` and only contains kernel-agnostic cases\n * (`kernel-empty-boot`) plus the universal preamble fixture.\n * Discovered via `resolveSpecRoot()`.\n *\n * - **Provider-owned scopes** — each built-in Provider (today\n * `claude`) carries its own `conformance/` directory next to its\n * manifest, with cases that exercise the Provider's kind catalog\n * (`basic-scan`, `rename-high`, `orphan-detection` for Claude).\n * Discovered by walking the source tree for now (dev/CI only —\n * the bundled CLI does not yet ship Provider conformance assets;\n * when it does, the resolver below grows a `dist/extensions/...`\n * fallback alongside the source path).\n *\n * The shape of a scope is intentionally narrow: a stable `id`, a label,\n * the absolute paths to its `cases/` and `fixtures/` directories. The\n * runner (`src/conformance/index.ts`) consumes them via\n * `IRunCaseOptions.casePath` + `IRunCaseOptions.fixturesRoot`.\n */\n\nimport { existsSync, readdirSync, statSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { createRequire } from 'node:module';\nimport { fileURLToPath } from 'node:url';\n\nexport type TConformanceScopeKind = 'spec' | 'provider';\n\nexport interface IConformanceScope {\n /**\n * Stable identifier used by `--scope <id>` on the CLI verb.\n *\n * - Spec scope: `'spec'`.\n * - Provider scopes: `'provider:<plugin-id>'` (e.g. `'provider:claude'`).\n */\n id: string;\n kind: TConformanceScopeKind;\n /** Human-readable label for diagnostic output. */\n label: string;\n /** Absolute path to the `cases/` directory. */\n casesDir: string;\n /** Absolute path to the `fixtures/` directory. */\n fixturesDir: string;\n /**\n * Absolute path to the `@skill-map/spec` root the runner should pass\n * through to assertions like `file-matches-schema`. Same value across\n * every scope — the spec is global.\n */\n specRoot: string;\n}\n\n/**\n * Locate the installed `@skill-map/spec` package root. Mirrors the\n * helper in `kernel/adapters/schema-validators.ts` — kept independent\n * to avoid a kernel-on-cli import direction.\n */\nfunction resolveSpecRoot(): string {\n const require = createRequire(import.meta.url);\n try {\n const indexPath = require.resolve('@skill-map/spec/index.json');\n return dirname(indexPath);\n } catch {\n throw new Error(\n '@skill-map/spec not resolvable — ensure the workspace is linked or the package is installed.',\n );\n }\n}\n\n/**\n * Locate this CLI workspace root (the `src/` directory in dev, the\n * package install dir at runtime). Built-in Provider conformance assets\n * live under `<workspace>/extensions/providers/<id>/conformance/`.\n *\n * Strategy:\n *\n * 1. Walk up from `import.meta.url` looking for a sibling\n * `extensions/providers/` directory. Works in both source-tree\n * (`src/cli/util/conformance-scopes.ts` → `src/`) and bundled-dist\n * layouts (`dist/cli.js` → `dist/`, once tsup copies the\n * conformance trees in `onSuccess`).\n * 2. Throw a directed error if the directory cannot be located —\n * callers convert this to an exit-2 with a hint.\n */\nfunction resolveCliWorkspaceRoot(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n let cursor = here;\n for (let depth = 0; depth < 6; depth += 1) {\n const candidate = resolve(cursor, 'built-in-plugins', 'providers');\n if (existsSync(candidate) && statSync(candidate).isDirectory()) {\n return cursor;\n }\n const parent = dirname(cursor);\n if (parent === cursor) break;\n cursor = parent;\n }\n throw new Error(\n 'sm conformance: built-in Provider conformance assets not found ' +\n \"(expected a 'built-in-plugins/providers/' directory above \" +\n `${here}). The bundled CLI may not yet copy the assets — ` +\n 'run from the source workspace, or rebuild after enabling the ' +\n 'asset-copy step.',\n );\n}\n\n/**\n * Enumerate every built-in Provider that ships a `conformance/`\n * directory next to its manifest. Today the only built-in Provider is\n * `claude`; the loop is generic so a future Provider only needs to add\n * its directory under `extensions/providers/<id>/conformance/` to be\n * discovered automatically.\n */\nfunction collectProviderScopes(specRoot: string): IConformanceScope[] {\n const out: IConformanceScope[] = [];\n let workspaceRoot: string;\n try {\n workspaceRoot = resolveCliWorkspaceRoot();\n } catch {\n return out;\n }\n const providersRoot = resolve(workspaceRoot, 'built-in-plugins', 'providers');\n if (!existsSync(providersRoot)) return out;\n for (const entry of readdirSync(providersRoot)) {\n const providerDir = resolve(providersRoot, entry);\n if (!statSync(providerDir).isDirectory()) continue;\n const conformanceDir = resolve(providerDir, 'conformance');\n if (!existsSync(conformanceDir)) continue;\n const casesDir = resolve(conformanceDir, 'cases');\n const fixturesDir = resolve(conformanceDir, 'fixtures');\n if (!existsSync(casesDir) || !existsSync(fixturesDir)) continue;\n out.push({\n id: `provider:${entry}`,\n kind: 'provider',\n label: `provider:${entry}`,\n casesDir,\n fixturesDir,\n specRoot,\n });\n }\n return out;\n}\n\n/**\n * Single source of truth for the spec-owned conformance scope.\n */\nfunction specScope(specRoot: string): IConformanceScope {\n return {\n id: 'spec',\n kind: 'spec',\n label: 'spec',\n casesDir: resolve(specRoot, 'conformance', 'cases'),\n fixturesDir: resolve(specRoot, 'conformance', 'fixtures'),\n specRoot,\n };\n}\n\n/**\n * Discover every conformance scope visible to the running CLI: the spec\n * scope plus every built-in Provider scope. Returned in stable order\n * (spec first, then providers in directory-listing order).\n */\nexport function listConformanceScopes(): IConformanceScope[] {\n const specRoot = resolveSpecRoot();\n return [specScope(specRoot), ...collectProviderScopes(specRoot)];\n}\n\n/**\n * Resolve a `--scope` value to one or more concrete scopes. Accepts:\n *\n * - `'all'` (or `undefined`): every scope.\n * - `'spec'`: the spec scope alone.\n * - `'provider:<id>'`: the matching Provider scope. Unknown ids\n * throw — callers map the error to exit-2 with a hint listing the\n * available scopes.\n */\nexport function selectConformanceScopes(\n scope: string | undefined,\n): IConformanceScope[] {\n const scopes = listConformanceScopes();\n if (scope === undefined || scope === 'all') return scopes;\n const match = scopes.find((s) => s.id === scope);\n if (!match) {\n const available = scopes.map((s) => s.id).join(', ');\n throw new Error(\n `sm conformance: unknown --scope '${scope}'. Available: ${available}.`,\n );\n }\n return [match];\n}\n\n/**\n * List every `*.json` case file under `scope.casesDir` in\n * lexicographic order. Empty array if the directory contains no\n * cases (a Provider that ships an empty suite still surfaces — the\n * verb just runs zero cases against it and reports zero passes).\n */\nexport function listCaseFiles(scope: IConformanceScope): string[] {\n if (!existsSync(scope.casesDir)) return [];\n return readdirSync(scope.casesDir)\n .filter((entry) => entry.endsWith('.json'))\n .sort()\n .map((entry) => resolve(scope.casesDir, entry));\n}\n","/**\n * `sm db` — database lifecycle verbs. Backup, restore, reset, shell, dump,\n * migrate. Destructive verbs (`restore`, `reset --state`, `reset --hard`)\n * require interactive confirmation unless `--yes` / `--force` is passed, per\n * spec/cli-contract.md §Database.\n *\n * Exit codes follow spec/cli-contract.md:\n * 0 ok\n * 2 error (unhandled / config / user aborted)\n * 5 not-found\n */\n\nimport { spawnSync } from 'node:child_process';\nimport { chmod, copyFile, mkdir, rm, stat } from 'node:fs/promises';\nimport { dirname, join, resolve } from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\nimport { withSqlite } from '../util/with-sqlite.js';\nimport { confirm } from '../util/confirm.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { DB_TEXTS } from '../i18n/db.texts.js';\n\nimport { Command, Option } from 'clipanion';\n\nimport { createSqliteStorage } from '../../kernel/adapters/sqlite/index.js';\nimport type { StoragePort } from '../../kernel/ports/storage.js';\nimport type { IPluginApplyResult } from '../../kernel/adapters/sqlite/plugin-migrations.js';\nimport type { IDiscoveredPlugin } from '../../kernel/types/plugin.js';\nimport { assertDbExists, resolveDbPath } from '../util/db-path.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport { formatErrorMessage } from '../util/error-reporter.js';\nimport {\n emptyPluginRuntime,\n loadPluginRuntime,\n} from '../util/plugin-runtime.js';\n\nconst SAFE_SQL_IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\n/**\n * Reject any sqlite_master row name that is not a plain identifier before\n * it reaches a `db.exec` statement. The catalog filter `LIKE 'scan_%'`\n * (and optional `state_%`) shipped above is the primary line of defence;\n * this function is the second layer.\n */\nfunction assertSafeIdentifier(name: string): void {\n if (!SAFE_SQL_IDENTIFIER_RE.test(name)) {\n throw new Error(`refusing to operate on non-identifier table name: ${JSON.stringify(name)}`);\n }\n}\n\n/**\n * Async existence probe via `fs.stat`. Used in place of `existsSync` so\n * the verb stays cooperative on the event loop. ENOENT is the only swallowed\n * error code; anything else (permission denied, IO failure) propagates so\n * the caller sees the real reason instead of a false \"not found\".\n */\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw err;\n }\n}\n\n/** Same as `pathExists` but returns the `Stats` so the caller can read `.size`. */\nasync function statOrNull(path: string): Promise<import('node:fs').Stats | null> {\n try {\n return await stat(path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n}\n\n/**\n * Force `0o600` perms on a file, swallowing failures (Windows / non-POSIX\n * filesystems may reject `chmod`). Used after `db restore` to keep the\n * restored DB owner-readable only — see audit L4.\n */\nasync function chmodOwnerOnlyBestEffort(target: string): Promise<void> {\n try {\n await chmod(target, 0o600);\n } catch {\n // Best effort — the DB is already in place; tightening perms is a\n // hardening pass, not a correctness gate.\n }\n}\n\n// --- backup ---------------------------------------------------------------\n\nexport class DbBackupCommand extends Command {\n static override paths = [['db', 'backup']];\n static override usage = Command.Usage({\n category: 'Database',\n description: 'WAL checkpoint + copy the DB file to a backup.',\n details: `\n Default output: <db-dir>/backups/<timestamp>.db. Use --out to override.\n scan_* is regenerated on demand and is NOT excluded from the raw file\n copy, but restoring a backup over a live DB is the expected use —\n running sm scan afterwards refreshes scan_*.\n `,\n });\n\n global = Option.Boolean('-g,--global', false);\n db = Option.String('--db', { required: false });\n out = Option.String('--out', { required: false });\n\n async execute(): Promise<number> {\n const path = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });\n if (!assertDbExists(path, this.context.stderr)) return ExitCode.NotFound;\n\n const ts = new Date().toISOString().replace(/[:.]/g, '-');\n const outPath = this.out ? resolve(this.out) : join(dirname(path), 'backups', `${ts}.db`);\n\n // Route through the storage port — the port's `writeBackup` does\n // the WAL checkpoint, parent-directory creation, and atomic file\n // copy in one call. `autoMigrate: false` keeps the open from\n // touching schema; `autoBackup: false` is implied because no\n // migrations run. The verb composes `outPath` (timestamp default\n // or `--out` override) and hands it to the port.\n await withSqlite({ databasePath: path, autoMigrate: false }, async (storage) => {\n storage.migrations.writeBackup(outPath);\n });\n\n this.context.stdout.write(tx(DB_TEXTS.backupWritten, { outPath }));\n return ExitCode.Ok;\n }\n}\n\n// --- restore --------------------------------------------------------------\n\nexport class DbRestoreCommand extends Command {\n static override paths = [['db', 'restore']];\n static override usage = Command.Usage({\n category: 'Database',\n description: 'Replace the active DB file with a backup.',\n details: `\n Destructive. Requires interactive confirmation unless --yes / --force\n is passed. scan_* will be re-populated by the next sm scan.\n With --dry-run: previews the swap (source size, target overwrite\n status, sidecars to drop) without copying or deleting anything.\n Dry-run bypasses the confirmation prompt.\n `,\n });\n\n source = Option.String({ required: true });\n global = Option.Boolean('-g,--global', false);\n db = Option.String('--db', { required: false });\n yes = Option.Boolean('--yes,--force', false);\n dryRun = Option.Boolean('-n,--dry-run', false, {\n description: 'Preview the restore without overwriting the live DB.',\n });\n\n async execute(): Promise<number> {\n const target = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });\n const sourcePath = resolve(this.source);\n\n const sourceStat = await statOrNull(sourcePath);\n if (!sourceStat) {\n this.context.stderr.write(tx(DB_TEXTS.restoreSourceNotFound, { sourcePath }));\n return ExitCode.NotFound;\n }\n\n if (this.dryRun) {\n this.context.stdout.write(DB_TEXTS.dryRunHeader);\n const sourceBytes = sourceStat.size;\n const targetClause = (await pathExists(target))\n ? DB_TEXTS.dryRunRestoreTargetExistsClause\n : DB_TEXTS.dryRunRestoreTargetMissingClause;\n this.context.stdout.write(\n tx(DB_TEXTS.dryRunRestoreWouldOverwrite, {\n sourcePath,\n sourceBytes,\n target,\n targetClause,\n }),\n );\n return ExitCode.Ok;\n }\n\n if (!this.yes) {\n const ok = await confirm(tx(DB_TEXTS.restoreConfirm, { sourcePath, target }), {\n stdin: this.context.stdin,\n stderr: this.context.stderr,\n });\n if (!ok) {\n this.context.stderr.write(DB_TEXTS.aborted);\n return ExitCode.Error;\n }\n }\n\n await mkdir(dirname(target), { recursive: true });\n await copyFile(sourcePath, target);\n // Defence in depth (audit L4): force restrictive owner-only perms on\n // the restored DB. Helper-extracted so the try/catch doesn't push\n // `execute` past the cyclomatic budget.\n await chmodOwnerOnlyBestEffort(target);\n // WAL sidecars from the old DB would be out of sync — delete them so\n // next open starts clean against the restored main file.\n for (const sidecar of [`${target}-wal`, `${target}-shm`]) {\n if (await pathExists(sidecar)) await rm(sidecar);\n }\n\n this.context.stdout.write(tx(DB_TEXTS.restoreDone, { sourcePath, target }));\n return ExitCode.Ok;\n }\n}\n\n// --- reset ----------------------------------------------------------------\n\nexport class DbResetCommand extends Command {\n static override paths = [['db', 'reset']];\n static override usage = Command.Usage({\n category: 'Database',\n description: 'Drop scan_* (default), optionally state_*, or delete the DB entirely.',\n details: `\n Without flags: drops scan_* tables only. Non-destructive — no prompt.\n With --state: also drops state_* tables. Destructive — requires\n confirmation unless --yes / --force.\n With --hard: deletes the DB file entirely. Destructive — requires\n confirmation unless --yes / --force.\n With --dry-run: previews what would be cleared / deleted without\n touching the DB. Bypasses the confirmation prompt entirely (the\n preview itself is non-destructive).\n `,\n });\n\n global = Option.Boolean('-g,--global', false);\n db = Option.String('--db', { required: false });\n state = Option.Boolean('--state', false);\n hard = Option.Boolean('--hard', false);\n yes = Option.Boolean('--yes,--force', false);\n dryRun = Option.Boolean('-n,--dry-run', false, {\n description: 'Preview the reset without dropping any tables or unlinking any files.',\n });\n\n // CLI orchestrator: --state vs --hard flag combo + --dry-run + --yes\n // confirm + per-mode actions. The early-return chain is the clearest\n // expression of the flag semantics; splitting per branch would\n // distance the validations from their guards.\n // eslint-disable-next-line complexity\n async execute(): Promise<number> {\n if (this.state && this.hard) {\n this.context.stderr.write(DB_TEXTS.resetStateAndHardMutex);\n return ExitCode.Error;\n }\n\n const path = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });\n\n if (this.hard) {\n if (this.dryRun) {\n this.context.stdout.write(DB_TEXTS.dryRunHeader);\n const dbStat = await statOrNull(path);\n const sizeBytes = dbStat ? dbStat.size : null;\n this.context.stdout.write(\n sizeBytes === null\n ? tx(DB_TEXTS.dryRunResetHardWouldDeleteMissing, { path })\n : tx(DB_TEXTS.dryRunResetHardWouldDelete, { path, sizeBytes }),\n );\n return ExitCode.Ok;\n }\n if (!this.yes) {\n const ok = await confirm(tx(DB_TEXTS.resetHardConfirm, { path }), {\n stdin: this.context.stdin,\n stderr: this.context.stderr,\n });\n if (!ok) {\n this.context.stderr.write(DB_TEXTS.aborted);\n return ExitCode.Error;\n }\n }\n for (const suffix of ['', '-wal', '-shm']) {\n const p = `${path}${suffix}`;\n if (await pathExists(p)) await rm(p);\n }\n this.context.stdout.write(tx(DB_TEXTS.resetHardDeleted, { path }));\n return ExitCode.Ok;\n }\n\n if (!assertDbExists(path, this.context.stderr)) return ExitCode.NotFound;\n\n if (this.state && !this.yes && !this.dryRun) {\n const ok = await confirm(tx(DB_TEXTS.resetStateConfirm, { path }), {\n stdin: this.context.stdin,\n stderr: this.context.stderr,\n });\n if (!ok) {\n this.context.stderr.write(DB_TEXTS.aborted);\n return ExitCode.Error;\n }\n }\n\n const db = new DatabaseSync(path);\n try {\n const rows = db\n .prepare(\n \"SELECT name FROM sqlite_master WHERE type='table' AND (name LIKE 'scan\\\\_%' ESCAPE '\\\\'\"\n + (this.state ? \" OR name LIKE 'state\\\\_%' ESCAPE '\\\\'\" : '')\n + ')',\n )\n .all() as Array<{ name: string }>;\n\n // Defence in depth — the LIKE filter above already restricts\n // results to `scan_*` (and optionally `state_*`) catalog rows, but\n // the per-plugin migration validator approves DML in plugin-owned\n // tables. A future bug there could yield a row with an unsafe\n // name reaching this loop. Whitelist + double-quote before\n // interpolating into a statement that is exec'd as-is.\n for (const r of rows) assertSafeIdentifier(r.name);\n\n if (this.dryRun) {\n this.context.stdout.write(DB_TEXTS.dryRunHeader);\n if (rows.length === 0) {\n this.context.stdout.write(DB_TEXTS.dryRunResetWouldClearNone);\n return ExitCode.Ok;\n }\n // Probe row counts so the user sees the destructive scope. Read-\n // only queries — safe in dry-run.\n const withCounts = rows.map((r) => {\n const count = db.prepare(`SELECT COUNT(*) AS c FROM \"${r.name}\"`).get() as { c: number };\n return { name: r.name, rowCount: Number(count.c) };\n });\n const totalRows = withCounts.reduce((acc, r) => acc + r.rowCount, 0);\n const lines = withCounts.map((r) => ` - ${r.name}: ${r.rowCount} row(s)`).join('\\n');\n this.context.stdout.write(\n tx(DB_TEXTS.dryRunResetWouldClearWithRowCounts, {\n tableCount: rows.length,\n totalRows,\n lines,\n }),\n );\n return ExitCode.Ok;\n }\n\n db.exec('BEGIN');\n for (const { name } of rows) {\n db.exec(`DELETE FROM \"${name}\"`);\n }\n db.exec('COMMIT');\n\n this.context.stdout.write(\n rows.length === 0\n ? DB_TEXTS.resetClearedNone\n : tx(DB_TEXTS.resetCleared, {\n tableCount: rows.length,\n tableNames: rows.map((r) => r.name).join(', '),\n }),\n );\n } finally {\n db.close();\n }\n return ExitCode.Ok;\n }\n}\n\n// --- shell ----------------------------------------------------------------\n\nexport class DbShellCommand extends Command {\n static override paths = [['db', 'shell']];\n static override usage = Command.Usage({\n category: 'Database',\n description: 'Open an interactive sqlite3 shell on the DB file.',\n details: `\n Spawns the system sqlite3 binary. If sqlite3 is not on PATH, a\n clear error points at the two workarounds: install sqlite3, or use\n sm db dump for a read-only inspection.\n `,\n });\n\n global = Option.Boolean('-g,--global', false);\n db = Option.String('--db', { required: false });\n\n async execute(): Promise<number> {\n const path = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });\n if (!assertDbExists(path, this.context.stderr)) return ExitCode.NotFound;\n\n const result = spawnSync('sqlite3', [path], { stdio: 'inherit' });\n if (result.error && (result.error as NodeJS.ErrnoException).code === 'ENOENT') {\n this.context.stderr.write(DB_TEXTS.shellSqlite3NotFound);\n return ExitCode.Error;\n }\n return result.status ?? 0;\n }\n}\n\n// --- dump -----------------------------------------------------------------\n\nexport class DbDumpCommand extends Command {\n static override paths = [['db', 'dump']];\n static override usage = Command.Usage({\n category: 'Database',\n description: 'SQL dump to stdout.',\n details: 'Read-only. Use --tables <names...> to limit the dump to specific tables.',\n });\n\n global = Option.Boolean('-g,--global', false);\n db = Option.String('--db', { required: false });\n tables = Option.Array('--tables', { required: false });\n\n // CLI orchestrator: each branch (db existence, per-table identifier\n // gate, sqlite3-not-found fallback, exit-status passthrough) is a\n // single dispatcher decision. Splitting per branch scatters the gate\n // away from the value it gates.\n // eslint-disable-next-line complexity\n async execute(): Promise<number> {\n const path = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });\n if (!assertDbExists(path, this.context.stderr)) return ExitCode.NotFound;\n\n const args = ['-readonly', path, '.dump'];\n if (this.tables && this.tables.length > 0) {\n for (const t of this.tables) {\n if (!SAFE_SQL_IDENTIFIER_RE.test(t)) {\n this.context.stderr.write(tx(DB_TEXTS.dumpInvalidTable, { table: t }));\n return ExitCode.Error;\n }\n }\n args.push(...this.tables);\n }\n const result = spawnSync('sqlite3', args, { stdio: ['ignore', 'inherit', 'inherit'] });\n if (result.error && (result.error as NodeJS.ErrnoException).code === 'ENOENT') {\n this.context.stderr.write(DB_TEXTS.dumpSqlite3NotFound);\n return ExitCode.Error;\n }\n return result.status ?? 0;\n }\n}\n\n// --- migrate --------------------------------------------------------------\n\nexport class DbMigrateCommand extends Command {\n static override paths = [['db', 'migrate']];\n static override usage = Command.Usage({\n category: 'Database',\n description: 'Apply pending kernel + plugin migrations (default) or inspect plan.',\n details: `\n --dry-run show pending migrations without applying.\n --status print applied vs pending summary and exit.\n --to <n> apply up to (and including) version N (kernel only).\n --no-backup skip the pre-apply backup.\n --kernel-only skip plugin migrations entirely.\n --plugin <id> run only that plugin's migrations (skips kernel migrations).\n\n Plugin migrations live under <plugin-dir>/migrations/ and follow\n the same NNN_snake_case.sql convention as kernel migrations. Each\n migration is gated by a triple-protection rule: every object it\n creates / alters / drops MUST live in the namespace\n \\`plugin_<normalizedId>_*\\`. Layer 1 validates every pending file\n before anything runs; Layer 2 re-validates immediately before\n apply; Layer 3 sweeps sqlite_master after apply and reports any\n object outside the prefix.\n `,\n });\n\n global = Option.Boolean('-g,--global', false);\n db = Option.String('--db', { required: false });\n dryRun = Option.Boolean('--dry-run', false);\n status = Option.Boolean('--status', false);\n to = Option.String('--to', { required: false });\n noBackup = Option.Boolean('--no-backup', false);\n kernelOnly = Option.Boolean('--kernel-only', false);\n pluginId = Option.String('--plugin', { required: false });\n\n // Multi-flag CLI orchestrator: validates flag combos, optionally\n // discovers plugins, fans out into status / apply branches against\n // both the kernel ledger and per-plugin ledgers. Splitting per branch\n // would scatter the close-to-call-site flag handling without making\n // the verb easier to follow.\n // eslint-disable-next-line complexity\n async execute(): Promise<number> {\n if (this.kernelOnly && this.pluginId !== undefined) {\n this.context.stderr.write(DB_TEXTS.migrateKernelOnlyAndPluginMutex);\n return ExitCode.Error;\n }\n\n const path = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });\n\n if (path !== ':memory:') await mkdir(dirname(path), { recursive: true });\n\n // `autoMigrate: false` keeps the adapter from running migrations\n // on init() — the verb itself orchestrates the apply (or skips it\n // for `--status` / `--dry-run`). The migrations namespace's\n // methods open their own short-lived raw `DatabaseSync` handles\n // internally; the adapter's Kysely connection is unused by this\n // verb.\n const adapter = createSqliteStorage({\n databasePath: path,\n autoMigrate: false,\n });\n await adapter.init();\n try {\n const files = adapter.migrations.discover();\n\n // --- discover plugins for everything but --kernel-only -----------\n // We always need the plugin set for `--status` and the apply path\n // when plugin migrations are in play. Skip discovery only when the\n // user explicitly asked for kernel-only mode.\n const pluginRuntime = this.kernelOnly\n ? emptyPluginRuntime()\n : await loadPluginRuntime({ scope: this.global ? 'global' : 'project' });\n for (const warn of pluginRuntime.warnings) {\n this.context.stderr.write(`${warn}\\n`);\n }\n const dedicated = pluginRuntime.discovered.filter(\n (p) => p.status === 'enabled' && p.manifest?.storage?.mode === 'dedicated',\n );\n const targetedPlugins = this.pluginId !== undefined\n ? dedicated.filter((p) => p.id === this.pluginId)\n : dedicated;\n\n if (this.pluginId !== undefined && targetedPlugins.length === 0) {\n this.context.stderr.write(\n tx(DB_TEXTS.migratePluginNotFound, { pluginId: this.pluginId }),\n );\n return ExitCode.NotFound;\n }\n\n // --- status branch (read-only summary) ---------------------------\n if (this.status) {\n if (!this.pluginId) {\n const plan = adapter.migrations.plan(files);\n this.context.stdout.write(\n tx(DB_TEXTS.migrateStatusKernelHeader, {\n applied: plan.applied.length, pending: plan.pending.length,\n }),\n );\n for (const f of plan.pending) {\n this.context.stdout.write(\n tx(DB_TEXTS.migrateStatusPending, { name: formatKernelName(f.version, f.description) }),\n );\n }\n for (const r of plan.applied) {\n this.context.stdout.write(\n tx(DB_TEXTS.migrateStatusApplied, { name: formatKernelName(r.version, r.description) }),\n );\n }\n }\n if (!this.kernelOnly) {\n for (const plugin of targetedPlugins) {\n const plan = adapter.pluginMigrations.plan(plugin);\n this.context.stdout.write(\n tx(DB_TEXTS.migrateStatusPluginHeader, {\n pluginId: plugin.id,\n applied: plan.applied.length,\n pending: plan.pending.length,\n }),\n );\n for (const f of plan.pending) {\n this.context.stdout.write(\n tx(DB_TEXTS.migrateStatusPending, { name: formatKernelName(f.version, f.description) }),\n );\n }\n for (const r of plan.applied) {\n this.context.stdout.write(\n tx(DB_TEXTS.migrateStatusApplied, { name: formatKernelName(r.version, r.description) }),\n );\n }\n }\n }\n return ExitCode.Ok;\n }\n\n // `Number.parseInt` is permissive: it accepts `'123abc'` as `123`\n // and negatives. Reject anything that isn't a clean non-negative\n // integer so a typo doesn't silently roll the migration ledger to\n // an unexpected target.\n let toValue: number | undefined;\n if (this.to !== undefined) {\n const trimmed = this.to.trim();\n const parsed = Number.parseInt(trimmed, 10);\n if (!Number.isInteger(parsed) || parsed < 0 || String(parsed) !== trimmed) {\n this.context.stderr.write(tx(DB_TEXTS.migrateInvalidTo, { to: this.to }));\n return ExitCode.Error;\n }\n toValue = parsed;\n }\n\n // --- kernel pass --------------------------------------------------\n // Skipped under `--plugin <id>`: that mode targets a single plugin\n // and is not meant to advance the kernel ledger.\n let kernelApplied: number | undefined;\n let backupPath: string | null = null;\n if (this.pluginId === undefined) {\n const options: { backup: boolean; dryRun: boolean; to?: number } = {\n backup: !this.noBackup,\n dryRun: this.dryRun,\n };\n if (toValue !== undefined) options.to = toValue;\n\n const result = adapter.migrations.apply(options, files);\n kernelApplied = result.applied.length;\n backupPath = result.backupPath;\n\n if (this.dryRun) {\n this.context.stdout.write(\n kernelApplied === 0\n ? DB_TEXTS.migrateKernelDryNothing\n : tx(DB_TEXTS.migrateKernelDryHeader, {\n count: kernelApplied,\n lines: result.applied\n .map((m) => ` ${formatKernelName(m.version, m.description)}`)\n .join('\\n'),\n }),\n );\n } else if (kernelApplied === 0) {\n this.context.stdout.write(DB_TEXTS.migrateKernelUpToDate);\n } else {\n this.context.stdout.write(\n backupPath\n ? tx(DB_TEXTS.migrateKernelAppliedWithBackup, {\n count: kernelApplied,\n backupPath,\n })\n : tx(DB_TEXTS.migrateKernelApplied, { count: kernelApplied }),\n );\n }\n }\n\n // --- plugin pass --------------------------------------------------\n if (!this.kernelOnly) {\n const exitCode = await runPluginMigrations({\n adapter,\n plugins: targetedPlugins,\n dryRun: this.dryRun,\n stdout: this.context.stdout,\n stderr: this.context.stderr,\n });\n if (exitCode !== 0) return exitCode;\n }\n\n return ExitCode.Ok;\n } finally {\n await adapter.close();\n }\n }\n}\n\ninterface IRunPluginMigrationsOpts {\n adapter: StoragePort;\n plugins: IDiscoveredPlugin[];\n dryRun: boolean;\n stdout: NodeJS.WritableStream;\n stderr: NodeJS.WritableStream;\n}\n\n/**\n * Drive every targeted plugin's migration batch in sequence. Layer-3\n * intrusions are reported on stderr and flip the exit code to 2 — the\n * ledger row is still written for whatever applied cleanly, but the\n * caller knows something deeper is off (a plugin slipped a non-prefixed\n * object past the regex check). This is the intentional contract: don't\n * silently revert, surface the breach loud and clear.\n */\nasync function runPluginMigrations(opts: IRunPluginMigrationsOpts): Promise<number> {\n const { adapter, plugins, dryRun, stdout, stderr } = opts;\n let exit = 0;\n for (const plugin of plugins) {\n let result: IPluginApplyResult;\n try {\n result = adapter.pluginMigrations.apply(plugin, { dryRun });\n } catch (err) {\n const reason = formatErrorMessage(err);\n stderr.write(tx(DB_TEXTS.pluginMigrateFailure, { pluginId: plugin.id, reason }));\n exit = ExitCode.Error;\n continue;\n }\n if (dryRun) {\n stdout.write(\n result.applied.length === 0\n ? tx(DB_TEXTS.pluginMigrateDryNothing, { pluginId: plugin.id })\n : tx(DB_TEXTS.pluginMigrateDryHeader, {\n pluginId: plugin.id,\n count: result.applied.length,\n lines: result.applied\n .map((m) => ` ${formatKernelName(m.version, m.description)}`)\n .join('\\n'),\n }),\n );\n } else {\n stdout.write(\n result.applied.length === 0\n ? tx(DB_TEXTS.pluginMigrateUpToDate, { pluginId: plugin.id })\n : tx(DB_TEXTS.pluginMigrateApplied, {\n pluginId: plugin.id,\n count: result.applied.length,\n }),\n );\n }\n if (result.intrusions.length > 0) {\n stderr.write(\n tx(DB_TEXTS.pluginMigrateIntrusion, {\n pluginId: plugin.id,\n intrusions: result.intrusions.join(', '),\n }),\n );\n exit = ExitCode.Error;\n }\n }\n return exit;\n}\n\nfunction formatKernelName(version: number, description: string): string {\n return `${String(version).padStart(3, '0')}_${description}`;\n}\n\n/** Aggregate export so CLI entry can register every db verb in one line. */\nexport const DB_COMMANDS = [\n DbBackupCommand,\n DbRestoreCommand,\n DbResetCommand,\n DbShellCommand,\n DbDumpCommand,\n DbMigrateCommand,\n];\n\n","/**\n * Interactive yes/no prompt helper used by destructive verbs\n * (`sm db restore`, `sm db reset --state`, `sm db reset --hard`,\n * `sm orphans undo-rename`).\n *\n * Writes the question + `[y/N] ` suffix to the supplied `stderr`. The\n * affirmative-answer regex is sourced from `UTIL_TEXTS` so a future\n * non-English locale can extend the alternation without touching this\n * helper. Match is trimmed and case-insensitive; any other answer\n * (including empty) returns false.\n *\n * Streams are supplied by the caller (typically `this.context.stdin` /\n * `this.context.stderr` from Clipanion) so commands can be tested with\n * captured streams instead of monkey-patching `process.*`.\n */\n\nimport { createInterface } from 'node:readline';\n\nimport type { Readable, Writable } from 'node:stream';\n\nimport { UTIL_TEXTS } from '../i18n/util.texts.js';\n\nexport interface IConfirmStreams {\n stdin: Readable;\n stderr: Writable;\n}\n\nconst YES_PATTERN = new RegExp(UTIL_TEXTS.confirmYesPatternSource, 'i');\n\nexport async function confirm(question: string, streams: IConfirmStreams): Promise<boolean> {\n const rl = createInterface({ input: streams.stdin, output: streams.stderr });\n try {\n const answer = await new Promise<string>((resolveP) =>\n rl.question(`${question}${UTIL_TEXTS.confirmPromptSuffix}`, resolveP),\n );\n return YES_PATTERN.test(answer.trim());\n } finally {\n rl.close();\n }\n}\n","/**\n * CLI strings emitted by `sm db *` — `cli/commands/db.ts`.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n *\n * Includes the `--dry-run` previews for `sm db reset` (default /\n * --state / --hard) and `sm db restore`, per `cli-contract.md`\n * §Dry-run.\n */\n\nexport const DB_TEXTS = {\n // --- reset -----------------------------------------------------------\n resetStateAndHardMutex: '--state and --hard are mutually exclusive.\\n',\n\n resetCleared: 'Cleared {{tableCount}} table(s): {{tableNames}}\\n',\n resetClearedNone: 'Cleared 0 table(s): (none)\\n',\n\n resetHardConfirm: 'Delete DB file {{path}}?',\n resetHardDeleted: 'Deleted {{path}}\\n',\n\n resetStateConfirm: 'Drop scan_* AND state_* in {{path}}?',\n\n // --- restore ---------------------------------------------------------\n restoreSourceNotFound: 'Backup not found: {{sourcePath}}\\n',\n restoreConfirm: 'Restore {{sourcePath}} over {{target}}? This overwrites the current DB.',\n restoreDone: 'Restored {{sourcePath}} → {{target}}\\n',\n\n // --- shared ----------------------------------------------------------\n aborted: 'Aborted.\\n',\n backupWritten: 'Backup written: {{outPath}}\\n',\n\n // --- migrate (sm db migrate) -----------------------------------------\n migrateKernelOnlyAndPluginMutex: '--kernel-only and --plugin are mutually exclusive.\\n',\n migratePluginNotFound:\n '--plugin {{pluginId}}: no loaded plugin with that id and `storage.mode = \"dedicated\"`.\\n',\n migrateStatusKernelHeader: 'kernel · Applied: {{applied}} · Pending: {{pending}}\\n',\n migrateStatusPluginHeader:\n '\\nplugin {{pluginId}} · Applied: {{applied}} · Pending: {{pending}}\\n',\n migrateStatusPending: ' pending {{name}}\\n',\n migrateStatusApplied: ' applied {{name}}\\n',\n migrateInvalidTo: '--to expects an integer, got {{to}}\\n',\n\n // --- migrate kernel apply / dry-run output ---------------------------\n migrateKernelDryNothing: 'kernel · Nothing to apply.\\n',\n migrateKernelDryHeader: 'kernel · Would apply {{count}} migration(s):\\n{{lines}}\\n',\n migrateKernelUpToDate: 'kernel · Already up to date.\\n',\n migrateKernelApplied: 'kernel · Applied {{count}} migration(s)\\n',\n migrateKernelAppliedWithBackup:\n 'kernel · Applied {{count}} migration(s) · backup: {{backupPath}}\\n',\n\n // --- shell / dump (system sqlite3 binary required) ------------------\n shellSqlite3NotFound:\n 'sqlite3 binary not found on PATH. Install it (macOS: brew install sqlite; Debian/Ubuntu: apt install sqlite3) or use `sm db dump` for read-only inspection.\\n',\n dumpSqlite3NotFound:\n 'sqlite3 binary not found on PATH. Install it to use `sm db dump`.\\n',\n dumpInvalidTable:\n '--tables: refusing non-identifier name {{table}}. Table names must match [a-zA-Z_][a-zA-Z0-9_]*\\n',\n\n // --- plugin migration runner -----------------------------------------\n pluginMigrateFailure: 'plugin {{pluginId}} · {{reason}}\\n',\n pluginMigrateDryNothing: 'plugin {{pluginId}} · Nothing to apply.\\n',\n pluginMigrateDryHeader:\n 'plugin {{pluginId}} · Would apply {{count}} migration(s):\\n{{lines}}\\n',\n pluginMigrateUpToDate: 'plugin {{pluginId}} · Already up to date.\\n',\n pluginMigrateApplied: 'plugin {{pluginId}} · Applied {{count}} migration(s)\\n',\n pluginMigrateIntrusion:\n 'plugin {{pluginId}} · catalog intrusion detected: {{intrusions}}\\n',\n\n // --- dry-run previews ------------------------------------------------\n dryRunHeader: '(dry-run — no DB writes, no file unlinks)\\n',\n\n dryRunResetWouldClearNone:\n 'would clear 0 table(s): (none — DB schema is empty)\\n',\n\n // The `lines` arg is a pre-built multi-line block, one \" - name: N row(s)\"\n // per table, joined with `\\n`.\n dryRunResetWouldClearWithRowCounts:\n 'would clear {{tableCount}} table(s) ({{totalRows}} total row(s)):\\n{{lines}}\\n',\n\n dryRunResetHardWouldDelete: 'would delete {{path}} ({{sizeBytes}} bytes)\\n',\n dryRunResetHardWouldDeleteMissing:\n 'would delete {{path}} (file does not exist — no-op)\\n',\n\n // The `targetClause` arg is one of two pre-built strings:\n // \"(exists, would be overwritten)\" / \"(does not exist, would be created)\".\n dryRunRestoreWouldOverwrite:\n 'would copy {{sourcePath}} ({{sourceBytes}} bytes) → {{target}} {{targetClause}}\\n' +\n 'would delete {{target}}-wal and {{target}}-shm sidecars if present\\n',\n\n dryRunRestoreTargetExistsClause: '(exists, would be overwritten)',\n dryRunRestoreTargetMissingClause: '(does not exist, would be created)',\n} as const;\n","/**\n * `sm export <query> --format <json|md|mermaid>`\n *\n * Filtered export over the persisted graph. Reads the DB, parses the\n * query (see `src/kernel/scan/query.ts` for the grammar), applies the\n * filter, and emits the selected subset in the requested format.\n *\n * Read-only: opens the DB, calls `loadScanResult`, never persists.\n *\n * Exit codes (per `spec/cli-contract.md` §Exit codes):\n * 0 ok\n * 2 bad flag / unsupported format / invalid query / unhandled error\n * 5 DB missing\n *\n * **Format support at v0.5.0**: `json` and `md` are real; `mermaid`\n * exits 2 with a clear pointer to Step 12, when the mermaid formatter\n * lands as a built-in. Wiring the format here ahead of the formatter\n * would require a synthesis layer this verb shouldn't carry.\n */\n\nimport { Command, Option } from 'clipanion';\n\nimport {\n applyExportQuery,\n ExportQueryError,\n parseExportQuery,\n} from '../../kernel/scan/query.js';\nimport type { IExportSubset } from '../../kernel/scan/query.js';\nimport type { Issue, Link, Node } from '../../kernel/types.js';\nimport { assertDbExists, resolveDbPath } from '../util/db-path.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { sanitizeForTerminal } from '../../kernel/util/safe-text.js';\nimport { EXPORT_TEXTS } from '../i18n/export.texts.js';\nimport { withSqlite } from '../util/with-sqlite.js';\n\n// Built-in Claude Provider catalog rendered first, in this canonical\n// order. External Providers may emit additional kinds; those are\n// rendered after, sorted alphabetically (see `renderNodesByKindSection`).\nconst KIND_ORDER: readonly string[] = ['agent', 'command', 'hook', 'skill', 'note'];\nconst SUPPORTED_FORMATS = ['json', 'md'] as const;\nconst DEFERRED_FORMATS: Record<string, string> = {\n mermaid: EXPORT_TEXTS.formatDeferredReasonMermaid,\n};\n\nexport class ExportCommand extends Command {\n static override paths = [['export']];\n static override usage = Command.Usage({\n category: 'Browse',\n description: 'Filtered export. Query syntax is implementation-defined pre-1.0.',\n details: `\n Reads the persisted scan, applies the query filter, and emits the\n selected subset.\n\n Query syntax (v0.5.0): whitespace-separated key=value tokens; AND\n across keys, OR within comma-separated values. Keys: \\`kind\\`\n (skill / agent / command / hook / note), \\`has\\` (issues), \\`path\\`\n (POSIX glob — \\`*\\` matches a single segment, \\`**\\` matches across\n segments).\n\n Pass an empty query (\\`\"\"\\`) to export every node.\n\n Run \\`sm scan\\` first to populate the DB.\n `,\n examples: [\n ['Every command node', '$0 export \"kind=command\" --format json'],\n ['Skills + agents with issues', '$0 export \"kind=skill,agent has=issues\" --format md'],\n ['Files under a path glob', '$0 export \"path=.claude/commands/**\" --format json'],\n ['Whole graph as Markdown', '$0 export \"\" --format md'],\n ],\n });\n\n query = Option.String({ required: true });\n format = Option.String('--format', { required: false });\n global = Option.Boolean('-g,--global', false);\n db = Option.String('--db', { required: false });\n\n async execute(): Promise<number> {\n const format = (this.format ?? 'json').toLowerCase();\n if (DEFERRED_FORMATS[format]) {\n this.context.stderr.write(\n tx(EXPORT_TEXTS.formatNotImplemented, {\n format,\n reason: DEFERRED_FORMATS[format],\n }),\n );\n return ExitCode.Error;\n }\n if (!(SUPPORTED_FORMATS as readonly string[]).includes(format)) {\n this.context.stderr.write(\n tx(EXPORT_TEXTS.formatUnsupported, {\n format,\n supported: SUPPORTED_FORMATS.join(', '),\n deferred: Object.keys(DEFERRED_FORMATS).join(', '),\n }),\n );\n return ExitCode.Error;\n }\n\n let parsedQuery;\n try {\n parsedQuery = parseExportQuery(this.query);\n } catch (err) {\n if (err instanceof ExportQueryError) {\n this.context.stderr.write(tx(EXPORT_TEXTS.errorPrefix, { message: err.message }));\n return ExitCode.Error;\n }\n throw err;\n }\n\n const dbPath = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });\n if (!assertDbExists(dbPath, this.context.stderr)) return ExitCode.NotFound;\n\n return withSqlite({ databasePath: dbPath, autoBackup: false }, async (adapter) => {\n const scan = await adapter.scans.load();\n const subset = applyExportQuery(\n { nodes: scan.nodes, links: scan.links, issues: scan.issues },\n parsedQuery,\n );\n\n if (format === 'json') {\n this.context.stdout.write(JSON.stringify(serialiseSubset(subset)) + '\\n');\n return ExitCode.Ok;\n }\n // format === 'md'\n this.context.stdout.write(renderMarkdown(subset));\n return ExitCode.Ok;\n });\n }\n}\n\nfunction serialiseSubset(subset: IExportSubset): {\n query: string;\n filters: { kinds?: string[]; hasIssues?: boolean; pathGlobs?: string[] };\n counts: { nodes: number; links: number; issues: number };\n nodes: Node[];\n links: Link[];\n issues: Issue[];\n} {\n const filters: ReturnType<typeof serialiseSubset>['filters'] = {};\n if (subset.query.kinds) filters.kinds = subset.query.kinds;\n if (subset.query.hasIssues) filters.hasIssues = true;\n if (subset.query.pathGlobs) filters.pathGlobs = subset.query.pathGlobs;\n return {\n query: subset.query.raw,\n filters,\n counts: {\n nodes: subset.nodes.length,\n links: subset.links.length,\n issues: subset.issues.length,\n },\n nodes: subset.nodes,\n links: subset.links,\n issues: subset.issues,\n };\n}\n\nfunction renderMarkdown(subset: IExportSubset): string {\n const out: string[] = [];\n out.push(EXPORT_TEXTS.mdTitle);\n out.push('');\n out.push(\n tx(EXPORT_TEXTS.mdQueryLine, {\n query: subset.query.raw || EXPORT_TEXTS.mdQueryEmpty,\n }),\n );\n out.push(\n tx(EXPORT_TEXTS.mdCounts, {\n nodes: subset.nodes.length,\n links: subset.links.length,\n issues: subset.issues.length,\n }),\n );\n out.push('');\n\n const issuesPerNode = countIssuesPerNode(subset.issues);\n out.push(...renderNodesByKindSection(subset.nodes, issuesPerNode));\n\n if (subset.links.length > 0) {\n out.push(tx(EXPORT_TEXTS.mdLinksSectionHeader, { count: subset.links.length }));\n out.push('');\n const sorted = [...subset.links].sort((a, b) => {\n const aKey = `${a.source}\\x00${a.kind}\\x00${a.target}`;\n const bKey = `${b.source}\\x00${b.kind}\\x00${b.target}`;\n return aKey.localeCompare(bKey);\n });\n for (const link of sorted) {\n out.push(\n tx(EXPORT_TEXTS.mdLinkBullet, {\n source: sanitizeForTerminal(link.source),\n kind: sanitizeForTerminal(link.kind),\n target: sanitizeForTerminal(link.target),\n confidence: link.confidence,\n }),\n );\n }\n out.push('');\n }\n\n if (subset.issues.length > 0) {\n out.push(tx(EXPORT_TEXTS.mdIssuesSectionHeader, { count: subset.issues.length }));\n out.push('');\n for (const issue of subset.issues) {\n out.push(\n tx(EXPORT_TEXTS.mdIssueBullet, {\n severity: issue.severity,\n ruleId: sanitizeForTerminal(issue.ruleId),\n message: sanitizeForTerminal(issue.message),\n }),\n );\n }\n out.push('');\n }\n\n return out.join('\\n');\n}\n\n/** Index issues by node path so the per-kind renderer can show issue counts. */\nfunction countIssuesPerNode(issues: Issue[]): Map<string, number> {\n const issuesPerNode = new Map<string, number>();\n for (const issue of issues) {\n for (const id of issue.nodeIds) {\n issuesPerNode.set(id, (issuesPerNode.get(id) ?? 0) + 1);\n }\n }\n return issuesPerNode;\n}\n\n/**\n * Render the nodes-by-kind sections of the markdown export. Groups\n * nodes per kind in `KIND_ORDER`, sorts each group by path, and emits\n * `## <kind> (N)` headers followed by `- \\`<path>\\` — \"<title>\" — N\n * issues` bullets.\n */\nfunction renderNodesByKindSection(\n nodes: Node[],\n issuesPerNode: Map<string, number>,\n): string[] {\n const byKind = new Map<string, Node[]>();\n for (const node of nodes) {\n if (!byKind.has(node.kind)) byKind.set(node.kind, []);\n byKind.get(node.kind)!.push(node);\n }\n\n // Built-in Claude catalog first in canonical order; external-Provider\n // kinds appended after, alphabetically sorted, so the output is\n // deterministic across runs even with arbitrary kind sets.\n const lines: string[] = [];\n const renderedKinds = new Set<string>();\n const orderedKinds: string[] = [\n ...KIND_ORDER,\n ...[...byKind.keys()].filter((k) => !KIND_ORDER.includes(k)).sort(),\n ];\n for (const kind of orderedKinds) {\n if (renderedKinds.has(kind)) continue;\n const group = byKind.get(kind);\n if (!group || group.length === 0) continue;\n appendKindSection(lines, kind, group, issuesPerNode);\n renderedKinds.add(kind);\n }\n return lines;\n}\n\nfunction appendKindSection(\n lines: string[],\n kind: string,\n group: Node[],\n issuesPerNode: Map<string, number>,\n): void {\n const sorted = [...group].sort((a, b) => a.path.localeCompare(b.path));\n lines.push(\n tx(EXPORT_TEXTS.mdKindSectionHeader, {\n kind: sanitizeForTerminal(kind),\n count: sorted.length,\n }),\n );\n lines.push('');\n for (const node of sorted) lines.push(renderNodeBullet(node, issuesPerNode));\n lines.push('');\n}\n\n/** Render one node as a markdown bullet, with optional title + issue count. */\nfunction renderNodeBullet(node: Node, issuesPerNode: Map<string, number>): string {\n const title = pickTitle(node);\n const issueCount = issuesPerNode.get(node.path) ?? 0;\n const titleSegment = title\n ? tx(EXPORT_TEXTS.mdNodeTitleSuffix, { title: sanitizeForTerminal(title) })\n : '';\n const issuesSegment = issueCount > 0\n ? tx(EXPORT_TEXTS.mdNodeIssueSuffix, {\n count: issueCount,\n label: issueCount === 1\n ? EXPORT_TEXTS.mdNodeIssueLabelSingular\n : EXPORT_TEXTS.mdNodeIssueLabelPlural,\n })\n : '';\n return tx(EXPORT_TEXTS.mdNodeBullet, {\n path: sanitizeForTerminal(node.path),\n title: titleSegment,\n issues: issuesSegment,\n });\n}\n\nfunction pickTitle(node: Node): string | null {\n if (node.title) return node.title;\n const name = node.frontmatter?.['name'];\n return typeof name === 'string' ? name : null;\n}\n","/**\n * Export query — minimal filter language for `sm export <query>` (Step 8.3).\n *\n * Spec contract: `spec/cli-contract.md` line 190 says \"Query syntax is\n * implementation-defined pre-1.0\". This module defines the v0.5.0 syntax.\n *\n * **Grammar** (BNF-ish, intentionally tiny):\n *\n * query := token (WS+ token)*\n * token := key \"=\" value-list\n * key := \"kind\" | \"has\" | \"path\"\n * value-list := value (\",\" value)*\n * value := non-comma, non-whitespace string\n *\n * Tokens AND together; values within one token OR. An empty / whitespace-only\n * query is valid and matches every node (\"export everything\").\n *\n * **Filters**:\n *\n * - `kind=skill` / `kind=skill,agent` — node kind whitelist.\n * - `has=issues` — node must appear in some issue's `nodeIds`. (Future\n * expansion: `has=findings` / `has=summary` once Step 10 / 11 land.\n * Unknown values are a parse error today; we'll ratchet up the\n * accepted set additively.)\n * - `path=foo/*` / `path=.claude/agents/**` — POSIX glob over `node.path`.\n * Supports `*` (any chars except `/`) and `**` (any chars including `/`).\n *\n * **Subset semantics** (`applyExportQuery`):\n *\n * - Nodes pass when every specified filter matches (AND across keys,\n * OR within values).\n * - Links survive only when BOTH endpoints (`source` + `target`) belong\n * to the filtered node set. A subset that includes \"edges out to\n * unfiltered nodes\" would be confusing — the user asked for a focused\n * subgraph, not its boundary. External-URL pseudo-links are already\n * stripped by the orchestrator and never reach this layer.\n * - Issues survive when ANY of the issue's `nodeIds` is in the filtered\n * set. Issues span multiple nodes (e.g. `trigger-collision` over two\n * advertisers); dropping an issue when one of its nodes is outside\n * would hide cross-cutting problems the user is investigating.\n *\n * Pure: no IO, no DB, no FS.\n */\n\nimport type { Issue, Link, Node } from '../types.js';\nimport { QUERY_TEXTS } from '../i18n/storage.texts.js';\nimport { tx } from '../util/tx.js';\n\nconst HAS_VALUES = new Set(['issues']);\n\nexport interface IExportQuery {\n /** Original query string echoed back so consumers can render the header. */\n raw: string;\n /**\n * Whitelist of node kinds (`node.kind` is open string — built-in\n * Claude catalog `skill` / `agent` / `command` / `hook` / `note`,\n * plus whatever external Providers declare). The query parser does\n * not validate values against a closed enum; an unknown kind simply\n * yields zero matches at filter time.\n */\n kinds?: string[];\n hasIssues?: boolean;\n pathGlobs?: string[];\n}\n\nexport interface IExportSubset {\n query: IExportQuery;\n nodes: Node[];\n links: Link[];\n issues: Issue[];\n}\n\nexport class ExportQueryError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ExportQueryError';\n }\n}\n\n// Token-by-token parser with switch over keys + per-key validators.\n// Branching is intrinsic to the multi-key query grammar.\n// eslint-disable-next-line complexity\nexport function parseExportQuery(raw: string): IExportQuery {\n const trimmed = raw.trim();\n const out: IExportQuery = { raw: trimmed };\n if (trimmed.length === 0) return out;\n\n // Tokens are whitespace-separated key=value pairs. Values within one\n // token are comma-separated (multi-value OR). Keys repeated across\n // tokens are an error — the user should comma-separate within one\n // token instead, which is the documented form.\n const seen = new Set<string>();\n for (const token of trimmed.split(/\\s+/)) {\n const eq = token.indexOf('=');\n if (eq <= 0 || eq === token.length - 1) {\n throw new ExportQueryError(\n tx(QUERY_TEXTS.exportQueryInvalidToken, { token }),\n );\n }\n const key = token.slice(0, eq).toLowerCase();\n const valuePart = token.slice(eq + 1);\n if (seen.has(key)) {\n throw new ExportQueryError(\n tx(QUERY_TEXTS.exportQueryDuplicateKey, { key }),\n );\n }\n seen.add(key);\n\n const values = valuePart.split(',').map((v) => v.trim()).filter((v) => v.length > 0);\n if (values.length === 0) {\n throw new ExportQueryError(tx(QUERY_TEXTS.exportQueryEmptyValues, { key }));\n }\n\n switch (key) {\n case 'kind':\n out.kinds = parseKindValues(values);\n break;\n case 'has':\n if (parseHasValues(values)) out.hasIssues = true;\n break;\n case 'path':\n out.pathGlobs = values;\n break;\n default:\n throw new ExportQueryError(\n tx(QUERY_TEXTS.exportQueryUnknownKey, { key }),\n );\n }\n }\n\n return out;\n}\n\n/**\n * Validate every token of a `kind=...` clause. Per\n * `node.schema.json#/properties/kind`, kinds are an open string — any\n * non-empty value is structurally valid. We still reject empty tokens\n * (a typo like `kind=,skill` shouldn't silently match every node).\n * Unknown-but-non-empty kinds simply yield zero matches at filter time.\n */\nfunction parseKindValues(values: string[]): string[] {\n for (const v of values) {\n if (v.length === 0) {\n throw new ExportQueryError(QUERY_TEXTS.exportQueryEmptyKind);\n }\n }\n return values;\n}\n\n/** Validate every token of a `has=...` clause; returns true iff `issues` is present. */\nfunction parseHasValues(values: string[]): boolean {\n for (const v of values) {\n if (!HAS_VALUES.has(v)) {\n throw new ExportQueryError(\n tx(QUERY_TEXTS.exportQueryUnsupportedHas, {\n value: v,\n allowed: [...HAS_VALUES].join(', '),\n }),\n );\n }\n }\n return values.includes('issues');\n}\n\nexport function applyExportQuery(\n scan: { nodes: Node[]; links: Link[]; issues: Issue[] },\n query: IExportQuery,\n): IExportSubset {\n const nodesWithIssues = query.hasIssues\n ? collectNodesWithIssues(scan.issues)\n : null;\n const compiledGlobs = query.pathGlobs\n ? query.pathGlobs.map(compileGlob)\n : null;\n\n const filteredNodes = scan.nodes.filter((node) => {\n if (query.kinds && !query.kinds.includes(node.kind)) return false;\n if (nodesWithIssues && !nodesWithIssues.has(node.path)) return false;\n if (compiledGlobs && !compiledGlobs.some((re) => re.test(node.path))) return false;\n return true;\n });\n\n const survivingPaths = new Set(filteredNodes.map((n) => n.path));\n\n // Links: both endpoints must survive. See module-level commentary on\n // why we close the subgraph instead of carrying boundary edges.\n const filteredLinks = scan.links.filter(\n (link) => survivingPaths.has(link.source) && survivingPaths.has(link.target),\n );\n\n // Issues: any node in the issue's nodeIds being in scope keeps the\n // issue. See module-level commentary on why we don't require all.\n const filteredIssues = scan.issues.filter((issue) =>\n issue.nodeIds.some((id) => survivingPaths.has(id)),\n );\n\n return {\n query,\n nodes: filteredNodes,\n links: filteredLinks,\n issues: filteredIssues,\n };\n}\n\nfunction collectNodesWithIssues(issues: Issue[]): Set<string> {\n const out = new Set<string>();\n for (const issue of issues) {\n for (const nodeId of issue.nodeIds) out.add(nodeId);\n }\n return out;\n}\n\n/**\n * Compile a minimal POSIX glob into a RegExp. Supports:\n *\n * - `*` — any sequence of chars except `/` (single segment wildcard).\n * - `**` — any sequence of chars including `/` (cross-segment wildcard).\n * - everything else is literal (regex metacharacters escaped).\n *\n * No `?`, no `[abc]`, no brace expansion. The grammar is explicitly\n * minimal so the spec doesn't bind us to a specific glob library before\n * v1.0; we can grow this when consumers ask for it.\n */\nfunction compileGlob(pattern: string): RegExp {\n // First escape every regex metachar EXCEPT `*` (which we'll process\n // in a second pass). A negated character class is the cleanest way\n // to enumerate \"everything that needs escaping in a path glob\".\n const escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&');\n // `**` first so the `*` pass below doesn't double-process it. Use a\n // sentinel that can't appear in user input post-escape.\n const withDouble = escaped.replace(/\\*\\*/g, '\u0000DOUBLESTAR\u0000');\n const withSingle = withDouble.replace(/\\*/g, '[^/]*');\n // Null-byte sentinel is intentional — guarantees the marker can't\n // collide with anything in user-supplied glob patterns post-escape.\n // eslint-disable-next-line no-control-regex\n const final = withSingle.replace(/\u0000DOUBLESTAR\u0000/g, '.*');\n return new RegExp(`^${final}$`);\n}\n","/**\n * Strings emitted by `cli/commands/export.ts`.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const EXPORT_TEXTS = {\n errorPrefix: 'sm export: {{message}}\\n',\n\n formatNotImplemented: 'format={{format}} not yet implemented ({{reason}}).\\n',\n formatUnsupported:\n 'Unsupported format: {{format}}. Supported: {{supported}}. Deferred: {{deferred}}.\\n',\n\n /**\n * Reason emitted by `formatNotImplemented` when the user asks for\n * `--format mermaid`. Pre-1.0 placeholder until the formatter lands as\n * a built-in.\n */\n formatDeferredReasonMermaid: 'lands at Step 12 with the mermaid formatter',\n\n // --- markdown body ---------------------------------------------------------\n /** Top-level heading for the markdown export. */\n mdTitle: '# skill-map export',\n /** Echo of the user's query string (or the empty placeholder). */\n mdQueryLine: 'Query: `{{query}}`',\n /** Placeholder used when the user's query is empty. */\n mdQueryEmpty: '(empty — all nodes)',\n /** Counts summary line under the query. */\n mdCounts:\n 'Counts: {{nodes}} nodes, {{links}} links, {{issues}} issues.',\n\n /** Section header for a single node-kind group. */\n mdKindSectionHeader: '## {{kind}} ({{count}})',\n\n /** Bullet template for a node row. `{{title}}` and `{{issues}}` are pre-rendered (empty when absent). */\n mdNodeBullet: '- `{{path}}`{{title}}{{issues}}',\n /** ` — \"<title>\"` segment when the node has a title. */\n mdNodeTitleSuffix: ' — \"{{title}}\"',\n /** ` — N issue(s)` segment when the node has any associated issues. */\n mdNodeIssueSuffix: ' — {{count}} {{label}}',\n mdNodeIssueLabelSingular: 'issue',\n mdNodeIssueLabelPlural: 'issues',\n\n /** Section header for the links block. */\n mdLinksSectionHeader: '## links ({{count}})',\n /** Bullet template for one link row. */\n mdLinkBullet:\n '- `{{source}}` --{{kind}}--> `{{target}}` _[{{confidence}}]_',\n\n /** Section header for the issues block. */\n mdIssuesSectionHeader: '## issues ({{count}})',\n /** Bullet template for one issue row. */\n mdIssueBullet:\n '- **[{{severity}}]** `{{ruleId}}`: {{message}}',\n} as const;\n","/**\n * `sm graph [--format <name>]`\n *\n * Renders the persisted graph through a registered formatter and writes\n * the result to stdout. Default `--format ascii` (the only built-in\n * formatter at v0.5.0; mermaid / dot land at Step 12 as drop-in additions).\n *\n * Read-only: opens the DB, calls `loadScanResult`, picks the formatter\n * whose `formatId` matches `--format`, and prints. Never persists.\n *\n * Exit codes (per `spec/cli-contract.md` §Exit codes):\n * 0 ok\n * 2 bad flag / no formatter registered / unhandled error\n * 5 DB missing\n *\n * Formatter registry: built-in formatters plus drop-in plugin formatters\n * discovered under `.skill-map/plugins/` and `~/.skill-map/plugins/`\n * (Step 9.1). Failed plugins emit one stderr warning each; the verb\n * keeps running on whatever loaded successfully. Pass `--no-plugins`\n * to skip plugin discovery entirely.\n */\n\nimport { Command, Option } from 'clipanion';\n\nimport { tx } from '../../kernel/util/tx.js';\nimport { GRAPH_TEXTS } from '../i18n/graph.texts.js';\nimport { assertDbExists, resolveDbPath } from '../util/db-path.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport {\n composeFormatters,\n emptyPluginRuntime,\n loadPluginRuntime,\n} from '../util/plugin-runtime.js';\nimport { withSqlite } from '../util/with-sqlite.js';\n\nconst DEFAULT_FORMAT = 'ascii';\n\nexport class GraphCommand extends Command {\n static override paths = [['graph']];\n static override usage = Command.Usage({\n category: 'Browse',\n description: 'Render the full graph via the named formatter.',\n details: `\n Reads the persisted scan and prints a textual rendering. The\n built-in \\`ascii\\` formatter is the only format available at\n v0.5.0; \\`mermaid\\` and \\`dot\\` are deferred to Step 12 and will\n surface here automatically once they ship as built-ins.\n\n Run \\`sm scan\\` first to populate the DB.\n `,\n examples: [\n ['Render the graph as ASCII (default)', '$0 graph'],\n ['Render with an explicit format', '$0 graph --format ascii'],\n ['Use a non-default DB file', '$0 graph --db /path/to/skill-map.db'],\n ],\n });\n\n format = Option.String('--format', DEFAULT_FORMAT, {\n description: `Formatter format. Must match the \\`formatId\\` field of a registered formatter. Default: ${DEFAULT_FORMAT}.`,\n });\n global = Option.Boolean('-g,--global', false);\n db = Option.String('--db', { required: false });\n noPlugins = Option.Boolean('--no-plugins', false, {\n description: 'Skip drop-in plugin discovery. Only built-in formatters participate.',\n });\n\n async execute(): Promise<number> {\n const dbPath = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });\n if (!assertDbExists(dbPath, this.context.stderr)) return ExitCode.NotFound;\n\n const pluginRuntime = this.noPlugins\n ? emptyPluginRuntime()\n : await loadPluginRuntime({ scope: this.global ? 'global' : 'project' });\n for (const warn of pluginRuntime.warnings) this.context.stderr.write(`${warn}\\n`);\n\n const formatters = composeFormatters({ pluginRuntime });\n const formatter = formatters.find((f) => f.formatId === this.format);\n if (!formatter) {\n const available = formatters\n .map((f) => f.formatId)\n .sort()\n .join(', ');\n this.context.stderr.write(\n tx(GRAPH_TEXTS.noFormatterRegistered, {\n format: this.format,\n available: available || GRAPH_TEXTS.availableNone,\n }),\n );\n return ExitCode.Error;\n }\n\n return withSqlite({ databasePath: dbPath, autoBackup: false }, async (adapter) => {\n const scan = await adapter.scans.load();\n const text = formatter.format({\n nodes: scan.nodes,\n links: scan.links,\n issues: scan.issues,\n });\n // Formatter output is text; trailing newline normalisation makes the\n // verb safe to pipe into anything that splits on lines without\n // double-newlining when the formatter already terminates its output.\n this.context.stdout.write(text.endsWith('\\n') ? text : text + '\\n');\n return ExitCode.Ok;\n });\n }\n}\n\n","/**\n * CLI strings emitted by `sm graph` (`cli/commands/graph.ts`).\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const GRAPH_TEXTS = {\n noFormatterRegistered:\n 'No formatter registered for format={{format}}. Available: {{available}}.\\n',\n\n availableNone: '(none)',\n} as const;\n","/**\n * `sm help [<verb>] [--format human|md|json]`\n *\n * Self-describing introspection over the registered command surface. The\n * shape of the JSON output is normative (see spec/cli-contract.md §Help)\n * so third-party tooling — docs generator, shell completion, Web UI form\n * generation, IDE extensions, test harness, sm-cli skill — can rely on it.\n *\n * `human` delegates to Clipanion's own Cli.usage() for overview and\n * Cli.usage(command) for a specific verb so we match the built-in\n * formatting exactly. `md` emits canonical markdown grouped by category;\n * `context/cli-reference.md` is regenerated from this and diffed in CI.\n * `json` emits the structured surface dump.\n */\n\nimport { readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { resolve } from 'node:path';\n\nimport { Cli, Command, Option } from 'clipanion';\n\nimport { ExitCode } from '../util/exit-codes.js';\nimport { BINARY_LABEL, VERSION } from '../version.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { HELP_TEXTS } from '../i18n/help.texts.js';\n\ntype THelpFormat = 'human' | 'md' | 'json';\n\ninterface ICliDefinition {\n /** Just the verb path: `sm scan compare-with`. No options or positionals. */\n path: string;\n /**\n * Detailed usage line as Clipanion would render it, e.g.\n * `sm scan compare-with [--json] [--no-tokens] <dump> ...`. Used to\n * extract positional fragments since `path` strips them.\n */\n usage: string;\n category?: string;\n description?: string;\n details?: string;\n examples?: Array<[string, string]>;\n options?: Array<{\n preferredName: string;\n nameSet: string[];\n definition: string;\n description?: string;\n required?: boolean;\n }>;\n}\n\ninterface IHelpVerb {\n name: string;\n category: string;\n description: string;\n details: string;\n /** Trailing positional fragment ready to splice into a USAGE line, e.g. ` <orphanPath>`. Empty when none. */\n positionals: string;\n examples: Array<{ title: string; command: string }>;\n flags: Array<{\n name: string;\n aliases: string[];\n type: 'boolean' | 'string';\n description: string;\n required: boolean;\n }>;\n}\n\ninterface IHelpDocument {\n cliVersion: string;\n specVersion: string;\n globalFlags: Array<{ name: string; type: 'boolean'; description: string }>;\n verbs: IHelpVerb[];\n}\n\nexport class HelpCommand extends Command {\n static override paths = [['help']];\n static override usage = Command.Usage({\n category: 'Introspection',\n description: 'Self-describing introspection. --format human|md|json.',\n details: `\n Without a verb: overview of every registered command grouped by category.\n With a verb: the detail view for that single command.\n\n Formats:\n human (default) — pretty terminal output.\n md — canonical markdown. context/cli-reference.md is\n regenerated from this and CI fails on drift.\n json — structured surface dump per spec/cli-contract.md.\n `,\n });\n\n verbParts = Option.Rest({ required: 0 });\n format = Option.String('--format', 'human');\n\n async execute(): Promise<number> {\n const format = normalizeFormat(this.format);\n if (!format) {\n this.context.stderr.write(tx(HELP_TEXTS.invalidFormat, { format: this.format }));\n return ExitCode.Error;\n }\n\n // Pull definitions from Clipanion and normalise them into our shape.\n const rawDefs = this.cli.definitions() as ICliDefinition[];\n const verbs = rawDefs\n .filter((d) => !isBuiltin(d))\n .map(normalizeDefinition)\n .sort(byPath);\n\n const verb = this.verbParts.join(' ').trim();\n if (verb) {\n const target = verbs.find((v) => v.name === verb);\n if (!target) {\n this.context.stderr.write(tx(HELP_TEXTS.unknownVerb, { verb }));\n return ExitCode.NotFound;\n }\n this.context.stdout.write(renderSingle(target, format));\n return ExitCode.Ok;\n }\n\n if (format === 'human') {\n this.context.stdout.write(renderCompactOverview(verbs));\n return ExitCode.Ok;\n }\n\n const doc: IHelpDocument = {\n cliVersion: VERSION,\n specVersion: resolveSpecVersion(),\n globalFlags: [\n { name: '--help', type: 'boolean', description: HELP_TEXTS.globalFlagHelpDescription },\n ],\n verbs,\n };\n\n if (format === 'json') {\n this.context.stdout.write(JSON.stringify(doc, null, 2) + '\\n');\n return ExitCode.Ok;\n }\n\n this.context.stdout.write(renderMarkdown(doc));\n return ExitCode.Ok;\n }\n}\n\n// --- normalisation --------------------------------------------------------\n\nfunction normalizeFormat(raw: string): THelpFormat | null {\n if (raw === 'human' || raw === 'md' || raw === 'json') return raw;\n return null;\n}\n\nfunction isBuiltin(def: ICliDefinition): boolean {\n // Clipanion's built-ins register as `help` / `-h` / `--help` / `--version`.\n // Our own HelpCommand registers as `help` too; we want to keep that entry\n // but drop the raw flag paths that come from Builtins.\n const path = (def.path ?? '').trim();\n return path === 'sm -h' || path === 'sm --help' || path === 'sm --version';\n}\n\nfunction normalizeDefinition(def: ICliDefinition): IHelpVerb {\n const rawPath = (def.path ?? '').trim();\n const detailedUsage = (def.usage ?? rawPath).trim();\n const path = rawPath.replace(/^sm\\s+/, '').replace(/\\s*\\.\\.\\.$/, '').trim();\n const verbName = stripPositionals(path);\n const options = def.options ?? [];\n const flags = options.map((opt) => ({\n name: opt.preferredName,\n aliases: opt.nameSet.filter((n) => n !== opt.preferredName),\n type: inferOptionType(opt.definition),\n description: (opt.description ?? '').trim(),\n required: opt.required === true,\n }));\n const examples = (def.examples ?? []).map(([title, command]) => ({\n title: title.trim(),\n command: command.trim(),\n }));\n return {\n name: verbName,\n category: (def.category ?? 'Other').trim(),\n description: (def.description ?? '').trim(),\n details: (def.details ?? '').trim(),\n positionals: extractPositionals(detailedUsage, verbName),\n examples,\n flags,\n };\n}\n\n/**\n * Extract trailing positional tokens from a Clipanion path string, ready\n * to splice into a USAGE line. Positionals are bracket-balanced tokens\n * (`<x>` or `[x]`) whose first inner char is NOT `-` (those are flags).\n * Returns ` <pos1> [pos2]` or empty string. Variadic `...` is preserved.\n *\n * Exception #2 (char-by-char parser / state machine): each char triggers\n * a depth/buffer state transition; splitting per state mode hides the\n * dispatcher loop. See AGENTS.md \"When eslint-disable-next-line is acceptable\".\n */\n// eslint-disable-next-line complexity\nfunction extractPositionals(rawPath: string, verbName: string): string {\n const afterPrefix = rawPath.replace(/^sm\\s+/, '').trim();\n const stripped = afterPrefix.startsWith(verbName)\n ? afterPrefix.slice(verbName.length).trim()\n : afterPrefix;\n if (!stripped) return '';\n\n const tokens: string[] = [];\n let depth = 0;\n let buf = '';\n for (const ch of stripped) {\n if (ch === '[' || ch === '<') {\n if (depth === 0 && buf.trim()) {\n tokens.push(buf.trim());\n buf = '';\n }\n depth++;\n buf += ch;\n } else if (ch === ']' || ch === '>') {\n depth--;\n buf += ch;\n if (depth === 0) {\n tokens.push(buf.trim());\n buf = '';\n }\n } else {\n buf += ch;\n }\n }\n if (buf.trim()) tokens.push(buf.trim());\n\n const positionals = tokens.filter((t) => /^[<[][^-]/.test(t) || t === '...');\n return positionals.length > 0 ? ' ' + positionals.join(' ') : '';\n}\n\n/** Drop trailing positional fragments to recover the bare verb path (`db migrate`). */\nfunction stripPositionals(path: string): string {\n // Verb tokens are space-separated and don't start with `[`, `<`, or `--`.\n const out: string[] = [];\n for (const tok of path.split(/\\s+/)) {\n if (tok.startsWith('[') || tok.startsWith('<') || tok.startsWith('-') || tok === '...') break;\n out.push(tok);\n }\n return out.join(' ');\n}\n\nfunction inferOptionType(definition: string): 'boolean' | 'string' {\n // Clipanion emits \"--json\" for booleans and \"--json #0\" (with a\n // placeholder) for strings / arrays.\n return /#\\d/.test(definition) ? 'string' : 'boolean';\n}\n\nfunction byPath(a: IHelpVerb, b: IHelpVerb): number {\n return a.name.localeCompare(b.name);\n}\n\nfunction resolveSpecVersion(): string {\n try {\n const req = createRequire(import.meta.url);\n const indexPath = req.resolve('@skill-map/spec/index.json');\n const pkgPath = resolve(indexPath, '..', 'package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as { version: string };\n return pkg.version;\n } catch {\n return 'unknown';\n }\n}\n\n// --- renderers ------------------------------------------------------------\n\nfunction renderMarkdown(doc: IHelpDocument): string {\n const out: string[] = [];\n out.push(HELP_TEXTS.mdReferenceTitle, '');\n out.push(HELP_TEXTS.mdGeneratedNotice, '');\n out.push(tx(HELP_TEXTS.mdCliVersionLine, { version: doc.cliVersion }));\n out.push(tx(HELP_TEXTS.mdSpecVersionLine, { version: doc.specVersion }));\n out.push('');\n\n if (doc.globalFlags.length > 0) {\n out.push(HELP_TEXTS.mdHeaderGlobalFlags, '');\n for (const flag of doc.globalFlags) {\n out.push(tx(HELP_TEXTS.mdGlobalFlagBullet, { name: flag.name, description: flag.description }));\n }\n out.push('');\n }\n\n const byCategory = new Map<string, IHelpVerb[]>();\n for (const verb of doc.verbs) {\n if (!byCategory.has(verb.category)) byCategory.set(verb.category, []);\n byCategory.get(verb.category)!.push(verb);\n }\n const sortedCategories = [...byCategory.keys()].sort();\n\n for (const category of sortedCategories) {\n out.push(tx(HELP_TEXTS.mdCategoryHeading, { category }), '');\n const verbs = byCategory.get(category)!;\n for (const verb of verbs) out.push(...renderVerbBlock(verb));\n }\n\n return out.join('\\n').replace(/\\n{3,}/g, '\\n\\n') + '\\n';\n}\n\n/**\n * Render the markdown block for a single verb: heading, description,\n * details, flags table, examples. Used per verb under each category in\n * `renderMarkdown`. Returns the lines as an array; caller concatenates.\n */\nfunction renderVerbBlock(verb: IHelpVerb): string[] {\n const out: string[] = [];\n out.push(tx(HELP_TEXTS.mdVerbHeading, { name: verb.name }), '');\n if (verb.description) out.push(verb.description, '');\n if (verb.details) out.push(verb.details, '');\n if (verb.flags.length > 0) out.push(...renderVerbFlags(verb.flags));\n if (verb.examples.length > 0) out.push(...renderVerbExamples(verb.examples));\n return out;\n}\n\n/** Markdown flags table for one verb. */\nfunction renderVerbFlags(flags: IHelpVerb['flags']): string[] {\n const lines: string[] = [HELP_TEXTS.mdLabelFlags, ''];\n for (const flag of flags) {\n const names = [flag.name, ...flag.aliases].map((n) => `\\`${n}\\``).join(', ');\n const required = flag.required ? HELP_TEXTS.mdFlagBulletRequiredFragment : '';\n const description = flag.description\n ? tx(HELP_TEXTS.mdFlagBulletDescriptionFragment, { description: flag.description })\n : '';\n lines.push(tx(HELP_TEXTS.mdFlagBullet, { names, type: flag.type, required, description }));\n }\n lines.push('');\n return lines;\n}\n\n/** Markdown examples block for one verb. */\nfunction renderVerbExamples(examples: IHelpVerb['examples']): string[] {\n const lines: string[] = [HELP_TEXTS.mdLabelExamples, ''];\n for (const ex of examples) {\n lines.push(tx(HELP_TEXTS.mdExampleBullet, { title: ex.title }));\n lines.push(' ```');\n lines.push(` ${ex.command}`);\n lines.push(' ```');\n }\n lines.push('');\n return lines;\n}\n\nfunction renderSingle(verb: IHelpVerb, format: THelpFormat): string {\n if (format === 'json') return JSON.stringify(verb, null, 2) + '\\n';\n if (format === 'md') {\n const doc: IHelpDocument = {\n cliVersion: VERSION,\n specVersion: resolveSpecVersion(),\n globalFlags: [],\n verbs: [verb],\n };\n return renderMarkdown(doc);\n }\n return renderSingleHuman(verb);\n}\n\nfunction renderSingleHuman(verb: IHelpVerb): string {\n const out: string[] = [];\n out.push(buildHumanHeader(verb));\n out.push('');\n out.push(HELP_TEXTS.humanUsageHeading);\n out.push(tx(HELP_TEXTS.humanUsageRow, { name: verb.name, positionals: verb.positionals }));\n if (verb.details) out.push(...renderHumanDescription(verb.details));\n if (verb.flags.length > 0) out.push(...renderHumanFlags(verb.flags));\n out.push('');\n out.push(tx(HELP_TEXTS.humanFooter, { name: verb.name }));\n return out.join('\\n') + '\\n';\n}\n\nfunction buildHumanHeader(verb: IHelpVerb): string {\n const { isStub, clean } = classifyDescription(verb.description);\n const description = (isStub ? HELP_TEXTS.compactStubMarker : '') + firstSentence(clean);\n return tx(HELP_TEXTS.humanVerbHeader, { name: verb.name, description });\n}\n\nfunction renderHumanDescription(details: string): string[] {\n const out: string[] = ['', HELP_TEXTS.humanDescriptionHeading];\n for (const line of normaliseDetailLines(details)) {\n out.push(line === '' ? '' : ' ' + line);\n }\n return out;\n}\n\nfunction renderHumanFlags(flags: IHelpVerb['flags']): string[] {\n const out: string[] = ['', HELP_TEXTS.humanFlagsHeading];\n const flagNames = flags.map((f) => [f.name, ...f.aliases].join(', '));\n const flagWidth = Math.max(...flagNames.map((n) => n.length));\n for (let i = 0; i < flags.length; i++) {\n const flag = flags[i]!;\n const names = flagNames[i]!;\n const padding = padRight('', flagWidth - names.length);\n const required = flag.required ? HELP_TEXTS.humanFlagRowRequiredFragment : '';\n const row = tx(HELP_TEXTS.humanFlagRow, {\n names,\n padding,\n description: firstSentence(flag.description),\n required,\n });\n out.push(truncate(row, COMPACT_ROW_MAX));\n }\n return out;\n}\n\n/**\n * Reflow a Clipanion `details` block into terminal-friendly lines: trim,\n * collapse soft-wrapped paragraphs, keep blank-line paragraph breaks.\n * Clipanion's source emits details with leading whitespace per line and\n * hard-wrap from the docstring; we restore intent.\n */\nfunction normaliseDetailLines(details: string): string[] {\n return details\n .split('\\n')\n .map((l) => l.replace(/^\\s+/, '').trimEnd())\n .filter((l, i, arr) => !(l === '' && (i === 0 || arr[i - 1] === '')));\n}\n\n// --- compact overview (sm / sm --help / sm help, no verb, human) ----------\n\ninterface ICompactExample {\n command: string;\n description: string;\n}\n\n/**\n * The intro examples shown at the top of the compact overview. Commands\n * are literal CLI invocations (not translated); descriptions go through\n * the `tx` catalog so they can be localised.\n */\nfunction compactExamples(): ICompactExample[] {\n return [\n { command: 'sm init', description: HELP_TEXTS.compactExampleInit },\n { command: 'sm scan && sm check', description: HELP_TEXTS.compactExampleScanCheck },\n { command: 'sm orphans --json | jq', description: HELP_TEXTS.compactExampleOrphans },\n ];\n}\n\n/** Take the first sentence of a description for the compact one-liner column. */\nfunction firstSentence(text: string): string {\n const idx = text.indexOf('. ');\n if (idx === -1) return text.replace(/\\.$/, '').trim();\n return text.slice(0, idx).trim();\n}\n\n/** Hard cap on the rendered width of a single compact row, including indent. */\nconst COMPACT_ROW_MAX = 120;\n\n/**\n * Strip the `(planned)` marker from a verb description and report whether\n * it was present. Stubs are surfaced via a leading `[stub] ` tag in the\n * compact column rather than the trailing parenthetical, so the column\n * stays alignable and the marker stays visible after `firstSentence()`\n * truncation.\n */\nfunction classifyDescription(raw: string): { isStub: boolean; clean: string } {\n const isStub = /\\(planned\\)/.test(raw);\n const clean = raw.replace(/\\s*\\(planned\\)\\s*/g, ' ').replace(/\\s+/g, ' ').trim();\n return { isStub, clean };\n}\n\n/** Truncate `text` with a single-char ellipsis to fit `width`, no-op if it already fits. */\nfunction truncate(text: string, width: number): string {\n if (text.length <= width) return text;\n if (width < 1) return '';\n return text.slice(0, width - 1) + '…';\n}\n\n/** Right-pad `value` with spaces to `width` columns. */\nfunction padRight(value: string, width: number): string {\n return value.length >= width ? value : value + ' '.repeat(width - value.length);\n}\n\n/**\n * Render the compact top-level overview. Replaces Clipanion's default\n * `cli.usage()` ANSI banner. Format: header tagline, USAGE block,\n * EXAMPLES block, then per-category two-column command listing, then a\n * footer pointing to per-verb help. Per-category column width is\n * computed independently so a single long verb in one category does not\n * widen every other section.\n */\nexport function renderCompactOverview(verbs: IHelpVerb[]): string {\n const lines: string[] = [];\n lines.push(tx(HELP_TEXTS.compactHeader, { binary: BINARY_LABEL, version: VERSION }));\n lines.push('');\n lines.push(HELP_TEXTS.compactUsageHeading);\n lines.push(HELP_TEXTS.compactUsageLine);\n lines.push('');\n\n const examples = compactExamples();\n const exampleWidth = Math.max(...examples.map((e) => e.command.length));\n lines.push(HELP_TEXTS.compactExamplesHeading);\n for (const ex of examples) {\n const padding = ' '.repeat(exampleWidth - ex.command.length);\n lines.push(tx(HELP_TEXTS.compactExampleRow, {\n command: ex.command,\n padding,\n description: ex.description,\n }));\n }\n lines.push('');\n\n const byCategory = new Map<string, IHelpVerb[]>();\n for (const verb of verbs) {\n const list = byCategory.get(verb.category) ?? [];\n list.push(verb);\n byCategory.set(verb.category, list);\n }\n\n const sortedCategories = [...byCategory.keys()].sort();\n for (const category of sortedCategories) {\n const verbsInCategory = byCategory.get(category)!;\n verbsInCategory.sort((a, b) => a.name.localeCompare(b.name));\n const verbWidth = Math.max(...verbsInCategory.map((v) => v.name.length));\n lines.push(tx(HELP_TEXTS.compactCategoryHeading, { category: category.toUpperCase() }));\n for (const verb of verbsInCategory) {\n const { isStub, clean } = classifyDescription(verb.description);\n const description = (isStub ? HELP_TEXTS.compactStubMarker : '') + firstSentence(clean);\n const row = tx(HELP_TEXTS.compactVerbRow, {\n name: verb.name,\n padding: padRight('', verbWidth - verb.name.length),\n description,\n });\n lines.push(truncate(row, COMPACT_ROW_MAX));\n }\n lines.push('');\n }\n\n lines.push(HELP_TEXTS.compactFooter);\n return lines.join('\\n') + '\\n';\n}\n\n// --- root --help / -h -----------------------------------------------------\n\n/**\n * Replaces Clipanion's `Builtins.HelpCommand` so `sm`, `sm --help`,\n * `sm -h` render the compact overview instead of the default ANSI\n * banner. Per-verb `sm <verb> --help` is still served by Clipanion's\n * built-in tokenizer and untouched.\n */\nexport class RootHelpCommand extends Command {\n static override paths = [['-h'], ['--help'], Command.Default];\n\n async execute(): Promise<number> {\n const rawDefs = this.cli.definitions() as ICliDefinition[];\n const verbs = rawDefs\n .filter((d) => !isBuiltin(d))\n .map(normalizeDefinition)\n .sort(byPath);\n this.context.stdout.write(renderCompactOverview(verbs));\n return ExitCode.Ok;\n }\n}\n\n// --- argv routing ---------------------------------------------------------\n\nconst HELP_FLAG_PATTERN = /^(-h|--help)(=.*)?$/;\n\n/**\n * Reroute `sm <verb...> --help|-h` invocations to `sm help <verb...>` so\n * per-verb help goes through `renderSingle()` instead of Clipanion's\n * default `cli.usage(command)` ANSI banner. Matches the longest\n * registered verb-path prefix in the leading positionals; if no prefix\n * matches, args pass through unchanged and Clipanion handles them.\n *\n * Pure: no I/O, no side effects. Called from `entry.ts` before\n * `cli.run()`.\n */\nexport function routeHelpArgs(args: string[], cli: Cli): string[] {\n if (!shouldRouteHelp(args)) return args;\n const leading = leadingPositionals(args);\n if (leading.length === 0) return args;\n const verbPath = longestVerbPrefix(leading, registeredVerbPaths(cli));\n if (verbPath.length === 0) return args;\n return ['help', ...verbPath];\n}\n\n/** Pre-check: only reroute when the args contain a help flag and are not already a help invocation. */\nfunction shouldRouteHelp(args: string[]): boolean {\n if (args.length === 0) return false;\n if (args[0] === 'help') return false;\n if (!args.some((a) => HELP_FLAG_PATTERN.test(a))) return false;\n // Top-level `sm --help` / `sm -h` — RootHelpCommand handles directly.\n if (args.every((a) => HELP_FLAG_PATTERN.test(a))) return false;\n return true;\n}\n\n/** Collect leading positional tokens up to the first flag. */\nfunction leadingPositionals(args: string[]): string[] {\n const out: string[] = [];\n for (const arg of args) {\n if (arg.startsWith('-')) break;\n out.push(arg);\n }\n return out;\n}\n\n/** Return the longest registered verb path that is a prefix of `positionals`, or `[]` if none match. */\nfunction longestVerbPrefix(positionals: string[], verbPaths: string[][]): string[] {\n let best: string[] = [];\n for (const path of verbPaths) {\n if (path.length > positionals.length) continue;\n const matches = path.every((tok, i) => positionals[i] === tok);\n if (matches && path.length > best.length) best = path;\n }\n return best;\n}\n\n/**\n * Snapshot of every registered verb path as a token array, e.g.\n * `[['scan'], ['scan', 'compare-with'], ['db', 'migrate'], ...]`.\n * Excludes Clipanion built-ins.\n */\nfunction registeredVerbPaths(cli: Cli): string[][] {\n const rawDefs = cli.definitions() as ICliDefinition[];\n const paths: string[][] = [];\n for (const def of rawDefs) {\n if (isBuiltin(def)) continue;\n const path = (def.path ?? '').replace(/^sm\\s+/, '').replace(/\\s*\\.\\.\\.$/, '').trim();\n const verb = stripPositionals(path);\n if (!verb) continue;\n paths.push(verb.split(/\\s+/).filter(Boolean));\n }\n return paths;\n}\n","{\n \"name\": \"@skill-map/cli\",\n \"version\": \"0.9.0\",\n \"description\": \"skill-map reference implementation — kernel + CLI + adapters.\",\n \"license\": \"MIT\",\n \"type\": \"module\",\n \"homepage\": \"https://skill-map.dev\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/crystian/skill-map.git\",\n \"directory\": \"src\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/crystian/skill-map/issues\"\n },\n \"keywords\": [\n \"skill-map\",\n \"markdown\",\n \"ai-agents\",\n \"claude-code\",\n \"graph\"\n ],\n \"bin\": {\n \"sm\": \"bin/sm.js\",\n \"skill-map\": \"bin/sm.js\"\n },\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./kernel\": {\n \"types\": \"./dist/kernel/index.d.ts\",\n \"import\": \"./dist/kernel/index.js\"\n },\n \"./conformance\": {\n \"types\": \"./dist/conformance/index.d.ts\",\n \"import\": \"./dist/conformance/index.js\"\n }\n },\n \"files\": [\n \"bin/\",\n \"dist/\",\n \"migrations/\",\n \"README.md\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"typecheck\": \"tsc --noEmit\",\n \"lint\": \"eslint .\",\n \"lint:fix\": \"eslint . --fix\",\n \"test\": \"tsc --noEmit && node --import tsx --test --test-reporter=spec 'test/**/*.test.ts' 'built-in-plugins/**/*.test.ts' 'kernel/**/*.test.ts'\",\n \"test:ci\": \"tsc --noEmit && node --import tsx --test 'test/**/*.test.ts' 'built-in-plugins/**/*.test.ts' 'kernel/**/*.test.ts'\",\n \"test:coverage\": \"tsc --noEmit && SKILL_MAP_SKIP_BENCHMARK=1 node --experimental-default-config-file --import tsx --test --experimental-test-coverage 'test/**/*.test.ts' 'built-in-plugins/**/*.test.ts' 'kernel/**/*.test.ts'\",\n \"test:coverage:html\": \"tsc --noEmit && SKILL_MAP_SKIP_BENCHMARK=1 c8 node --import tsx --test 'test/**/*.test.ts' 'built-in-plugins/**/*.test.ts' 'kernel/**/*.test.ts'\",\n \"clean\": \"rm -rf dist coverage\"\n },\n \"dependencies\": {\n \"@skill-map/spec\": \"*\",\n \"ajv\": \"8.18.0\",\n \"ajv-formats\": \"3.0.1\",\n \"chokidar\": \"5.0.0\",\n \"clipanion\": \"4.0.0-rc.4\",\n \"ignore\": \"7.0.5\",\n \"js-tiktoken\": \"1.0.21\",\n \"js-yaml\": \"4.1.1\",\n \"kysely\": \"0.28.16\",\n \"semver\": \"7.7.4\",\n \"typanion\": \"3.14.0\"\n },\n \"devDependencies\": {\n \"@eslint/js\": \"10.0.1\",\n \"@stylistic/eslint-plugin\": \"5.10.0\",\n \"@types/js-yaml\": \"4.0.9\",\n \"@types/node\": \"24.12.2\",\n \"@types/semver\": \"7.7.1\",\n \"c8\": \"11.0.0\",\n \"eslint\": \"10.2.1\",\n \"eslint-plugin-import-x\": \"4.16.2\",\n \"tsup\": \"8.5.1\",\n \"tsx\": \"4.21.0\",\n \"typescript\": \"5.9.3\",\n \"typescript-eslint\": \"8.59.1\"\n },\n \"engines\": {\n \"node\": \">=24.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","import pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION: string = pkg.version;\nexport const BINARY_NAME = 'sm';\nexport const BINARY_LABEL = 'skill-map';\n","/**\n * Strings emitted by `cli/commands/help.ts`.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n *\n * Markdown structural pieces (code-fence backticks `\\`\\`\\``, leading\n * pipes, blockquote markers) stay inline in the renderer — they are\n * markdown syntax, not user-facing prose. Everything that a translator\n * would touch (headings, labels, the \"Generated from...\" notice, the\n * version-line copy) lives here.\n */\n\nexport const HELP_TEXTS = {\n // --- format / verb validation --------------------------------------------\n invalidFormat: '--format expects one of: human | md | json. Got: {{format}}\\n',\n unknownVerb: 'Unknown verb: {{verb}}\\n',\n\n // --- markdown header -----------------------------------------------------\n mdReferenceTitle: '# `sm` CLI reference',\n mdGeneratedNotice:\n 'Generated from `sm help --format md`. Do not hand-edit; CI regenerates this file from the live command surface.',\n mdCliVersionLine: '- CLI version: `{{version}}`',\n mdSpecVersionLine: '- Spec version: `{{version}}`',\n\n // --- global flags section ------------------------------------------------\n mdHeaderGlobalFlags: '## Global flags',\n mdGlobalFlagBullet: '- `{{name}}` — {{description}}',\n /** Description copy for the `--help` global flag in the JSON / md output. */\n globalFlagHelpDescription: 'Print usage and exit.',\n\n // --- per-category / per-verb (md) ----------------------------------------\n mdCategoryHeading: '## {{category}}',\n mdVerbHeading: '### `sm {{name}}`',\n mdLabelFlags: '**Flags:**',\n mdLabelExamples: '**Examples:**',\n mdFlagBullet: '- {{names}} `{{type}}`{{required}}{{description}}',\n /** Trailing fragment for `mdFlagBullet`'s `{{required}}` slot. */\n mdFlagBulletRequiredFragment: ' (required)',\n /** Trailing fragment for `mdFlagBullet`'s `{{description}}` slot (with leading em-dash). */\n mdFlagBulletDescriptionFragment: ' — {{description}}',\n mdExampleBullet: '- {{title}}',\n\n // --- human single-verb renderer ------------------------------------------\n /** Header line for `sm help <verb>` and `sm <verb> --help`. */\n humanVerbHeader: 'sm {{name}} — {{description}}',\n humanDescriptionHeading: 'DESCRIPTION',\n humanUsageHeading: 'USAGE',\n /**\n * Single-line USAGE row. `{{positionals}}` is the trailing portion of\n * the Clipanion path (e.g. `<orphanPath>` or `[roots...]`); empty when\n * the command takes no positionals.\n */\n humanUsageRow: ' sm {{name}} [options]{{positionals}}',\n humanFlagsHeading: 'FLAGS',\n /** Aligned flag row inside the FLAGS block; `{{padding}}` keeps the description column flush. */\n humanFlagRow: ' {{names}}{{padding}} {{description}}{{required}}',\n /** Trailing fragment for `humanFlagRow`'s `{{required}}` slot. */\n humanFlagRowRequiredFragment: ' (required)',\n humanFooter: 'Run `sm help {{name}} --format md` for the full reference.',\n\n // --- human compact overview (sm / sm --help / sm help, no verb) ---------\n /**\n * Compact-overview header. Replaces the Clipanion default ANSI banner.\n * Tagline mirrors README.md \"In a sentence\" — keep them in sync.\n */\n compactHeader: '{{binary}} {{version}} — graph explorer for Markdown-based AI-agent ecosystems',\n compactUsageHeading: 'USAGE',\n compactUsageLine: ' sm <command> [options]',\n compactExamplesHeading: 'EXAMPLES',\n compactExampleInit: 'Bootstrap a project scope',\n compactExampleScanCheck: 'Scan and review issues',\n compactExampleOrphans: 'Pipe orphans to jq',\n /**\n * Marker prepended to the description column for not-yet-implemented\n * verbs (those whose registered description carries `(planned)`).\n * Trailing space is intentional — the marker is concatenated before\n * the rest of the description.\n */\n compactStubMarker: '[stub] ',\n /** Per-category section heading (uppercased from the registered category). */\n compactCategoryHeading: '{{category}}',\n /**\n * Single command row. The renderer pads `{{name}}` to the category's\n * widest verb so descriptions align in a column.\n */\n compactVerbRow: ' {{name}}{{padding}} {{description}}',\n /** Same row shape for example rows; padding aligned across the EXAMPLES block. */\n compactExampleRow: ' {{command}}{{padding}} {{description}}',\n compactFooter: 'Run `sm <command> --help` for flags and arguments.',\n} as const;\n","/**\n * `sm init [-g] [--no-scan] [--force]` — bootstrap a skill-map scope.\n *\n * - Creates `<root>/.skill-map/` (project = cwd, global = ~).\n * - Writes `settings.json` (`{ \"schemaVersion\": 1 }`) and\n * `settings.local.json` (`{}`).\n * - Copies the bundled `.skill-mapignore` template into the scope root.\n * - Provisions `<root>/.skill-map/skill-map.db` (kernel migrations\n * run automatically via `SqliteStorageAdapter.init()`).\n * - Project scope only: appends `.skill-map/settings.local.json` and\n * `.skill-map/skill-map.db` to the project's `.gitignore`\n * (creating the file when missing). The default `history.share`\n * is `false`, so the DB stays untracked unless the team opts in.\n * - Runs a first scan unless `--no-scan` is passed.\n *\n * Re-running on an already-initialised scope errors with exit 2 unless\n * `--force` is passed.\n */\n\nimport { mkdir, readFile, stat, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { Command, Option } from 'clipanion';\n\nimport { createKernel, runScanWithRenames } from '../../kernel/index.js';\nimport { builtIns, listBuiltIns } from '../../built-in-plugins/built-ins.js';\nimport { loadConfig } from '../../kernel/config/loader.js';\nimport {\n buildIgnoreFilter,\n loadBundledIgnoreText,\n readIgnoreFileText,\n} from '../../kernel/scan/ignore.js';\nimport { emitDoneStderr, startElapsed } from '../util/elapsed.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { INIT_TEXTS } from '../i18n/init.texts.js';\nimport { createCliProgressEmitter } from '../util/cli-progress-emitter.js';\nimport { SKILL_MAP_DIR } from '../util/db-path.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport { formatErrorMessage } from '../util/error-reporter.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\nimport { withSqlite } from '../util/with-sqlite.js';\n\nconst GITIGNORE_ENTRIES: readonly string[] = [\n '.skill-map/settings.local.json',\n '.skill-map/skill-map.db',\n];\n\n/**\n * Async existence probe — `fs.stat` returning a boolean. ENOENT is the\n * only swallowed error code; anything else (permission denied, IO\n * failure) propagates so the caller sees the real reason.\n */\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw err;\n }\n}\n\nexport class InitCommand extends Command {\n static override paths = [['init']];\n static override usage = Command.Usage({\n category: 'Setup',\n description: 'Bootstrap the current scope: scaffold .skill-map/, provision DB, run first scan.',\n details: `\n Project scope (default): creates ./.skill-map/ with settings.json,\n settings.local.json, and skill-map.db. Drops a starter\n .skill-mapignore at the scope root and appends the DB + local\n settings to .gitignore.\n\n Global scope (-g): same scaffolding under ~/.skill-map/. No\n .gitignore is touched; \"$HOME\" isn't a repo.\n\n Re-running over an existing scope errors with exit 2 unless\n --force is passed. --no-scan skips the first scan; useful in CI\n where the operator wants to provision before populating roots.\n `,\n examples: [\n ['Initialise the current project', '$0 init'],\n ['Provision the global scope', '$0 init -g'],\n ['Bootstrap without running the first scan', '$0 init --no-scan'],\n ['Force-overwrite an existing scope', '$0 init --force'],\n ['Preview what would be created', '$0 init --dry-run'],\n ],\n });\n\n global = Option.Boolean('-g,--global', false, {\n description: 'Initialise ~/.skill-map/ instead of ./.skill-map/.',\n });\n noScan = Option.Boolean('--no-scan', false, {\n description: 'Skip the first scan after scaffolding.',\n });\n force = Option.Boolean('--force', false, {\n description: 'Overwrite an existing settings.json / settings.local.json / .skill-mapignore.',\n });\n strict = Option.Boolean('--strict', false, {\n description: 'Strict mode: fail on any layered-loader warning AND promote frontmatter warnings to errors during the first scan. Same flag as sm scan / sm config.',\n });\n dryRun = Option.Boolean('-n,--dry-run', false, {\n description: 'Preview the scope provisioning without touching the filesystem or the DB. Honours --force for the would-overwrite preview. Skips the first scan unconditionally — dry-run never persists.',\n });\n\n // CLI orchestrator: paths setup + dry-run branch (delegated to\n // `writeDryRunPlan`) + real provision (mkdir + 4 file writes +\n // gitignore management + DB provision + first scan delegation).\n // The first-scan branch already lives in `runFirstScan`.\n // eslint-disable-next-line complexity\n async execute(): Promise<number> {\n const elapsed = startElapsed();\n const ctx = defaultRuntimeContext();\n const scopeRoot = this.global ? ctx.homedir : ctx.cwd;\n const skillMapDir = join(scopeRoot, SKILL_MAP_DIR);\n const settingsPath = join(skillMapDir, 'settings.json');\n const localPath = join(skillMapDir, 'settings.local.json');\n const ignorePath = join(scopeRoot, '.skill-mapignore');\n const dbPath = join(skillMapDir, 'skill-map.db');\n\n if ((await pathExists(settingsPath)) && !this.force) {\n this.context.stderr.write(tx(INIT_TEXTS.alreadyInitialised, { settingsPath }));\n emitDoneStderr(this.context.stderr, elapsed);\n return ExitCode.Error;\n }\n\n if (this.dryRun) {\n await writeDryRunPlan(this.context.stdout, {\n skillMapDir, settingsPath, localPath, ignorePath, dbPath,\n scopeRoot, force: this.force, global: this.global, noScan: this.noScan,\n });\n emitDoneStderr(this.context.stderr, elapsed);\n return ExitCode.Ok;\n }\n\n await mkdir(skillMapDir, { recursive: true });\n await writeFile(settingsPath, JSON.stringify({ schemaVersion: 1 }, null, 2) + '\\n');\n if (!(await pathExists(localPath)) || this.force) {\n await writeFile(localPath, '{}\\n');\n }\n if (!(await pathExists(ignorePath)) || this.force) {\n await writeFile(ignorePath, loadBundledIgnoreText());\n }\n\n if (!this.global) {\n const updated = await ensureGitignoreEntries(scopeRoot, GITIGNORE_ENTRIES);\n if (updated) {\n const gitignorePath = join(scopeRoot, '.gitignore');\n this.context.stdout.write(\n GITIGNORE_ENTRIES.length === 1\n ? tx(INIT_TEXTS.gitignoreUpdatedSingular, { path: gitignorePath })\n : tx(INIT_TEXTS.gitignoreUpdatedPlural, {\n path: gitignorePath,\n count: GITIGNORE_ENTRIES.length,\n }),\n );\n }\n }\n\n // Provision the DB. `withSqlite` opens the adapter, which auto-\n // applies migrations on init(); by the time this returns the DB is\n // at the latest kernel schema.\n await withSqlite({ databasePath: dbPath, autoBackup: false }, async () => {\n // No-op: opening (and closing) the adapter is the work here.\n });\n\n this.context.stdout.write(tx(INIT_TEXTS.initialised, { skillMapDir }));\n\n if (this.noScan) {\n emitDoneStderr(this.context.stderr, elapsed);\n return ExitCode.Ok;\n }\n\n // First scan. Inline (not subprocess) so the parent process owns\n // the elapsed line and the stdout/stderr streams cleanly.\n const scanCode = await runFirstScan(scopeRoot, ctx.homedir, dbPath, this.strict, this.context.stdout, this.context.stderr);\n emitDoneStderr(this.context.stderr, elapsed);\n return scanCode;\n }\n}\n\n/**\n * Render the `--dry-run` plan to stdout: which directories/files the\n * verb would create or overwrite, and whether the first scan would\n * run. Used only when `--dry-run` is set; the real provision path\n * skips this entirely.\n */\nasync function writeDryRunPlan(\n stdout: NodeJS.WritableStream,\n opts: {\n skillMapDir: string;\n settingsPath: string;\n localPath: string;\n ignorePath: string;\n dbPath: string;\n scopeRoot: string;\n force: boolean;\n global: boolean;\n noScan: boolean;\n },\n): Promise<void> {\n stdout.write(INIT_TEXTS.dryRunHeader);\n if (!(await pathExists(opts.skillMapDir))) {\n stdout.write(tx(INIT_TEXTS.dryRunWouldCreateDir, { path: opts.skillMapDir }));\n }\n // settingsPath: always written (caller gated --force above).\n stdout.write(await dryRunFileMessage(opts.settingsPath));\n // Local + ignore: written only when missing OR --force.\n if (!(await pathExists(opts.localPath)) || opts.force) {\n stdout.write(await dryRunFileMessage(opts.localPath));\n }\n if (!(await pathExists(opts.ignorePath)) || opts.force) {\n stdout.write(await dryRunFileMessage(opts.ignorePath));\n }\n if (!opts.global) await writeDryRunGitignorePlan(stdout, opts.scopeRoot);\n stdout.write(tx(INIT_TEXTS.dryRunWouldProvisionDb, { path: opts.dbPath }));\n stdout.write(\n opts.noScan ? INIT_TEXTS.dryRunWouldSkipFirstScan : INIT_TEXTS.dryRunWouldRunFirstScan,\n );\n}\n\n/** \"would overwrite X\" if the file exists, else \"would write X\". */\nasync function dryRunFileMessage(path: string): Promise<string> {\n return (await pathExists(path))\n ? tx(INIT_TEXTS.dryRunWouldOverwriteFile, { path })\n : tx(INIT_TEXTS.dryRunWouldWriteFile, { path });\n}\n\n/**\n * Subhelper of `writeDryRunPlan` — render the `.gitignore` preview\n * (unchanged / one-entry / multi-entry phrasing). Project scope only.\n */\nasync function writeDryRunGitignorePlan(\n stdout: NodeJS.WritableStream,\n scopeRoot: string,\n): Promise<void> {\n const wouldAdd = await previewGitignoreEntries(scopeRoot, GITIGNORE_ENTRIES);\n const gitignorePath = join(scopeRoot, '.gitignore');\n if (wouldAdd.length === 0) {\n stdout.write(tx(INIT_TEXTS.dryRunWouldLeaveGitignoreUnchanged, { path: gitignorePath }));\n } else if (wouldAdd.length === 1) {\n stdout.write(\n tx(INIT_TEXTS.dryRunWouldUpdateGitignoreSingular, {\n path: gitignorePath,\n entries: wouldAdd[0]!,\n }),\n );\n } else {\n stdout.write(\n tx(INIT_TEXTS.dryRunWouldUpdateGitignorePlural, {\n path: gitignorePath,\n count: wouldAdd.length,\n entries: wouldAdd.join(', '),\n }),\n );\n }\n}\n\nasync function runFirstScan(\n scopeRoot: string,\n homedir: string,\n dbPath: string,\n strict: boolean,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n stdout.write(INIT_TEXTS.runningFirstScan);\n\n const kernel = createKernel();\n for (const manifest of listBuiltIns()) kernel.registry.register(manifest);\n\n let cfg;\n try {\n cfg = loadConfig({ scope: 'project', cwd: scopeRoot, homedir, strict }).effective;\n } catch (err) {\n const message = formatErrorMessage(err);\n stderr.write(tx(INIT_TEXTS.configLoadFailure, { message }));\n return ExitCode.Error;\n }\n const ignoreFileText = readIgnoreFileText(scopeRoot);\n const ignoreFilterOpts: Parameters<typeof buildIgnoreFilter>[0] = {};\n if (cfg.ignore.length > 0) ignoreFilterOpts.configIgnore = cfg.ignore;\n if (ignoreFileText !== undefined) ignoreFilterOpts.ignoreFileText = ignoreFileText;\n const ignoreFilter = buildIgnoreFilter(ignoreFilterOpts);\n\n let result;\n let renameOps;\n let extractorRuns;\n let enrichments;\n try {\n const ran = await runScanWithRenames(kernel, {\n roots: [scopeRoot],\n scope: 'project',\n tokenize: true,\n extensions: builtIns(),\n ignoreFilter,\n strict,\n emitter: createCliProgressEmitter(stderr),\n });\n result = ran.result;\n renameOps = ran.renameOps;\n extractorRuns = ran.extractorRuns;\n enrichments = ran.enrichments;\n } catch (err) {\n const message = formatErrorMessage(err);\n stderr.write(tx(INIT_TEXTS.scanFailed, { message }));\n return ExitCode.Error;\n }\n\n await withSqlite({ databasePath: dbPath, autoBackup: false }, (adapter) =>\n adapter.scans.persist(result, { renameOps, extractorRuns, enrichments }),\n );\n\n stdout.write(\n tx(INIT_TEXTS.firstScanSummary, {\n nodes: result.nodes.length,\n links: result.links.length,\n issues: result.issues.length,\n }),\n );\n // Issues with severity=error gate the exit code, mirroring `sm scan`.\n const hasErrors = result.issues.some((i) => i.severity === 'error');\n return hasErrors ? ExitCode.Issues : ExitCode.Ok;\n}\n\n/**\n * Compute which `entries` would be appended to `<scopeRoot>/.gitignore`\n * by the live `ensureGitignoreEntries` call, WITHOUT writing. Used by\n * `--dry-run` to render an honest preview of what `sm init` would\n * change. Same parsing rules as the live function so the preview tracks\n * the real outcome (skip blank lines and comment lines, dedupe by exact\n * trimmed match).\n */\nasync function previewGitignoreEntries(\n scopeRoot: string,\n entries: readonly string[],\n): Promise<string[]> {\n const path = join(scopeRoot, '.gitignore');\n const body = (await pathExists(path)) ? await readFile(path, 'utf8') : '';\n const present = new Set(\n body\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith('#')),\n );\n return entries.filter((entry) => !present.has(entry));\n}\n\n/**\n * Append every `entry` to `<scopeRoot>/.gitignore` that is not already\n * present (compared as trimmed line). Creates the file if absent.\n * Returns true if the file was written.\n */\nasync function ensureGitignoreEntries(\n scopeRoot: string,\n entries: readonly string[],\n): Promise<boolean> {\n const path = join(scopeRoot, '.gitignore');\n let body = '';\n if (await pathExists(path)) {\n body = await readFile(path, 'utf8');\n }\n const present = new Set(\n body\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith('#')),\n );\n let changed = false;\n for (const entry of entries) {\n if (present.has(entry)) continue;\n if (body.length > 0 && !body.endsWith('\\n')) body += '\\n';\n body += `${entry}\\n`;\n present.add(entry);\n changed = true;\n }\n if (changed) await writeFile(path, body);\n return changed;\n}\n","/**\n * Scan orchestrator — runs the Provider → extractor → rule pipeline across\n * every registered extension and emits `ProgressEmitterPort` events in\n * canonical order. The callable extension set is injected via\n * `RunScanOptions.extensions` — the Registry holds manifest metadata, the\n * callable set holds the runtime instances the orchestrator actually\n * invokes. Separating the two lets `sm plugins` and `sm help` introspect\n * the graph without loading code.\n *\n * With zero registered extensions (or a callable set that carries none)\n * the pipeline still produces a valid zero-filled `ScanResult` — the\n * kernel-empty-boot invariant.\n *\n * Roots are validated up front: each entry of `RunScanOptions.roots`\n * must exist on disk as a directory. The first failure throws a clear\n * `Error` naming the offending path. This guards every caller (CLI,\n * server, skill-agent) against silently producing a zero-filled\n * `ScanResult` when a Provider walks a non-existent path — the bug\n * that wiped a populated DB via `sm scan -- --dry-run` (clipanion's\n * `--` made `--dry-run` a positional root that did not exist).\n *\n * Incremental scans: when `priorSnapshot` is supplied, the\n * orchestrator walks the filesystem, hashes each file, and reuses the\n * prior node + its prior-extracted internal links whenever both\n * `bodyHash` and `frontmatterHash` match. New / modified files run\n * through the full extractor pipeline (including the external-url-counter\n * which produces ephemeral pseudo-links). Rules ALWAYS run over the\n * fully merged graph — issue state can change even for an unchanged node\n * (e.g. a previously broken `references` link now resolves because a new\n * node was added). For unchanged nodes the prior `externalRefsCount` is\n * preserved as-is (the external pseudo-links were never persisted, so\n * they cannot be reconstructed; the count survived in the node row).\n *\n * Extractor output model (B.1, post-rename from Detector): extractors\n * return `void` and emit through three callbacks injected on the context:\n * - `ctx.emitLink(link)` → orchestrator validates against\n * `emitsLinkKinds` then partitions into internal / external buckets.\n * - `ctx.enrichNode(partial)` → orchestrator records ONE enrichment\n * entry per `(node, extractor)` so attribution survives into the DB.\n * Persisted into `node_enrichments` (A.8). The author-supplied\n * frontmatter on `node.frontmatter` stays immutable from any Extractor\n * — the enrichment layer is the only writable surface, and rules /\n * formatters consume it via `mergeNodeWithEnrichments`.\n * - `ctx.store` → plugin's own KV / dedicated tables (spec § A.12).\n * Wired by the driving adapter via `RunScanOptions.pluginStores`,\n * which the orchestrator looks up per-extractor by `pluginId` and\n * attaches to the context. The orchestrator never inspects what\n * plugins write through it; the wrapper handles AJV validation\n * when the manifest declared an output schema.\n */\n\nimport { createHash } from 'node:crypto';\nimport { existsSync, statSync } from 'node:fs';\n\n// js-tiktoken ships CJS subpaths without explicit `.cjs` in the import\n// specifier — the lint rule's hard-coded extension matrix doesn't model\n// dual-package CJS subpath exports.\n// eslint-disable-next-line import-x/extensions\nimport { Tiktoken } from 'js-tiktoken/lite';\n// eslint-disable-next-line import-x/extensions\nimport cl100k_base from 'js-tiktoken/ranks/cl100k_base';\nimport yaml from 'js-yaml';\n\nimport pkg from '../package.json' with { type: 'json' };\n\nimport type { IIgnoreFilter } from './scan/ignore.js';\nimport type { Kernel } from './index.js';\nimport type {\n Confidence,\n Issue,\n Link,\n LinkKind,\n Node,\n ScanResult,\n ScanScannedBy,\n Severity,\n TripleSplit,\n} from './types.js';\nimport type {\n ProgressEmitterPort,\n ProgressEvent,\n} from './ports/progress-emitter.js';\nimport { InMemoryProgressEmitter } from './adapters/in-memory-progress.js';\nimport { log } from './util/logger.js';\nimport { installedSpecVersion } from './adapters/plugin-loader.js';\nimport type { IPluginStore } from './adapters/plugin-store.js';\nimport {\n buildProviderFrontmatterValidator,\n type IProviderFrontmatterValidator,\n} from './adapters/schema-validators.js';\nimport { ORCHESTRATOR_TEXTS } from './i18n/orchestrator.texts.js';\nimport { qualifiedExtensionId } from './registry.js';\nimport { tx } from './util/tx.js';\nimport type {\n IProvider,\n IRawNode,\n IExtractorContext,\n IExtractor,\n IHook,\n IHookContext,\n IRule,\n THookTrigger,\n} from './extensions/index.js';\n\n// Resolved once at module init so every scan reuses the same metadata.\n// `installedSpecVersion()` reads `@skill-map/spec/package.json` off disk;\n// failure is non-fatal — fall back to `'unknown'` and keep the field\n// shape spec-conformant (string).\nconst SCANNED_BY: ScanScannedBy = {\n name: 'skill-map',\n version: pkg.version,\n specVersion: resolveSpecVersionSafe(),\n};\n\nfunction resolveSpecVersionSafe(): string {\n try {\n return installedSpecVersion();\n } catch {\n return 'unknown';\n }\n}\n\nexport interface IScanExtensions {\n providers: IProvider[];\n extractors: IExtractor[];\n rules: IRule[];\n /**\n * Optional hooks (spec § A.11). When supplied, the orchestrator's\n * lifecycle dispatcher invokes deterministic hooks subscribed to one\n * of the eight hookable triggers in canonical order with the matching\n * event payload. Absent → no hooks fire (the scan still emits its\n * lifecycle events to `ProgressEmitterPort` for observability).\n * Probabilistic hooks are loaded but skipped here with a stderr\n * advisory until the job subsystem ships once the job subsystem ships.\n */\n hooks?: IHook[];\n}\n\n/**\n * Confidence-tagged plan to repoint `state_*` references from one node\n * path to another. Emitted by the rename heuristic during `runScan` and\n * consumed by `persistScanResult` so the FK migration runs inside the\n * same transaction as the scan zone replace-all.\n */\nexport interface RenameOp {\n from: string;\n to: string;\n confidence: 'high' | 'medium';\n}\n\nexport interface RunScanOptions {\n /**\n * Filesystem roots to walk. Spec requires `minItems: 1`; passing an\n * empty array makes `runScan` throw before any work happens.\n */\n roots: string[];\n emitter?: ProgressEmitterPort;\n /** Runtime extension instances. Absent → empty pipeline. */\n extensions?: IScanExtensions;\n /**\n * Scan scope. Defaults to `'project'`. The CLI flag wiring lands in\n * the config layer wiring; `runScan` already accepts the override\n * so plugins / tests can opt into `'global'` today.\n */\n scope?: 'project' | 'global';\n /**\n * Compute per-node token counts (frontmatter / body / total) using the\n * cl100k_base BPE (the modern OpenAI tokenizer used by GPT-4 / GPT-3.5).\n * Defaults to true. Set false to skip tokenization; `node.tokens` is\n * left undefined (spec-valid: the field is optional).\n */\n tokenize?: boolean;\n /**\n * Prior snapshot for two purposes (decoupled by design):\n *\n * 1. **Rename heuristic** (`spec/db-schema.md` §Rename detection):\n * always evaluated when `priorSnapshot` is supplied. The\n * heuristic compares prior vs current node paths and emits\n * high / medium / ambiguous / orphan classifications. This\n * runs on EVERY `sm scan` (with or without `--changed`) so\n * reorganising files always preserves history, never silently.\n *\n * 2. **Cache reuse** (`sm scan --changed`): only kicks in when\n * `enableCache: true` is also passed. With the flag set, nodes\n * whose `path` exists in the prior with both `bodyHash` and\n * `frontmatterHash` matching the freshly-computed hashes are\n * reused as-is (their internal links and `externalRefsCount`\n * survive); only new / modified nodes run through extractors.\n * Rules always re-run over the merged graph.\n *\n * Pass `null` (or omit) for a fresh scan with no rename detection.\n */\n priorSnapshot?: ScanResult | null;\n /**\n * Reuse unchanged nodes from `priorSnapshot` instead of re-running\n * extractors over them. Defaults to `false` so a plain `sm scan`\n * always re-walks deterministically. `sm scan --changed` flips this\n * to `true` for the perf win on unchanged files.\n *\n * Has no effect without `priorSnapshot`; setting it to `true` with\n * a null prior is a no-op (every file is \"new\").\n */\n enableCache?: boolean;\n /**\n * Filter that decides which paths the Providers skip. Composed by the\n * caller (typically the CLI) from bundled defaults + `config.ignore`\n * + `.skill-mapignore`. Providers that omit this option fall back to\n * their own defensive defaults (just enough to keep `.git` /\n * `node_modules` out).\n */\n ignoreFilter?: IIgnoreFilter;\n /**\n * Promote frontmatter-validation findings from `warn` to `error`.\n * Defaults to false. The CLI surfaces this via `--strict` on `sm scan`\n * and the `scan.strict` config key. When false, the orchestrator\n * still emits a `frontmatter-invalid` issue per malformed file but\n * leaves the severity at `warn` so a clean scan exits 0; when true,\n * the same finding becomes `error` and the scan exits 1.\n */\n strict?: boolean;\n /**\n * Spec § A.9 — fine-grained Extractor cache breadcrumbs from the\n * prior scan. Shape: `Map<nodePath, Map<qualifiedExtractorId, bodyHashAtRun>>`.\n * Loaded from the `scan_extractor_runs` table by the CLI before\n * invoking `runScan`; absent / empty for a fresh DB or an out-of-band\n * caller that does not maintain a cache. Decoupled from `priorSnapshot`\n * because the runs live in a sibling table and are useful only when\n * `enableCache` is also set.\n *\n * Cache decision per `(node, extractor)`:\n * - body+frontmatter hashes match the prior node AND every currently-\n * registered extractor that applies to this kind has a matching\n * row → full skip, all prior outbound links reused.\n * - some applicable extractor lacks a matching row (newly registered,\n * or its prior run targeted a different body hash) → run only the\n * missing extractors, drop prior links whose `sources` map to any\n * missing extractor or to an extractor that is no longer registered.\n */\n priorExtractorRuns?: Map<string, Map<string, string>>;\n /**\n * Spec § A.12 — per-plugin storage wrappers exposed to extractors via\n * `ctx.store`. Keyed by `pluginId`; absent / missing entry leaves\n * `ctx.store` undefined for that extractor (the existing contract).\n *\n * The kernel does not construct these — the driving adapter (CLI,\n * future server) builds them with `makePluginStore` from\n * `kernel/adapters/plugin-store.js` and threads them through. This\n * keeps the orchestrator persistence-agnostic (the wrapper supplies\n * its own persist callback) and lets tests inject a captured-call\n * mock without spinning up a DB.\n */\n pluginStores?: ReadonlyMap<string, IPluginStore>;\n}\n\n/**\n * Spec § A.9 — runs to persist into `scan_extractor_runs`. One entry\n * per `(nodePath, qualifiedExtractorId)` pair the orchestrator decided\n * \"this extractor is current for this body\". Includes both freshly-run\n * pairs (extractor invoked this scan) and reused pairs (cached node, the\n * extractor's prior run still applies to the same body hash). Excludes\n * obsolete pairs — extractors that ran in the prior but are no longer\n * registered — so a replace-all persist drops them automatically.\n */\nexport interface IExtractorRunRecord {\n nodePath: string;\n extractorId: string;\n bodyHashAtRun: string;\n ranAt: number;\n}\n\n/**\n * Spec § A.8 — universal enrichment layer.\n *\n * One entry per `(nodePath, qualifiedExtractorId)` pair an Extractor\n * produced via `ctx.enrichNode(...)` during the walk. Attribution is\n * preserved per-Extractor (rather than merged client-side as B.1 did)\n * so the persistence layer can:\n *\n * - upsert a single row per pair (stable PRIMARY KEY conflict on\n * re-extract);\n * - flag probabilistic rows `stale = 1` when the body changes between\n * scans (preserving the prior LLM cost);\n * - feed `mergeNodeWithEnrichments` with `enrichedAt`-sorted partials\n * for last-write-wins per field at read time.\n *\n * `value` is the cumulative merge across every `enrichNode` call that\n * Extractor made for this node within this scan — multiple\n * `ctx.enrichNode({...})` calls inside one `extract(ctx)` invocation\n * fold into a single row, but two different Extractors hitting the\n * same node yield two distinct rows.\n *\n * `isProbabilistic` is denormalised so the persistence layer's stale\n * flag query stays a single-table read; recomputing from the live\n * registry would force every read-path to thread the runtime extension\n * set through.\n */\nexport interface IEnrichmentRecord {\n nodePath: string;\n extractorId: string;\n bodyHashAtEnrichment: string;\n value: Partial<Node>;\n enrichedAt: number;\n isProbabilistic: boolean;\n}\n\n/**\n * Same as `runScan` but also returns the rename heuristic's `RenameOp[]`\n * — the high- and medium-confidence renames the persistence layer must\n * apply to `state_*` rows inside the same tx as the scan zone replace-\n * all (per `spec/db-schema.md` §Rename detection). Most callers want\n * `runScan` (which returns just `ScanResult`); the CLI's `sm scan`\n * uses this variant so it can hand the ops off to `persistScanResult`.\n *\n * Also returns `extractorRuns` — the Spec § A.9 fine-grained cache\n * breadcrumbs the CLI persists into `scan_extractor_runs` so the next\n * incremental scan can decide per-(node, extractor) whether re-running\n * is required.\n */\nexport async function runScanWithRenames(\n _kernel: Kernel,\n options: RunScanOptions,\n): Promise<{\n result: ScanResult;\n renameOps: RenameOp[];\n extractorRuns: IExtractorRunRecord[];\n enrichments: IEnrichmentRecord[];\n}> {\n return runScanInternal(_kernel, options);\n}\n\nexport async function runScan(\n _kernel: Kernel,\n options: RunScanOptions,\n): Promise<ScanResult> {\n const { result } = await runScanInternal(_kernel, options);\n return result;\n}\n\n// eslint-disable-next-line complexity\nasync function runScanInternal(\n _kernel: Kernel,\n options: RunScanOptions,\n): Promise<{\n result: ScanResult;\n renameOps: RenameOp[];\n extractorRuns: IExtractorRunRecord[];\n enrichments: IEnrichmentRecord[];\n}> {\n validateRoots(options.roots);\n\n const start = Date.now();\n const scannedAt = start;\n const emitter = options.emitter ?? new InMemoryProgressEmitter();\n const exts = options.extensions ?? { providers: [], extractors: [], rules: [] };\n const hookDispatcher = makeHookDispatcher(exts.hooks ?? [], emitter);\n const tokenize = options.tokenize !== false;\n const scope: 'project' | 'global' = options.scope ?? 'project';\n const strict = options.strict === true;\n // Encoder is heavyweight to construct (loads the cl100k_base BPE table\n // once); reuse a single instance across the whole scan.\n const encoder = tokenize ? new Tiktoken(cl100k_base) : null;\n const prior = options.priorSnapshot ?? null;\n const enableCache = options.enableCache === true;\n // Spec § A.9 — `priorExtractorRuns === undefined` means the caller\n // doesn't track the fine-grained Extractor cache (legacy behaviour: out-\n // of-band tests, alternate driving adapters that have no DB). In that\n // case we fall back to the pre-A.9 model where the node-level body /\n // frontmatter hash check is sufficient and every applicable extractor\n // is assumed to have run against the prior body. Passing an explicit\n // (possibly empty) Map opts the caller into the fine-grained path.\n const priorExtractorRuns = options.priorExtractorRuns;\n\n const priorIndex = indexPriorSnapshot(prior);\n\n // Spec 0.8.0: each Provider owns its per-kind frontmatter\n // schemas. Compose a single AJV-backed validator over the live set of\n // Providers so the orchestrator can ask it directly during the walk.\n const providerFrontmatter = buildProviderFrontmatterValidator(exts.providers);\n\n const scanStartedEvent = makeEvent('scan.started', { roots: options.roots });\n emitter.emit(scanStartedEvent);\n await hookDispatcher.dispatch('scan.started', scanStartedEvent);\n\n const walked = await walkAndExtract({\n providers: exts.providers,\n extractors: exts.extractors,\n roots: options.roots,\n ...(options.ignoreFilter ? { ignoreFilter: options.ignoreFilter } : {}),\n emitter,\n encoder,\n strict,\n enableCache,\n prior,\n priorIndex,\n priorExtractorRuns,\n providerFrontmatter,\n pluginStores: options.pluginStores,\n });\n\n // External pseudo-links (target is http(s)://) drive `externalRefsCount`\n // and are then dropped: never persisted, never seen by rules, never in\n // result.links. The string-prefix check is the contract — see\n // external-url-counter/index.ts.\n recomputeLinkCounts(walked.nodes, walked.internalLinks);\n recomputeExternalRefsCount(walked.nodes, walked.externalLinks, walked.cachedPaths);\n\n // Spec § A.11 — Hook dispatch for `extractor.completed`. Aggregated:\n // one event per registered extractor, after the full walk completes.\n // The payload carries the qualified extractor id so a hook with a\n // `filter: { extractorId: '...' }` can target a single extractor.\n // No per-node fan-out — that lives in `scan.progress` which is\n // deliberately NOT hookable (too verbose).\n for (const extractor of exts.extractors) {\n const extractorId = qualifiedExtensionId(extractor.pluginId, extractor.id);\n const evt = makeEvent('extractor.completed', { extractorId });\n emitter.emit(evt);\n await hookDispatcher.dispatch('extractor.completed', evt);\n }\n\n // Rules ALWAYS re-run over the merged graph (no shortcut for\n // incremental scans): the issue set for an \"unchanged\" node can flip\n // when a sibling node changes.\n const issues = await runRules(exts.rules, walked.nodes, walked.internalLinks, emitter, hookDispatcher);\n // Frontmatter-invalid issues from the walk land here so the rename\n // heuristic (next pass) sees them and the final stats.issuesCount\n // reflects them.\n for (const issue of walked.frontmatterIssues) issues.push(issue);\n\n // Rename heuristic runs after rules so the merged graph is final. The\n // returned `RenameOp[]` flows through to `persistScanResult` so FK\n // migration lands inside the same tx as the scan zone replace-all.\n const renameOps = prior ? detectRenamesAndOrphans(prior, walked.nodes, issues) : [];\n\n const stats = {\n // `filesSkipped` is \"files walked but not classified by any Provider\".\n // Today every walked file IS classified by its Provider (the `claude`\n // Provider's `classify()` always returns a kind, falling back to\n // `'note'`), so this is always 0. Wired now so the field shape is\n // spec-conformant; meaningful once multiple Providers compete.\n filesWalked: walked.filesWalked,\n filesSkipped: 0,\n nodesCount: walked.nodes.length,\n linksCount: walked.internalLinks.length,\n issuesCount: issues.length,\n durationMs: Date.now() - start,\n };\n\n const scanCompletedEvent = makeEvent('scan.completed', { stats });\n emitter.emit(scanCompletedEvent);\n await hookDispatcher.dispatch('scan.completed', scanCompletedEvent);\n\n return {\n result: {\n schemaVersion: 1,\n scannedAt,\n scope,\n roots: options.roots,\n providers: exts.providers.map((a) => a.id),\n scannedBy: SCANNED_BY,\n nodes: walked.nodes,\n links: walked.internalLinks,\n issues,\n stats,\n },\n renameOps,\n extractorRuns: walked.extractorRuns,\n enrichments: walked.enrichments,\n };\n}\n\n/**\n * Validate every root exists as a directory BEFORE any IO, BEFORE the\n * tokenizer is constructed, BEFORE `scan.started` fires. Throws on the\n * first failure — single-error feedback is enough; the user fixes it\n * and re-runs. Without this guard the claude Provider's `walk()` swallows\n * ENOENT inside `readdir` and returns silently, which lets a non-existent\n * root produce a valid-looking zero-filled `ScanResult` — directly\n * enabling the `sm scan -- --dry-run` typo-trap that wipes a populated\n * DB.\n *\n * Spec contract (`scan-result.schema.json#/properties/roots/minItems: 1`):\n * a ScanResult must report at least one walked root. The CLI defaults\n * `roots` to `['.']` when no positional args are supplied, so the\n * empty-array branch is a programming error from the CLI surface.\n */\nfunction validateRoots(roots: string[]): void {\n if (roots.length === 0) {\n throw new Error(ORCHESTRATOR_TEXTS.runScanRootEmptyArray);\n }\n for (const root of roots) {\n if (!existsSync(root) || !statSync(root).isDirectory()) {\n throw new Error(tx(ORCHESTRATOR_TEXTS.runScanRootMissing, { root }));\n }\n }\n}\n\ninterface IPriorIndex {\n /** Prior nodes keyed by path so per-file lookup is O(1). */\n priorNodesByPath: Map<string, Node>;\n /** Set of every prior node path — used to disambiguate inverted\n * `supersedes` links (see `originatingNodeOf`). */\n priorNodePaths: Set<string>;\n /**\n * Prior internal links bucketed by **originating node** — the node\n * whose body / frontmatter the extractor was processing when it emitted\n * the link. For most kinds that equals `link.source`, but the\n * frontmatter extractor emits inverted `supersedes` links where the\n * originating node is `link.target`.\n */\n priorLinksByOriginating: Map<string, Link[]>;\n /**\n * Per-node frontmatter-invalid / -malformed issues from the prior — we\n * reuse them when the cache is hit, otherwise the incremental scan\n * would silently drop the warning that landed on the prior pass.\n */\n priorFrontmatterIssuesByNode: Map<string, Issue[]>;\n}\n\n// eslint-disable-next-line complexity\nfunction indexPriorSnapshot(prior: ScanResult | null): IPriorIndex {\n const priorNodesByPath = new Map<string, Node>();\n const priorNodePaths = new Set<string>();\n const priorLinksByOriginating = new Map<string, Link[]>();\n const priorFrontmatterIssuesByNode = new Map<string, Issue[]>();\n if (!prior) {\n return { priorNodesByPath, priorNodePaths, priorLinksByOriginating, priorFrontmatterIssuesByNode };\n }\n for (const node of prior.nodes) {\n priorNodesByPath.set(node.path, node);\n priorNodePaths.add(node.path);\n }\n for (const link of prior.links) {\n const key = originatingNodeOf(link, priorNodePaths);\n const list = priorLinksByOriginating.get(key);\n if (list) list.push(link);\n else priorLinksByOriginating.set(key, [link]);\n }\n for (const issue of prior.issues) {\n if (issue.ruleId !== 'frontmatter-invalid' && issue.ruleId !== 'frontmatter-malformed') continue;\n if (issue.nodeIds.length !== 1) continue;\n const path = issue.nodeIds[0]!;\n const list = priorFrontmatterIssuesByNode.get(path);\n if (list) list.push(issue);\n else priorFrontmatterIssuesByNode.set(path, [issue]);\n }\n return { priorNodesByPath, priorNodePaths, priorLinksByOriginating, priorFrontmatterIssuesByNode };\n}\n\ninterface IWalkAndExtractOptions {\n providers: IProvider[];\n extractors: IExtractor[];\n roots: string[];\n ignoreFilter?: IIgnoreFilter;\n emitter: ProgressEmitterPort;\n encoder: Tiktoken | null;\n strict: boolean;\n enableCache: boolean;\n prior: ScanResult | null;\n priorIndex: IPriorIndex;\n /**\n * Spec § A.9 — fine-grained Extractor cache breadcrumbs from the\n * prior scan, keyed `nodePath → qualifiedExtractorId → bodyHashAtRun`.\n * `undefined` opts out of the fine-grained path (legacy callers that\n * don't track the cache); the orchestrator falls back to the pre-A.9\n * node-level cache check.\n */\n priorExtractorRuns: Map<string, Map<string, string>> | undefined;\n providerFrontmatter: IProviderFrontmatterValidator;\n /**\n * Spec § A.12 — per-plugin `ctx.store` wrappers, keyed by `pluginId`.\n * Threaded through to `runExtractorsForNode → buildExtractorContext`\n * unchanged. `undefined` keeps `ctx.store` undefined for every\n * extractor (the legacy contract).\n */\n pluginStores: ReadonlyMap<string, IPluginStore> | undefined;\n}\n\ninterface IWalkAndExtractResult {\n nodes: Node[];\n internalLinks: Link[];\n externalLinks: Link[];\n /** Node paths reused verbatim from the prior snapshot. Their\n * `externalRefsCount` must NOT be zeroed before recomputation. */\n cachedPaths: Set<string>;\n /** Frontmatter-validation findings collected during the walk; the\n * composer appends these to the rule-emitted issue list so the\n * final ordering stays \"rules first, then derived issues\". */\n frontmatterIssues: Issue[];\n /**\n * Spec § A.8 — per-extractor enrichment records collected from\n * `ctx.enrichNode(...)` calls during the walk. One entry per\n * `(nodePath, extractorId)` pair an Extractor enriched. The\n * persistence layer upserts these into `node_enrichments`; the\n * read-side `mergeNodeWithEnrichments` helper combines them with\n * the author frontmatter for rule consumption.\n *\n * Attribution is preserved per-Extractor: two Extractors enriching\n * the same node produce two records, not one merged value. If a\n * single Extractor calls `ctx.enrichNode(...)` multiple times within\n * one `extract()` invocation, the partials fold into one record's\n * `value` (last-write-wins per field).\n */\n enrichments: IEnrichmentRecord[];\n /** Every `IRawNode` a Provider yielded across the whole scan\n * (including cached reuse). With one Provider it equals\n * `nodesCount`; with future multi-Provider scans walking overlapping\n * roots it can diverge. */\n filesWalked: number;\n /**\n * Spec § A.9 — the rows the persistence layer writes into\n * `scan_extractor_runs`. Includes both freshly-run pairs (extractor\n * invoked this scan) and reused pairs (cached node, the extractor's\n * prior run still applies to the same body hash). Excludes obsolete\n * pairs (extractor was uninstalled since the prior scan).\n */\n extractorRuns: IExtractorRunRecord[];\n}\n\n/**\n * Run a set of extractors against a single node, collecting their link\n * emissions and node-enrichment partials. Each extractor is invoked\n * exactly once with a fresh `IExtractorContext`. Caller decides what\n * to do with the returned arrays (push into per-scan buffers, write to\n * a focused refresh result, etc.).\n *\n * Exported so `cli/commands/refresh.ts` can reuse the same wiring it\n * needs for re-running a single extractor against a single node — the\n * pre-extraction code in `refresh.ts` was hand-duplicating this loop\n * (audit item V4).\n *\n * Within this call, multiple `enrichNode(partial)` calls from the same\n * extractor against the same node fold into one record (last-write-wins\n * per field) — same contract as the in-scan path.\n */\nexport async function runExtractorsForNode(opts: {\n extractors: IExtractor[];\n node: Node;\n body: string;\n frontmatter: Record<string, unknown>;\n bodyHash: string;\n emitter: ProgressEmitterPort;\n /**\n * Spec § A.12 — per-plugin `ctx.store` wrappers keyed by `pluginId`.\n * The map's lookup is per-extractor inside the loop, so callers that\n * don't track plugin storage can omit it; the resulting `ctx.store`\n * stays `undefined` (the existing contract).\n */\n pluginStores?: ReadonlyMap<string, IPluginStore>;\n}): Promise<{\n internalLinks: Link[];\n externalLinks: Link[];\n enrichments: IEnrichmentRecord[];\n}> {\n const internalLinks: Link[] = [];\n const externalLinks: Link[] = [];\n const enrichmentBuffer = new Map<string, IEnrichmentRecord>();\n\n for (const extractor of opts.extractors) {\n const qualifiedId = qualifiedExtensionId(extractor.pluginId, extractor.id);\n const isProb = extractor.mode === 'probabilistic';\n const emitLink = (link: Link): void => {\n const validated = validateLink(extractor, link, opts.emitter);\n if (!validated) return;\n if (isExternalUrlLink(validated)) externalLinks.push(validated);\n else internalLinks.push(validated);\n };\n const enrichNode = (partial: Partial<Node>): void => {\n const key = `${opts.node.path}\\x00${qualifiedId}`;\n const existing = enrichmentBuffer.get(key);\n if (existing) {\n existing.value = { ...existing.value, ...partial };\n existing.enrichedAt = Date.now();\n } else {\n enrichmentBuffer.set(key, {\n nodePath: opts.node.path,\n extractorId: qualifiedId,\n bodyHashAtEnrichment: opts.bodyHash,\n value: { ...partial },\n enrichedAt: Date.now(),\n isProbabilistic: isProb,\n });\n }\n };\n const store = opts.pluginStores?.get(extractor.pluginId);\n const ctx = buildExtractorContext(\n extractor,\n opts.node,\n opts.body,\n opts.frontmatter,\n emitLink,\n enrichNode,\n store,\n );\n await extractor.extract(ctx);\n }\n\n return {\n internalLinks,\n externalLinks,\n enrichments: Array.from(enrichmentBuffer.values()),\n };\n}\n\n/**\n * Compute the per-(node, extractor) cache decision for a single node.\n * Returns:\n * - `applicableExtractors` — extractors whose `applicableKinds`\n * accepts this node's kind (or unrestricted).\n * - `applicableQualifiedIds` — set of qualified ids of the above.\n * - `cachedQualifiedIds` — applicable extractors whose prior run for\n * this node's body hash is still valid.\n * - `missingExtractors` — applicable extractors that need to run.\n * - `fullCacheHit` — true iff the node-level hash matched AND every\n * applicable extractor is cached (nothing to re-extract).\n *\n * Legacy fallback: when `priorExtractorRuns === undefined` the caller\n * did not load fine-grained breadcrumbs (out-of-band tests, alternate\n * driving adapters); we treat every applicable extractor as cached\n * when the node-level hashes match — preserves the pre-A.9 contract.\n */\n// eslint-disable-next-line complexity\nfunction computeCacheDecision(opts: {\n extractors: IExtractor[];\n kind: string;\n nodePath: string;\n bodyHash: string;\n nodeHashCacheEligible: boolean;\n priorExtractorRuns: Map<string, Map<string, string>> | undefined;\n}): {\n applicableExtractors: IExtractor[];\n applicableQualifiedIds: Set<string>;\n cachedQualifiedIds: Set<string>;\n missingExtractors: IExtractor[];\n fullCacheHit: boolean;\n} {\n const applicableExtractors = opts.extractors.filter(\n (ex) => ex.applicableKinds === undefined || ex.applicableKinds.includes(opts.kind),\n );\n const applicableQualifiedIds = new Set(\n applicableExtractors.map((ex) => qualifiedExtensionId(ex.pluginId, ex.id)),\n );\n const cachedQualifiedIds = new Set<string>();\n const missingExtractors: IExtractor[] = [];\n\n if (opts.priorExtractorRuns === undefined) {\n if (opts.nodeHashCacheEligible) {\n for (const id of applicableQualifiedIds) cachedQualifiedIds.add(id);\n } else {\n for (const ex of applicableExtractors) missingExtractors.push(ex);\n }\n } else {\n const priorRunsForNode = opts.priorExtractorRuns.get(opts.nodePath) ?? new Map<string, string>();\n for (const ex of applicableExtractors) {\n const qualified = qualifiedExtensionId(ex.pluginId, ex.id);\n const priorBody = priorRunsForNode.get(qualified);\n if (opts.nodeHashCacheEligible && priorBody === opts.bodyHash) {\n cachedQualifiedIds.add(qualified);\n } else {\n missingExtractors.push(ex);\n }\n }\n }\n\n return {\n applicableExtractors,\n applicableQualifiedIds,\n cachedQualifiedIds,\n missingExtractors,\n fullCacheHit: opts.nodeHashCacheEligible && missingExtractors.length === 0,\n };\n}\n\n/**\n * Shallow-clone a prior node, reshape its outbound internal links per\n * A.9 source rules, and re-emit its prior frontmatter issues. Shared\n * by the full-cache-hit and partial-cache-hit branches of\n * `walkAndExtract`.\n *\n * Reshape rules (A.9 sources):\n * - missing source (extractor will re-emit) → drop link\n * - all-obsolete sources → drop link\n * - cached + obsolete → trim obsolete from `sources`\n * - cached only → keep verbatim\n */\nfunction cloneNodeAndReshapeLinks(opts: {\n priorNode: Node;\n strict: boolean;\n cachedQualifiedIds: Set<string>;\n applicableQualifiedIds: Set<string>;\n shortIdToQualified: Map<string, string[]>;\n priorLinksByOriginating: Map<string, Link[]>;\n priorFrontmatterIssuesByNode: Map<string, Issue[]>;\n}): { node: Node; internalLinks: Link[]; frontmatterIssues: Issue[] } {\n // Shallow-clone to avoid mutating the caller's prior snapshot when\n // `recomputeLinkCounts` resets per-node counts later.\n const node: Node = { ...opts.priorNode, bytes: { ...opts.priorNode.bytes } };\n if (opts.priorNode.tokens) node.tokens = { ...opts.priorNode.tokens };\n\n const internalLinks: Link[] = [];\n const reusedLinks = opts.priorLinksByOriginating.get(opts.priorNode.path) ?? [];\n for (const link of reusedLinks) {\n const reshaped = reuseCachedLink(\n link,\n opts.shortIdToQualified,\n opts.cachedQualifiedIds,\n opts.applicableQualifiedIds,\n );\n if (reshaped) internalLinks.push(reshaped);\n }\n\n // Re-emit prior frontmatter issues unchanged (frontmatter hash is\n // unchanged in both cache branches). `strict` can promote\n // `warn → error` retroactively.\n const frontmatterIssues: Issue[] = [];\n const reusedFm = opts.priorFrontmatterIssuesByNode.get(opts.priorNode.path) ?? [];\n for (const issue of reusedFm) {\n frontmatterIssues.push({ ...issue, severity: opts.strict ? 'error' : 'warn' });\n }\n\n return { node, internalLinks, frontmatterIssues };\n}\n\n/**\n * Build the reused-node bundle for a node that fully cache-hit (body\n * + frontmatter unchanged AND every applicable extractor still has a\n * matching `scan_extractor_runs` row). Caller pushes the returned\n * arrays into its scan-wide buffers and emits the progress event.\n *\n * Adds `extractorRuns` rows for every still-cached extractor so the\n * cache survives the next replace-all persist.\n */\nfunction reusePriorNode(opts: {\n priorNode: Node;\n bodyHash: string;\n strict: boolean;\n cachedQualifiedIds: Set<string>;\n applicableQualifiedIds: Set<string>;\n shortIdToQualified: Map<string, string[]>;\n priorLinksByOriginating: Map<string, Link[]>;\n priorFrontmatterIssuesByNode: Map<string, Issue[]>;\n}): {\n node: Node;\n internalLinks: Link[];\n frontmatterIssues: Issue[];\n extractorRuns: IExtractorRunRecord[];\n} {\n const base = cloneNodeAndReshapeLinks(opts);\n\n // Persist one `scan_extractor_runs` row per still-cached pair so the\n // cache survives the next replace-all persist (without this, cached\n // pairs silently disappear).\n const ranAt = Date.now();\n const extractorRuns: IExtractorRunRecord[] = [];\n for (const qualified of opts.cachedQualifiedIds) {\n extractorRuns.push({\n nodePath: opts.priorNode.path,\n extractorId: qualified,\n bodyHashAtRun: opts.bodyHash,\n ranAt,\n });\n }\n\n return { ...base, extractorRuns };\n}\n\n/**\n * Build a brand-new `Node` row from raw provider output and validate\n * its frontmatter. Used by the \"no cache hit\" branch of\n * `walkAndExtract`. Two frontmatter issue paths:\n * - With a frontmatter fence: AJV-validate against the Provider's\n * per-kind schema.\n * - Without a fence but a body that opens with malformed `---`:\n * emit `frontmatter-malformed`.\n *\n * Severity defaults to `warn`; `strict` promotes everything to `error`.\n */\nfunction buildFreshNodeAndValidateFrontmatter(opts: {\n raw: IRawNode;\n kind: string;\n provider: IProvider;\n bodyHash: string;\n frontmatterHash: string;\n encoder: Tiktoken | null;\n providerFrontmatter: IProviderFrontmatterValidator;\n strict: boolean;\n}): { node: Node; frontmatterIssues: Issue[] } {\n const node = buildNode({\n path: opts.raw.path,\n kind: opts.kind,\n providerId: opts.provider.id,\n frontmatterRaw: opts.raw.frontmatterRaw,\n body: opts.raw.body,\n frontmatter: opts.raw.frontmatter,\n bodyHash: opts.bodyHash,\n frontmatterHash: opts.frontmatterHash,\n encoder: opts.encoder,\n });\n\n const frontmatterIssues: Issue[] = [];\n if (opts.raw.frontmatterRaw.length > 0) {\n const fmIssue = validateFrontmatter(\n opts.providerFrontmatter,\n opts.provider,\n opts.kind,\n opts.raw.frontmatter,\n opts.raw.path,\n opts.strict,\n );\n if (fmIssue) frontmatterIssues.push(fmIssue);\n } else {\n const malformed = detectMalformedFrontmatter(opts.raw.body, opts.raw.path, opts.strict);\n if (malformed) frontmatterIssues.push(malformed);\n }\n\n return { node, frontmatterIssues };\n}\n\n// Main scan loop — for each provider/raw node: hash, classify, decide\n// cache (full / partial / none), reuse or build, run extractors,\n// record runs. Helpers extracted (`computeCacheDecision`,\n// `cloneNodeAndReshapeLinks`, `reusePriorNode`,\n// `buildFreshNodeAndValidateFrontmatter`, `runExtractorsForNode`)\n// already encapsulate the heavy-lift; remaining branching is the\n// dispatch glue that ties them together per-iteration.\n// eslint-disable-next-line complexity\nasync function walkAndExtract(opts: IWalkAndExtractOptions): Promise<IWalkAndExtractResult> {\n const {\n providers,\n extractors,\n roots,\n ignoreFilter,\n emitter,\n encoder,\n strict,\n enableCache,\n prior,\n priorIndex,\n priorExtractorRuns,\n providerFrontmatter,\n pluginStores,\n } = opts;\n const { priorNodesByPath, priorLinksByOriginating, priorFrontmatterIssuesByNode } = priorIndex;\n\n const nodes: Node[] = [];\n const internalLinks: Link[] = [];\n const externalLinks: Link[] = [];\n const cachedPaths = new Set<string>();\n const frontmatterIssues: Issue[] = [];\n // A.8 enrichment buffer. `ctx.enrichNode(partial)` calls fold into a\n // per-Extractor entry keyed by `(nodePath, qualifiedExtractorId)` so the\n // persistence layer can upsert exactly one row per pair into\n // `node_enrichments`. Attribution survives across scans, which lets:\n // - the stale flag query single-table on (extractor_id, body_hash);\n // - `sm refresh` re-run only the Extractor whose row is stale;\n // - the read-time merge sort by `enriched_at` for last-write-wins.\n // Within a single `extract()` invocation, multiple enrichNode calls fold\n // into the same record's `value` (last-write-wins per field).\n const enrichmentBuffer = new Map<string, IEnrichmentRecord>();\n // Spec § A.9 — accumulator for `scan_extractor_runs`. One row per\n // (nodePath, qualifiedExtractorId) pair the orchestrator decided \"this\n // extractor is current for this body\". Includes both freshly-run pairs\n // and pairs whose prior run was reused intact via the cache.\n const extractorRuns: IExtractorRunRecord[] = [];\n let filesWalked = 0;\n let index = 0;\n const walkOptions = ignoreFilter ? { ignoreFilter } : {};\n\n // Build the short→qualified id map once for the whole scan. Used to\n // bridge between author-supplied `link.sources` (short id, e.g.\n // `'slash'`) and the qualified ids (`'claude/slash'`) that drive cache\n // bookkeeping. Multiple plugins can in theory expose extractors with\n // the same short id; we keep all qualifieds per short id so the\n // partial-cache filter recognises any of them as \"still cached\".\n const shortIdToQualified = new Map<string, string[]>();\n for (const ex of extractors) {\n const qualified = qualifiedExtensionId(ex.pluginId, ex.id);\n const list = shortIdToQualified.get(ex.id);\n if (list) list.push(qualified);\n else shortIdToQualified.set(ex.id, [qualified]);\n }\n\n for (const provider of providers) {\n for await (const raw of provider.walk(roots, walkOptions)) {\n filesWalked += 1;\n const bodyHash = sha256(raw.body);\n // Canonical-form rationale — hash a CANONICAL form of the frontmatter so a YAML\n // formatter pass (re-indent, sort keys, normalise trailing\n // newline, swap single↔double quotes) doesn't break the\n // medium-confidence rename heuristic. Fallback to raw text when\n // canonicalisation produces empty (parse failed but raw is\n // non-empty) so a malformed-YAML file still hashes\n // deterministically against itself.\n const frontmatterHash = sha256(canonicalFrontmatter(raw.frontmatter, raw.frontmatterRaw));\n const priorNode = priorNodesByPath.get(raw.path);\n // Cache reuse is gated on the explicit `enableCache` option (Step\n // 5.8). The presence of a `prior` alone is no longer enough — a\n // plain `sm scan` always re-walks deterministically; only\n // `sm scan --changed` flips `enableCache` on. The rename heuristic\n // uses `prior` independently of `enableCache`.\n //\n // Spec § A.9 layered the per-(node, extractor) check on top of\n // the existing per-node body+frontmatter check. The node-level\n // hashes still gate cache eligibility (a body change forces a full\n // re-extract regardless of which extractors were registered);\n // within an eligible node we then ask \"did every currently-applicable\n // extractor run against this body hash already?\". A new extractor\n // registered between scans yields a partial hit: we run only the\n // newcomer.\n const nodeHashCacheEligible =\n enableCache &&\n prior !== null &&\n priorNode !== undefined &&\n priorNode.bodyHash === bodyHash &&\n priorNode.frontmatterHash === frontmatterHash;\n\n const kind = provider.classify(raw.path, raw.frontmatter);\n index += 1;\n\n // Per-node, per-extractor cache decision (only meaningful when the\n // node-level hashes already matched). For each extractor that\n // applies to this kind, ask whether the prior runs map already\n // records an entry against the current body hash. Missing entries\n // run; satisfied entries are skipped.\n //\n // Legacy fallback: when `priorExtractorRuns === undefined` the\n // caller did not load the fine-grained breadcrumbs (out-of-band\n // tests, alternate driving adapters), so we treat every applicable\n // extractor as cached when the node-level hashes match. This\n // preserves the pre-A.9 contract for callers that did not opt in.\n const cacheDecision = computeCacheDecision({\n extractors,\n kind,\n nodePath: raw.path,\n bodyHash,\n nodeHashCacheEligible,\n priorExtractorRuns,\n });\n const {\n applicableExtractors,\n applicableQualifiedIds,\n cachedQualifiedIds,\n missingExtractors,\n fullCacheHit,\n } = cacheDecision;\n\n if (fullCacheHit && priorNode) {\n const reused = reusePriorNode({\n priorNode,\n bodyHash,\n strict,\n cachedQualifiedIds,\n applicableQualifiedIds,\n shortIdToQualified,\n priorLinksByOriginating,\n priorFrontmatterIssuesByNode,\n });\n nodes.push(reused.node);\n cachedPaths.add(reused.node.path);\n for (const link of reused.internalLinks) internalLinks.push(link);\n for (const issue of reused.frontmatterIssues) frontmatterIssues.push(issue);\n for (const run of reused.extractorRuns) extractorRuns.push(run);\n emitter.emit(makeEvent('scan.progress', { index, path: raw.path, kind, cached: true }));\n continue;\n }\n\n // --- partial or full re-extract path -------------------------------\n // Either a brand-new node, a node whose body / frontmatter changed,\n // or a node whose hashes match but at least one applicable\n // extractor lacks a matching `scan_extractor_runs` row (newly\n // registered, or its prior run was against a different body hash).\n\n let node: Node;\n const partialCacheHit =\n nodeHashCacheEligible && cachedQualifiedIds.size > 0 && priorNode !== undefined;\n if (partialCacheHit && priorNode) {\n // Body / frontmatter unchanged AND at least one extractor is\n // still cached; reuse the prior node row + reshape its links\n // and frontmatter issues. NOT marking the path as `cachedPaths`\n // because some extraction is happening — the `externalRefsCount`\n // recompute wants the node re-derived from a fresh extractor\n // pass (the missing extractor may emit URLs).\n const partial = cloneNodeAndReshapeLinks({\n priorNode, strict, cachedQualifiedIds, applicableQualifiedIds,\n shortIdToQualified, priorLinksByOriginating, priorFrontmatterIssuesByNode,\n });\n node = partial.node;\n for (const link of partial.internalLinks) internalLinks.push(link);\n for (const issue of partial.frontmatterIssues) frontmatterIssues.push(issue);\n nodes.push(node);\n } else {\n const fresh = buildFreshNodeAndValidateFrontmatter({\n raw, kind, provider, bodyHash, frontmatterHash, encoder,\n providerFrontmatter, strict,\n });\n node = fresh.node;\n nodes.push(node);\n for (const issue of fresh.frontmatterIssues) frontmatterIssues.push(issue);\n }\n emitter.emit(makeEvent('scan.progress', {\n index,\n path: raw.path,\n kind,\n cached: false,\n ...(partialCacheHit ? { partialCache: true } : {}),\n }));\n\n // Decide which extractors actually run. Full re-extract → all\n // applicable. Partial cache → only the missing ones. Either way,\n // the orchestrator records a fresh `scan_extractor_runs` row for\n // each invocation AND for each cached extractor whose contribution\n // survived intact (so the cache persists across scans).\n const extractorsToRun = partialCacheHit ? missingExtractors : applicableExtractors;\n const extractResult = await runExtractorsForNode({\n extractors: extractorsToRun,\n node,\n body: raw.body,\n frontmatter: raw.frontmatter,\n bodyHash,\n emitter,\n ...(pluginStores ? { pluginStores } : {}),\n });\n for (const link of extractResult.internalLinks) internalLinks.push(link);\n for (const link of extractResult.externalLinks) externalLinks.push(link);\n // Merge per-node enrichment records into the scan-wide buffer.\n // Keys are `${nodePath}\\x00${extractorId}` and unique per node\n // (paths are unique across the scan), so `set()` is collision-free\n // — but we keep the keyed shape in case future code wants to fold\n // across providers walking the same node.\n for (const enr of extractResult.enrichments) {\n enrichmentBuffer.set(`${enr.nodePath}\\x00${enr.extractorId}`, enr);\n }\n\n // Persist a `scan_extractor_runs` row for every applicable\n // extractor (both freshly-run AND cached ones whose contribution\n // we reused). Skipping cached entries here would let the\n // replace-all persist forget them — defeating the whole point of\n // the partial-cache path.\n const ranAt = Date.now();\n for (const ex of applicableExtractors) {\n const qualified = qualifiedExtensionId(ex.pluginId, ex.id);\n extractorRuns.push({\n nodePath: node.path,\n extractorId: qualified,\n bodyHashAtRun: bodyHash,\n ranAt,\n });\n }\n }\n }\n\n return {\n nodes,\n internalLinks,\n externalLinks,\n cachedPaths,\n frontmatterIssues,\n filesWalked,\n enrichments: [...enrichmentBuffer.values()],\n extractorRuns,\n };\n}\n\n/**\n * Spec § A.9 — decide whether a prior link can be reused on a cached\n * node, and how its `sources` array should be reshaped.\n *\n * Three buckets per source short id:\n * - **Cached**: short id maps to a currently-registered qualified id\n * that has a matching `scan_extractor_runs` row for this body hash.\n * The contribution is fresh and survives.\n * - **Missing**: short id maps to a currently-registered qualified id\n * that does NOT have a matching row for this body hash (newly\n * registered, or its prior run targeted a different body). The\n * missing extractor is about to run and will re-emit its own link\n * row, so we drop the prior link entirely to avoid duplicates.\n * - **Obsolete**: short id maps to no currently-registered qualified\n * id at all (the extractor was uninstalled). The contribution is\n * stranded but harmless — we strip the obsolete short id from\n * `sources` and keep the link if at least one cached source remains.\n *\n * Decision rules:\n * - Any missing source → return `null` (drop the link).\n * - All cached, no obsolete → return the link as-is.\n * - Cached + obsolete (no missing) → return a clone with obsolete\n * sources filtered out.\n * - All obsolete (no cached, no missing) → return `null` (no live\n * extractor still claims this link).\n *\n * Source-id mapping caveat: `link.sources` carries the short id the\n * extractor author wrote (e.g. `'slash'`); the cache table keys on the\n * qualified id (`'claude/slash'`). Multiple plugins COULD declare an\n * extractor with the same short id; the map keeps every qualified id per\n * short id so this filter recognises any of them as \"still cached\".\n */\n// eslint-disable-next-line complexity\nfunction reuseCachedLink(\n link: Link,\n shortIdToQualified: Map<string, string[]>,\n cachedQualifiedIds: Set<string>,\n applicableQualifiedIds: Set<string>,\n): Link | null {\n if (!Array.isArray(link.sources) || link.sources.length === 0) return null;\n const cachedSources: string[] = [];\n const obsoleteSources: string[] = [];\n let hasMissing = false;\n for (const source of link.sources) {\n const candidates = shortIdToQualified.get(source);\n if (!candidates || candidates.length === 0) {\n // No registered extractor at all carries this short id → obsolete.\n obsoleteSources.push(source);\n continue;\n }\n if (candidates.some((q) => cachedQualifiedIds.has(q))) {\n cachedSources.push(source);\n continue;\n }\n if (candidates.some((q) => applicableQualifiedIds.has(q))) {\n // Registered for this kind but not cached for this body → the\n // missing extractor will re-emit; dropping the prior link avoids\n // duplicates.\n hasMissing = true;\n continue;\n }\n // Registered but not applicable to this kind → treat as obsolete\n // for this node (cannot be re-emitted here).\n obsoleteSources.push(source);\n }\n if (hasMissing) return null;\n if (cachedSources.length === 0) return null;\n if (obsoleteSources.length === 0) return link;\n // Trim the obsolete short ids from `sources` so the persisted row no\n // longer claims attribution from an extractor the user removed.\n return { ...link, sources: cachedSources };\n}\n\n/**\n * Run every registered rule over the merged graph. Rules see internal\n * links only — broken-ref / trigger-collision / superseded all reason\n * about graph relations, not URLs.\n */\nasync function runRules(\n rules: IRule[],\n nodes: Node[],\n internalLinks: Link[],\n emitter: ProgressEmitterPort,\n hookDispatcher: IHookDispatcher,\n): Promise<Issue[]> {\n const issues: Issue[] = [];\n for (const rule of rules) {\n const emitted = await rule.evaluate({ nodes, links: internalLinks });\n for (const issue of emitted) {\n const validated = validateIssue(rule, issue, emitter);\n if (validated) issues.push(validated);\n }\n // Spec § A.11 — `rule.completed`. Aggregated per Rule, after every\n // issue has been validated. Fan-out scope: one event per Rule per\n // scan. The payload carries the qualified rule id so a hook with\n // `filter: { ruleId: '...' }` can scope to a single rule.\n const ruleId = qualifiedExtensionId(rule.pluginId, rule.id);\n const evt = makeEvent('rule.completed', { ruleId });\n emitter.emit(evt);\n await hookDispatcher.dispatch('rule.completed', evt);\n }\n return issues;\n}\n\n/**\n * The \"originating node\" of a link — the node whose body / frontmatter\n * the extractor was processing when it emitted the link. For most kinds\n * this equals `link.source`, but the frontmatter extractor emits inverted\n * `supersedes` links (from a node's `metadata.supersededBy`) where\n * `target` is the originating node and `source` is the (forward-pointing)\n * supersedor. The forward case (`metadata.supersedes`) keeps\n * `originating === source` like every other extractor.\n *\n * Discriminator: the supersedor path in an inverted edge is rarely a\n * real node (it points \"forward\" to a file that may or may not exist on\n * disk under that exact path); the originating node always exists in\n * the prior snapshot (it's the node whose extraction produced the link).\n * So for `kind === 'supersedes'`: prefer `source` when source is a known\n * prior node, otherwise fall back to `target`. This handles BOTH the\n * forward case (originating === source, which IS a known node) and the\n * inverted case (source not a node → fall through to target, the\n * originating older node).\n *\n * Frontmatter is the only extractor that emits cross-source links today;\n * if a future extractor adds another inversion case, escalate to a\n * persisted `Link.extractedFromPath` field with a schema bump rather\n * than extending this heuristic.\n */\nfunction originatingNodeOf(link: Link, priorNodePaths: Set<string>): string {\n if (link.kind === 'supersedes' && !priorNodePaths.has(link.source)) {\n return link.target;\n }\n return link.source;\n}\n\n/**\n * Step 1 of `detectRenamesAndOrphans` — pair every `deletedPath` with a\n * `newPath` whose body hash matches. Greedy by sorted order; on first\n * hit the deletion is claimed and we move on. Mutates the supplied\n * `claimedDeleted` / `claimedNew` sets in place.\n */\nfunction findHighConfidenceRenames(opts: {\n deletedPaths: string[];\n newPaths: string[];\n priorByPath: Map<string, Node>;\n currentByPath: Map<string, Node>;\n claimedDeleted: Set<string>;\n claimedNew: Set<string>;\n}): RenameOp[] {\n const ops: RenameOp[] = [];\n for (const fromPath of opts.deletedPaths) {\n if (opts.claimedDeleted.has(fromPath)) continue;\n const fromNode = opts.priorByPath.get(fromPath)!;\n for (const toPath of opts.newPaths) {\n if (opts.claimedNew.has(toPath)) continue;\n const toNode = opts.currentByPath.get(toPath)!;\n if (toNode.bodyHash === fromNode.bodyHash) {\n ops.push({ from: fromPath, to: toPath, confidence: 'high' });\n opts.claimedDeleted.add(fromPath);\n opts.claimedNew.add(toPath);\n break;\n }\n }\n }\n return ops;\n}\n\n/**\n * Step 2 of `detectRenamesAndOrphans` — bucket every still-unclaimed\n * `newPath` by the set of still-unclaimed `deletedPath`s that share its\n * `frontmatterHash`. The map drives both the medium-confidence claim\n * pass and the ambiguous-flag pass.\n */\nfunction buildFrontmatterRenameCandidates(opts: {\n deletedPaths: string[];\n newPaths: string[];\n priorByPath: Map<string, Node>;\n currentByPath: Map<string, Node>;\n claimedDeleted: Set<string>;\n claimedNew: Set<string>;\n}): Map<string, string[]> {\n const candidatesByNew = new Map<string, string[]>();\n for (const toPath of opts.newPaths) {\n if (opts.claimedNew.has(toPath)) continue;\n const toNode = opts.currentByPath.get(toPath)!;\n const matches: string[] = [];\n for (const fromPath of opts.deletedPaths) {\n if (opts.claimedDeleted.has(fromPath)) continue;\n const fromNode = opts.priorByPath.get(fromPath)!;\n if (toNode.frontmatterHash === fromNode.frontmatterHash) {\n matches.push(fromPath);\n }\n }\n if (matches.length > 0) candidatesByNew.set(toPath, matches);\n }\n return candidatesByNew;\n}\n\n/**\n * Step 3a of `detectRenamesAndOrphans` — first pass over the candidate\n * map: a `newPath` whose surviving candidate set is a singleton wins\n * the deletion, with `auto-rename-medium`. Greedy by sorted `newPath`\n * order so a deletion claimed by an earlier singleton drops out of\n * later candidate filters. Mutates `claimedDeleted` / `claimedNew` /\n * `issues` in place.\n */\nfunction claimSingletonRenames(opts: {\n newPaths: string[];\n candidatesByNew: Map<string, string[]>;\n claimedDeleted: Set<string>;\n claimedNew: Set<string>;\n issues: Issue[];\n}): RenameOp[] {\n const ops: RenameOp[] = [];\n for (const toPath of opts.newPaths) {\n if (opts.claimedNew.has(toPath)) continue;\n const candidates = opts.candidatesByNew.get(toPath);\n if (!candidates) continue;\n const remaining = candidates.filter((p) => !opts.claimedDeleted.has(p));\n if (remaining.length === 1) {\n const fromPath = remaining[0]!;\n ops.push({ from: fromPath, to: toPath, confidence: 'medium' });\n opts.issues.push({\n ruleId: 'auto-rename-medium',\n severity: 'warn',\n nodeIds: [toPath],\n message: `Auto-rename (medium confidence): ${fromPath} → ${toPath}`,\n data: { from: fromPath, to: toPath, confidence: 'medium' },\n });\n opts.claimedDeleted.add(fromPath);\n opts.claimedNew.add(toPath);\n }\n }\n return ops;\n}\n\n/**\n * Step 3b of `detectRenamesAndOrphans` — any `newPath` left with more\n * than one viable candidate after singletons settled is ambiguous.\n * Emits one `auto-rename-ambiguous` per `newPath`. Candidates are NOT\n * claimed; they fall through to the orphan step so the user can\n * reconcile manually with `sm orphans undo-rename`.\n */\nfunction flagAmbiguousRenames(opts: {\n newPaths: string[];\n candidatesByNew: Map<string, string[]>;\n claimedDeleted: Set<string>;\n claimedNew: Set<string>;\n issues: Issue[];\n}): void {\n for (const toPath of opts.newPaths) {\n if (opts.claimedNew.has(toPath)) continue;\n const candidates = opts.candidatesByNew.get(toPath);\n if (!candidates) continue;\n const remaining = candidates.filter((p) => !opts.claimedDeleted.has(p));\n if (remaining.length > 1) {\n opts.issues.push({\n ruleId: 'auto-rename-ambiguous',\n severity: 'warn',\n nodeIds: [toPath],\n message:\n `Auto-rename ambiguous: ${toPath} matches ${remaining.length} ` +\n `prior frontmatters — pick one with \\`sm orphans undo-rename ` +\n `${toPath} --from <old.path>\\`.`,\n data: { to: toPath, candidates: remaining },\n });\n }\n }\n}\n\n/**\n * Step 4 of `detectRenamesAndOrphans` — every deletion left unclaimed\n * after steps 1-3 yields one `orphan` issue (info severity).\n */\nfunction flagOrphans(opts: {\n deletedPaths: string[];\n claimedDeleted: Set<string>;\n issues: Issue[];\n}): void {\n for (const fromPath of opts.deletedPaths) {\n if (opts.claimedDeleted.has(fromPath)) continue;\n opts.issues.push({\n ruleId: 'orphan',\n severity: 'info',\n nodeIds: [fromPath],\n message: `Orphan history: ${fromPath} was deleted; no rename match found.`,\n data: { path: fromPath },\n });\n }\n}\n\n/**\n * Pure rename / orphan classification per `spec/db-schema.md` §Rename\n * detection. Mutates `issues` in place — caller passes the in-progress\n * issue list; returns the `RenameOp[]` for the persistence layer to\n * apply inside its tx.\n *\n * Pipeline (1-to-1: a `newPath` claimed by one stage cannot be reused\n * by another):\n *\n * 1. **High-confidence**: pair each `deletedPath` with a `newPath`\n * that has the same `bodyHash`. No issue, no prompt.\n * 2. **Medium-confidence (1:1)**: of the remaining deletions, pair\n * each with the *unique* unclaimed `newPath` that shares its\n * `frontmatterHash`. Emits `auto-rename-medium` (severity warn)\n * with `data: { from, to, confidence: 'medium' }`.\n * 3. **Ambiguous (N:1)**: when a single `newPath` has more than one\n * remaining frontmatter-matching candidate, emit ONE\n * `auto-rename-ambiguous` issue per `newPath`, listing all\n * candidates in `data.candidates`. NO migration.\n * 4. **Orphan**: every `deletedPath` left after steps 1-3 yields one\n * `orphan` issue (severity info) with `data: { path: <deletedPath> }`.\n *\n * Determinism: `deletedPaths` and `newPaths` are iterated in lex-asc\n * order so the same input always produces the same matches —\n * required for reproducible tests and conformance fixtures (the spec\n * does not prescribe an order, but stability is the obvious contract).\n */\nexport function detectRenamesAndOrphans(\n prior: ScanResult,\n current: Node[],\n issues: Issue[],\n): RenameOp[] {\n const priorByPath = new Map<string, Node>();\n for (const n of prior.nodes) priorByPath.set(n.path, n);\n const currentByPath = new Map<string, Node>();\n for (const n of current) currentByPath.set(n.path, n);\n\n // Sets / sorted lists so iteration is deterministic.\n const deletedPaths = [...priorByPath.keys()]\n .filter((p) => !currentByPath.has(p))\n .sort();\n const newPaths = [...currentByPath.keys()]\n .filter((p) => !priorByPath.has(p))\n .sort();\n\n const claimedDeleted = new Set<string>();\n const claimedNew = new Set<string>();\n const ops: RenameOp[] = [];\n\n // Step 1 — high confidence (body hash match).\n ops.push(...findHighConfidenceRenames({\n deletedPaths, newPaths, priorByPath, currentByPath, claimedDeleted, claimedNew,\n }));\n\n // Step 2 — bucket every `newPath` by the deletions that share its\n // frontmatterHash, used by both medium-confidence and ambiguous passes.\n const candidatesByNew = buildFrontmatterRenameCandidates({\n deletedPaths, newPaths, priorByPath, currentByPath, claimedDeleted, claimedNew,\n });\n\n // Step 3a — singleton candidates → medium-confidence renames.\n ops.push(...claimSingletonRenames({\n newPaths, candidatesByNew, claimedDeleted, claimedNew, issues,\n }));\n\n // Step 3b — multi-candidate `newPath`s left after singletons settled.\n flagAmbiguousRenames({ newPaths, candidatesByNew, claimedDeleted, claimedNew, issues });\n\n // Step 4 — every unclaimed deletion is an orphan.\n flagOrphans({ deletedPaths, claimedDeleted, issues });\n\n return ops;\n}\n\n/**\n * Any link whose target carries a URL-shaped scheme is external (counted\n * via `externalRefsCount`, dropped from `result.links`). Internal links\n * are filesystem paths — relative or absolute, no scheme.\n *\n * The regex matches RFC 3986's `scheme = ALPHA *( ALPHA / DIGIT / \"+\" /\n * \"-\" / \".\" )` followed by `:`, with the extra constraint of ≥ 2 chars\n * so a Windows-style absolute path (`C:\\foo`) is not misclassified as a\n * URL on the rare cross-platform path that survives normalization.\n *\n * Before this regex the implementation only matched `http://` and\n * `https://`, which silently let `mailto:`, `data:`, `file:///`, `ftp://`\n * etc. pollute the graph as fake-internal links (their lookup against\n * `byPath` always missed, so counts stayed at 0, but the rows survived\n * in `result.links` and the rule pipeline saw them).\n */\nconst EXTERNAL_URL_SCHEME_RE = /^[a-z][a-z0-9+\\-.]+:/i;\n\nfunction isExternalUrlLink(link: Link): boolean {\n return EXTERNAL_URL_SCHEME_RE.test(link.target);\n}\n\nfunction makeEvent(type: string, data: unknown): ProgressEvent {\n return { type, timestamp: new Date().toISOString(), data };\n}\n\n/**\n * Spec § A.11 — Hook lifecycle dispatcher. Indexes the supplied hooks by\n * trigger and fans the matching event out to every subscribed\n * deterministic hook in registration order. Probabilistic hooks are\n * skipped here with a stderr advisory; they will dispatch via the job\n * subsystem once the job subsystem ships.\n *\n * Filter handling: when the hook declares a `filter` map, the dispatcher\n * walks `event.data` for each declared key and short-circuits the\n * invocation when any value disagrees. Top-level fields only in v0.x\n * (deep-path matching is deferred until a real use case justifies the\n * complexity).\n *\n * Error policy: a hook that throws is caught here, logged through a\n * synthetic `extension.error` event with kind `hook-error`, and the\n * scan continues. A buggy hook MUST NOT block the main pipeline —\n * that would invert the design intent (hooks REACT to events, they\n * never steer them).\n */\ninterface IHookDispatcher {\n dispatch(trigger: THookTrigger, event: ProgressEvent): Promise<void>;\n}\n\nfunction makeHookDispatcher(hooks: IHook[], emitter: ProgressEmitterPort): IHookDispatcher {\n if (hooks.length === 0) {\n // Cheap no-op fast path: most scans don't carry any hooks today.\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n return { dispatch: async () => {} };\n }\n\n // Index by trigger so dispatch is O(matching) rather than O(allHooks).\n // Iteration order within a trigger preserves registration order so\n // observers see deterministic fan-out.\n const byTrigger = new Map<THookTrigger, IHook[]>();\n for (const hook of hooks) {\n if (hook.mode === 'probabilistic') {\n // Probabilistic hooks defer to the job subsystem (future job subsystem). Log\n // once per hook at composition time — not per-event — so a noisy\n // scan doesn't flood the logger. The hook still surfaces in\n // `sm plugins list`; it just doesn't fire today.\n const qualifiedId = qualifiedExtensionId(hook.pluginId, hook.id);\n log.warn(\n `Probabilistic hook ${qualifiedId} deferred to job subsystem (future job subsystem). The hook is registered but will not dispatch in-scan.`,\n { hookId: qualifiedId, mode: 'probabilistic' },\n );\n continue;\n }\n for (const trig of hook.triggers) {\n const bucket = byTrigger.get(trig);\n if (bucket) bucket.push(hook);\n else byTrigger.set(trig, [hook]);\n }\n }\n\n return {\n async dispatch(trigger, event) {\n const subs = byTrigger.get(trigger);\n if (!subs || subs.length === 0) return;\n for (const hook of subs) {\n if (!matchesFilter(hook, event)) continue;\n const ctx = buildHookContext(hook, trigger, event);\n try {\n await hook.on(ctx);\n } catch (err) {\n const qualifiedId = qualifiedExtensionId(hook.pluginId, hook.id);\n const message = err instanceof Error ? err.message : String(err);\n emitter.emit(\n makeEvent('extension.error', {\n kind: 'hook-error',\n extensionId: qualifiedId,\n trigger,\n message,\n }),\n );\n }\n }\n },\n };\n}\n\nfunction matchesFilter(hook: IHook, event: ProgressEvent): boolean {\n if (!hook.filter) return true;\n const data = (event.data ?? {}) as Record<string, unknown>;\n for (const [key, expected] of Object.entries(hook.filter)) {\n if (data[key] !== expected) return false;\n }\n return true;\n}\n\n// eslint-disable-next-line complexity\nfunction buildHookContext(\n _hook: IHook,\n trigger: THookTrigger,\n event: ProgressEvent,\n): IHookContext {\n const data = (event.data ?? {}) as Record<string, unknown>;\n const ctx: IHookContext = {\n event: {\n type: trigger,\n timestamp: event.timestamp,\n ...(event.runId !== undefined ? { runId: event.runId } : {}),\n ...(event.jobId !== undefined ? { jobId: event.jobId } : {}),\n data: event.data,\n },\n };\n if (typeof data['extractorId'] === 'string') ctx.extractorId = data['extractorId'];\n if (typeof data['ruleId'] === 'string') ctx.ruleId = data['ruleId'];\n if (typeof data['actionId'] === 'string') ctx.actionId = data['actionId'];\n if (data['node'] && typeof data['node'] === 'object') {\n ctx.node = data['node'] as Node;\n }\n if (data['jobResult'] !== undefined) ctx.jobResult = data['jobResult'];\n return ctx;\n}\n\ninterface IBuildNodeArgs {\n path: string;\n kind: Node['kind'];\n providerId: string;\n frontmatterRaw: string;\n body: string;\n frontmatter: Record<string, unknown>;\n bodyHash: string;\n frontmatterHash: string;\n encoder: Tiktoken | null;\n}\n\nfunction buildNode(args: IBuildNodeArgs): Node {\n const bytesFrontmatter = Buffer.byteLength(args.frontmatterRaw, 'utf8');\n const bytesBody = Buffer.byteLength(args.body, 'utf8');\n const metadata = pickMetadata(args.frontmatter);\n const node: Node = {\n path: args.path,\n kind: args.kind,\n provider: args.providerId,\n bodyHash: args.bodyHash,\n frontmatterHash: args.frontmatterHash,\n bytes: {\n frontmatter: bytesFrontmatter,\n body: bytesBody,\n total: bytesFrontmatter + bytesBody,\n },\n linksOutCount: 0,\n linksInCount: 0,\n externalRefsCount: 0,\n frontmatter: args.frontmatter,\n title: pickString(args.frontmatter['name']),\n description: pickString(args.frontmatter['description']),\n stability: pickStability(metadata?.['stability']),\n version: pickString(metadata?.['version']),\n author: pickString(args.frontmatter['author']),\n };\n if (args.encoder) {\n node.tokens = countTokens(args.encoder, args.frontmatterRaw, args.body);\n }\n return node;\n}\n\nfunction countTokens(encoder: Tiktoken, frontmatterRaw: string, body: string): TripleSplit {\n // Tokenize the raw frontmatter bytes (not the parsed object) so the\n // count stays reproducible from on-disk content.\n const frontmatter = frontmatterRaw.length > 0 ? encoder.encode(frontmatterRaw).length : 0;\n const bodyTokens = body.length > 0 ? encoder.encode(body).length : 0;\n return { frontmatter, body: bodyTokens, total: frontmatter + bodyTokens };\n}\n\nfunction sha256(input: string): string {\n return createHash('sha256').update(input, 'utf8').digest('hex');\n}\n\n/**\n * Canonical-form rationale — canonical YAML form for frontmatter hashing.\n *\n * Goal: two `.md` files whose frontmatter parses to the same logical\n * value MUST produce the same `frontmatter_hash`, even if the raw bytes\n * differ in indentation, key order, quote style, or trailing whitespace.\n * Without this canonicalisation, a YAML formatter pass on the user's\n * editor (Prettier YAML, IDE autoformat, manual indent fix) silently\n * breaks the medium-confidence rename heuristic.\n *\n * Strategy:\n * 1. Take the parsed object the Provider already produced.\n * 2. Re-emit via `yaml.dump` with `sortKeys: true`, `lineWidth: -1`\n * (no auto-wrap), `noRefs: true` (no `*alias` shorthand),\n * `noCompatMode: true` (modern YAML 1.2 output).\n * 3. Hash the result.\n *\n * Fallback: when `parsed` is the empty object `{}` BUT `raw` is\n * non-empty, the Provider's parse failed silently. We fall back to\n * hashing the raw text — a malformed-YAML file should still hash\n * deterministically against itself across rescans, even if the\n * canonical form would be empty.\n */\nfunction canonicalFrontmatter(\n parsed: Record<string, unknown>,\n raw: string,\n): string {\n const hasParsedKeys = Object.keys(parsed).length > 0;\n const hasRawText = raw.length > 0;\n if (!hasParsedKeys && hasRawText) {\n // Parse failed but raw text exists. Hash the raw — preserves\n // identity for malformed-YAML files across scans.\n return raw;\n }\n return yaml.dump(parsed, {\n sortKeys: true,\n lineWidth: -1,\n noRefs: true,\n noCompatMode: true,\n });\n}\n\nfunction pickMetadata(fm: Record<string, unknown>): Record<string, unknown> | null {\n const m = fm['metadata'];\n return m && typeof m === 'object' && !Array.isArray(m) ? (m as Record<string, unknown>) : null;\n}\n\nfunction pickString(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\nfunction pickStability(value: unknown): 'experimental' | 'stable' | 'deprecated' | null {\n if (value === 'experimental' || value === 'stable' || value === 'deprecated') return value;\n return null;\n}\n\nfunction buildExtractorContext(\n extractor: IExtractor,\n node: Node,\n body: string,\n frontmatter: Record<string, unknown>,\n emitLink: (link: Link) => void,\n enrichNode: (partial: Partial<Node>) => void,\n store: IPluginStore | undefined,\n): IExtractorContext {\n const scope = extractor.scope;\n // Spread `store` only when present so the resulting context stays\n // strictly-shaped under `exactOptionalPropertyTypes` — assigning\n // `store: undefined` would publish the property with an `undefined`\n // value, which is observably different from the field being absent\n // (the legacy contract for plugins without declared storage).\n return {\n node,\n body: scope === 'frontmatter' ? '' : body,\n frontmatter: scope === 'body' ? {} : frontmatter,\n emitLink,\n enrichNode,\n ...(store !== undefined ? { store } : {}),\n };\n}\n\nfunction validateLink(extractor: IExtractor, link: Link, emitter: ProgressEmitterPort): Link | null {\n if (!extractor.emitsLinkKinds.includes(link.kind as LinkKind)) {\n // Extractor emitted a kind outside its declared set — drop the link.\n // Surface a `extension.error` diagnostic so plugin authors see WHY a\n // link they expected vanished from the result; silent drops are the\n // worst possible plugin-author UX. The orchestrator is the last line\n // of defence against a misbehaving extractor, but the author needs to\n // know the line fired.\n //\n // `extensionId` carries the qualified form `<pluginId>/<id>` (spec\n // § A.6) so the diagnostic matches what `sm plugins list` and\n // registry lookups use. Older builds emitted just the short id; the\n // qualified form is unambiguous across plugins.\n const qualifiedId = `${extractor.pluginId}/${extractor.id}`;\n emitter.emit(\n makeEvent('extension.error', {\n kind: 'link-kind-not-declared',\n extensionId: qualifiedId,\n linkKind: link.kind,\n declaredKinds: extractor.emitsLinkKinds,\n link: { source: link.source, target: link.target, kind: link.kind },\n message: tx(ORCHESTRATOR_TEXTS.extensionErrorLinkKindNotDeclared, {\n extractorId: qualifiedId,\n linkKind: link.kind,\n declaredKinds: extractor.emitsLinkKinds.join(', '),\n }),\n }),\n );\n return null;\n }\n const confidence: Confidence = link.confidence ?? extractor.defaultConfidence;\n return { ...link, confidence };\n}\n\n/**\n * Validate a node's frontmatter against the per-kind schema declared by\n * the Provider that classified the node. Only called for files that\n * actually declared a fence (caller checks `frontmatterRaw.length > 0`).\n * Returns a single `frontmatter-invalid` issue with the AJV error\n * string, or `null` when the frontmatter is structurally valid. Severity\n * is `warn` by default; `strict` flips it to `error` so the scan exit\n * code rises to 1.\n *\n * Spec 0.8.0: per-kind schemas live with the Provider, not in\n * spec. The orchestrator passes the live `IProviderFrontmatterValidator`\n * (composed from every loaded Provider's `kinds[<kind>].schemaJson`)\n * plus the active Provider so the lookup is `(provider.id, kind) →\n * schema`. A Provider that does not declare an entry for the kind it\n * classified into still gets a `frontmatter-invalid` issue with errors\n * `'no-schema'` so the kernel never silently skips validation.\n */\nfunction validateFrontmatter(\n providerFrontmatter: IProviderFrontmatterValidator,\n provider: IProvider,\n kind: string,\n frontmatter: Record<string, unknown>,\n path: string,\n strict: boolean,\n): Issue | null {\n const result = providerFrontmatter.validate(provider, kind, frontmatter);\n if (result.ok) return null;\n return {\n ruleId: 'frontmatter-invalid',\n severity: strict ? 'error' : 'warn',\n nodeIds: [path],\n message: tx(ORCHESTRATOR_TEXTS.frontmatterInvalid, { path, kind, errors: result.errors }),\n data: { kind, errors: result.errors },\n };\n}\n\n/**\n * Malformed-frontmatter detection — detect cases where the user clearly meant\n * frontmatter but the Provider's regex couldn't recognise the fence.\n * The Provider regex requires `^---\\r?\\n[\\s\\S]*?\\r?\\n---\\r?\\n?` —\n * column-0 open fence, column-0 close fence, CRLF or LF line endings.\n * Three real-world variants that fall through silently and silently\n * lose every metadata field:\n *\n * - `paste-with-indent`: terminal heredoc auto-indented every line,\n * so the open fence is `<spaces>---`. The most common variant\n * .\n * - `byte-order-mark`: a UTF-8 BOM () precedes the fence. Some\n * editors (notably old VS Code on Windows) inject this; the YAML\n * parser handles BOM, but the Provider regex doesn't anchor past it.\n * - `missing-close`: the open fence is on column 0 but the closing\n * fence is missing or indented. Whole \"frontmatter\" parses as body.\n *\n * Each variant emits a `frontmatter-malformed` warn with a `data.hint`\n * tag so downstream tooling can disambiguate. `--strict` promotes to\n * `error` consistent with the strict-fence policy.\n *\n * False-positive guards:\n *\n * - Indented `---` with no YAML-looking line after → likely a nested\n * horizontal rule, not malformed frontmatter.\n * - Column-0 `---` followed by prose (not a YAML key) → likely a\n * legitimate horizontal rule with prose underneath. Tested.\n *\n * The schema-strict validator above only fires when `frontmatterRaw`\n * is non-empty; this fills the previously-silent path where the Provider\n * couldn't even recognise the fence.\n */\nfunction detectMalformedFrontmatter(body: string, path: string, strict: boolean): Issue | null {\n const hint = classifyMalformedFrontmatter(body);\n if (!hint) return null;\n return {\n ruleId: 'frontmatter-malformed',\n severity: strict ? 'error' : 'warn',\n nodeIds: [path],\n message: malformedMessage(hint, path),\n data: { hint },\n };\n}\n\ntype TMalformedHint = 'paste-with-indent' | 'byte-order-mark' | 'missing-close';\n\nfunction classifyMalformedFrontmatter(body: string): TMalformedHint | null {\n // (a) BOM at the very first byte. Check before everything else\n // because a BOM offsets the column-0 anchor of the Provider's regex.\n // Pattern after BOM is the standard column-0 fence + YAML key-value\n // line, so we still require that shape to avoid false positives on\n // any BOM-prefixed prose.\n if (body.startsWith('')) {\n if (/^---\\r?\\n[\\s\\S]*?[A-Za-z0-9_-]+\\s*:/.test(body)) {\n return 'byte-order-mark';\n }\n }\n\n // (b) Indented opening fence followed by a YAML-looking key-value\n // line. The most common variant (terminal heredoc auto-indent).\n if (/^[ \\t]+---\\r?\\n[ \\t]*[A-Za-z0-9_-]+\\s*:/.test(body)) {\n return 'paste-with-indent';\n }\n\n // (c) Column-0 opening fence followed by a YAML-looking key-value\n // line, but no matching closing fence. The Provider regex needs both\n // fences; a missing close means the entire intended frontmatter\n // (plus the body) parses as body.\n //\n // Heuristic: open at column 0, then at least one `key: value` line\n // immediately, then anywhere in the file there is NO column-0 `---`\n // closing the block. If the body had been parsed as frontmatter the\n // Provider would have set `frontmatterRaw` non-empty and we wouldn't\n // be in this branch — so the absence of close means the regex\n // didn't match.\n if (/^---\\r?\\n[ \\t]*[A-Za-z0-9_-]+\\s*:/.test(body)) {\n // Search for any line that is exactly `---` (column 0, no indent).\n // If found, the Provider regex would have matched and this code\n // path is unreachable; absence here means the close is missing\n // or indented.\n const hasCloseFence = /\\r?\\n---(?:\\r?\\n|$)/.test(body);\n if (!hasCloseFence) {\n return 'missing-close';\n }\n }\n\n return null;\n}\n\nfunction malformedMessage(hint: TMalformedHint, path: string): string {\n switch (hint) {\n case 'paste-with-indent':\n return tx(ORCHESTRATOR_TEXTS.frontmatterMalformedPasteWithIndent, { path });\n case 'byte-order-mark':\n return tx(ORCHESTRATOR_TEXTS.frontmatterMalformedByteOrderMark, { path });\n case 'missing-close':\n return tx(ORCHESTRATOR_TEXTS.frontmatterMalformedMissingClose, { path });\n }\n}\n\nfunction validateIssue(rule: IRule, issue: Issue, emitter: ProgressEmitterPort): Issue | null {\n const severity: Severity | undefined = issue.severity;\n if (severity !== 'error' && severity !== 'warn' && severity !== 'info') {\n // Rule emitted an out-of-spec severity (or none at all) — drop the\n // issue. Surface a diagnostic so plugin authors see the issue\n // disappear FOR A REASON, instead of silently never showing up.\n // Qualified id (spec § A.6) keeps `extension.error` consumers\n // unambiguous across plugin namespaces.\n const qualifiedId = `${rule.pluginId}/${rule.id}`;\n emitter.emit(\n makeEvent('extension.error', {\n kind: 'issue-invalid-severity',\n extensionId: qualifiedId,\n severity,\n issue: { ruleId: issue.ruleId || rule.id, message: issue.message, nodeIds: issue.nodeIds },\n message: tx(ORCHESTRATOR_TEXTS.extensionErrorIssueInvalidSeverity, {\n ruleId: qualifiedId,\n severity: JSON.stringify(severity),\n }),\n }),\n );\n return null;\n }\n return { ...issue, ruleId: issue.ruleId || rule.id };\n}\n\nfunction recomputeLinkCounts(nodes: Node[], links: Link[]): void {\n const byPath = new Map<string, Node>();\n for (const node of nodes) {\n // Reset counts so a node reused from prior (which carries its prior\n // counts) gets re-counted from the merged internal-link list.\n node.linksOutCount = 0;\n node.linksInCount = 0;\n byPath.set(node.path, node);\n }\n for (const link of links) {\n const source = byPath.get(link.source);\n if (source) source.linksOutCount += 1;\n const target = byPath.get(link.target);\n if (target) target.linksInCount += 1;\n }\n}\n\nfunction recomputeExternalRefsCount(\n nodes: Node[],\n externalLinks: Link[],\n cachedPaths: Set<string>,\n): void {\n const byPath = new Map<string, Node>();\n for (const node of nodes) {\n // Zero only freshly-built nodes. Cached nodes preserve their prior\n // `externalRefsCount` because external pseudo-links were never\n // persisted, so we cannot re-derive the count from a fresh extractor\n // pass — the count survives untouched in the node row.\n if (!cachedPaths.has(node.path)) node.externalRefsCount = 0;\n byPath.set(node.path, node);\n }\n for (const link of externalLinks) {\n const source = byPath.get(link.source);\n // Cached nodes never appear as the source of a freshly-emitted\n // external pseudo-link (extractors didn't run for them), so this\n // increment only ever lands on a freshly-built node — but the guard\n // is cheap and defensive.\n if (source && !cachedPaths.has(source.path)) source.externalRefsCount += 1;\n }\n}\n\n/**\n * Spec § A.8 — produce the merged read-time view of a Node.\n *\n * Rules / `sm check` / `sm export` consume `node.frontmatter` directly\n * (deterministic CI-safe baseline — author intent, byte-stable). UI / future\n * rules that opt into enrichment context call this helper to merge the\n * author frontmatter with the live enrichment layer.\n *\n * Algorithm:\n *\n * 1. Filter `enrichments` down to rows targeting this node AND not\n * flagged `stale`. Stale rows (probabilistic enrichments whose\n * body changed since their last run) are excluded by default —\n * stale visibility belongs to the UI layer where the marker is\n * shown next to the value.\n * 2. Sort the survivors by `enrichedAt` ASC so iteration order is\n * \"oldest first\". This makes the spread merge below\n * last-write-wins per field — the freshest Extractor's value\n * pisar the older one for any conflicting key.\n * 3. Spread-merge each row's `value` over `node.frontmatter`. The\n * author's keys are the base; enrichment keys overlay them.\n *\n * The returned object is a fresh shallow copy — mutating it does not\n * touch the caller's node. The original `node.frontmatter` reference\n * remains accessible via `node.frontmatter` for callers that want the\n * pristine author baseline.\n *\n * @param node Node to merge against; `node.frontmatter` is the base.\n * @param enrichments Per-(node, extractor) enrichment records — typically\n * loaded via `loadNodeEnrichments(db, node.path)` or\n * pre-filtered to this node by the caller.\n * @param opts.includeStale When true, include rows flagged stale. Defaults\n * to false (the safe, CI-deterministic default).\n * UIs that want to display \"stale (last value: …)\"\n * pass `true` and consult `enrichment.stale`\n * on the source rows.\n */\nexport function mergeNodeWithEnrichments(\n node: Node,\n enrichments: IPersistedEnrichment[],\n opts: { includeStale?: boolean } = {},\n): Record<string, unknown> {\n const includeStale = opts.includeStale === true;\n const applicable = enrichments\n .filter((e) => e.nodePath === node.path)\n .filter((e) => includeStale || !e.stale)\n .sort((a, b) => a.enrichedAt - b.enrichedAt);\n // `assignSafe` strips `__proto__` / `constructor` / `prototype` from\n // every source before copying, so a hostile enrichment value\n // (plugin-authored, persisted as JSON) cannot replace the merged\n // object's prototype via the `__proto__` setter. Prototype stays\n // normal so consumers can `deepStrictEqual` the result against\n // JSON-parse-shaped baselines.\n const base: Record<string, unknown> = {};\n assignSafe(base, node.frontmatter ?? {});\n for (const row of applicable) {\n assignSafe(base, row.value as Record<string, unknown>);\n }\n return base;\n}\n\nconst FORBIDDEN_MERGE_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\nfunction assignSafe(target: Record<string, unknown>, source: Record<string, unknown>): void {\n for (const [k, v] of Object.entries(source)) {\n if (FORBIDDEN_MERGE_KEYS.has(k)) continue;\n target[k] = v;\n }\n}\n\n/**\n * A persisted enrichment row, post-load. Mirrors the DB row shape\n * but with `value` already deserialised from JSON and `stale` /\n * `isProbabilistic` already decoded from `0 | 1`. Surfaced via\n * `loadNodeEnrichments` (driven adapter) and consumed by\n * `mergeNodeWithEnrichments` and the `sm refresh` command.\n */\nexport interface IPersistedEnrichment {\n nodePath: string;\n extractorId: string;\n bodyHashAtEnrichment: string;\n value: Partial<Node>;\n stale: boolean;\n enrichedAt: number;\n isProbabilistic: boolean;\n}\n","/**\n * In-memory `ProgressEmitterPort` adapter. No network, no DB — just a\n * synchronous fan-out to registered listeners. Used by the default scan\n * orchestrator; the WebSocket-backed emitter that streams to\n * the Web UI lands.\n */\n\nimport type {\n ProgressEmitterPort,\n ProgressEvent,\n ProgressListener,\n} from '../ports/progress-emitter.js';\n\nexport class InMemoryProgressEmitter implements ProgressEmitterPort {\n readonly #listeners = new Set<ProgressListener>();\n\n emit(event: ProgressEvent): void {\n for (const listener of this.#listeners) listener(event);\n }\n\n subscribe(listener: ProgressListener): () => void {\n this.#listeners.add(listener);\n return () => {\n this.#listeners.delete(listener);\n };\n }\n}\n","/**\n * Kernel-side strings emitted by `kernel/orchestrator.ts`.\n *\n * Convention: every entry is a flat string with `{{name}}` placeholders\n * (Mustache / Handlebars / Transloco compatible). The `tx` helper at\n * `kernel/util/tx.ts` does the interpolation. Plural / conditional\n * logic lives in the caller — pick the right template, don't branch\n * inside one.\n */\n\nexport const ORCHESTRATOR_TEXTS = {\n frontmatterInvalid:\n 'Frontmatter for {{path}} ({{kind}}) failed schema validation: {{errors}}',\n\n frontmatterMalformedPasteWithIndent:\n 'Frontmatter fence in {{path}} appears indented; YAML frontmatter MUST start with `---` ' +\n 'at column 0. The file was scanned as body-only — the metadata block was silently lost. ' +\n 'Move the `---` lines to the start of the line.',\n\n frontmatterMalformedByteOrderMark:\n 'Frontmatter fence in {{path}} is preceded by a UTF-8 byte-order mark (BOM); the file ' +\n 'was scanned as body-only. Re-save the file as UTF-8 without BOM. The metadata block ' +\n 'was silently lost.',\n\n frontmatterMalformedMissingClose:\n 'Frontmatter in {{path}} opens with `---` but never closes — no matching `---` line ' +\n 'at column 0 was found. The file was scanned as body-only and every metadata field was ' +\n 'silently lost. Add a closing `---` line below the metadata block.',\n\n extensionErrorLinkKindNotDeclared:\n 'Extractor \"{{extractorId}}\" emitted a link of kind \"{{linkKind}}\" outside its ' +\n 'declared `emitsLinkKinds` set [{{declaredKinds}}]. Link dropped.',\n\n extensionErrorIssueInvalidSeverity:\n 'Rule \"{{ruleId}}\" emitted an issue with invalid severity {{severity}} ' +\n \"(allowed: 'error' | 'warn' | 'info'). Issue dropped.\",\n\n runScanRootEmptyArray:\n 'runScan: roots must contain at least one path (spec requires minItems: 1)',\n\n runScanRootMissing: \"runScan: root path '{{root}}' does not exist or is not a directory\",\n} as const;\n","/**\n * File watcher for `sm watch` / `sm scan --watch`.\n *\n * Wraps `chokidar` behind a small `IFsWatcher` interface so:\n *\n * 1. The CLI command is impl-agnostic — swapping chokidar for a\n * different watcher later (Java? Rust port? a future `WatchPort`?)\n * doesn't ripple into the command.\n * 2. Debouncing, batching, and ignore-filter integration live in one\n * place. The CLI just gets `onBatch(paths)` callbacks and decides\n * whether to re-scan.\n *\n * The watcher does NOT call into the orchestrator itself. That decision\n * is deliberate: the CLI owns the scan-and-persist pipeline (`runScan`,\n * `persistScanResult`, optional rebuild of the ignore filter when\n * `.skill-mapignore` itself changes). Pulling that into the watcher\n * would couple the kernel module to `SqliteStorageAdapter`, which the\n * Server wouldn't want. Keep this module side-effect free\n * apart from filesystem subscription.\n *\n * Ignore filter integration: the supplied `IIgnoreFilter` is consulted\n * via chokidar's `ignored` predicate, which receives an absolute path.\n * We re-derive the path RELATIVE to the closest matching root before\n * passing it through `IIgnoreFilter.ignores`. This mirrors what the\n * scan walker does (`extensions/providers/claude/index.ts`) so both code\n * paths agree on what \"ignored\" means.\n */\n\nimport { resolve, relative, sep } from 'node:path';\n\nimport chokidar from 'chokidar';\nimport type { FSWatcher } from 'chokidar';\n\nimport type { IIgnoreFilter } from './ignore.js';\n\n// -----------------------------------------------------------------------------\n// Public types\n// -----------------------------------------------------------------------------\n\nexport type TWatchEventKind = 'add' | 'change' | 'unlink';\n\nexport interface IWatchEvent {\n kind: TWatchEventKind;\n /** Absolute path. */\n absolutePath: string;\n}\n\nexport interface IWatchBatch {\n /** Events that arrived inside the debounce window, in arrival order. */\n events: IWatchEvent[];\n /** Convenience: deduplicated absolute paths across the batch. */\n paths: string[];\n}\n\nexport interface IFsWatcher {\n /** Resolves once chokidar has finished its initial directory scan and is ready to emit. */\n ready: Promise<void>;\n /** Tear down the watcher. Resolves after chokidar releases handles. */\n close: () => Promise<void>;\n}\n\nexport interface ICreateFsWatcherOptions {\n /** Roots to watch. Resolved relative to `cwd` if relative paths are passed. */\n roots: string[];\n /** Working directory used to resolve relative roots and the ignore-filter root. */\n cwd: string;\n /** Debounce window in milliseconds. `0` triggers `onBatch` synchronously per event. */\n debounceMs: number;\n /** Optional ignore filter — same instance the scan walker uses. */\n ignoreFilter?: IIgnoreFilter | undefined;\n /** Called once per debounced batch. Awaited; concurrent batches are serialised. */\n onBatch: (batch: IWatchBatch) => void | Promise<void>;\n /**\n * Called when the underlying watcher surfaces an error. The watcher\n * stays open — callers decide whether to log, keep going, or close.\n */\n onError?: (err: Error) => void;\n}\n\n// -----------------------------------------------------------------------------\n// Public API\n// -----------------------------------------------------------------------------\n\n/**\n * Construct a chokidar-backed watcher. Subscribes immediately; the\n * returned `ready` promise resolves once chokidar's initial directory\n * walk completes, at which point only NEW events fire `onBatch`.\n *\n * The initial directory walk is deliberately silent — we set\n * `ignoreInitial: true`. The CLI runs a one-shot scan before flipping\n * the watcher on, so re-emitting an `add` for every existing file\n * would be redundant churn.\n */\nexport function createChokidarWatcher(opts: ICreateFsWatcherOptions): IFsWatcher {\n const absRoots = opts.roots.map((r) => resolve(opts.cwd, r));\n const ignoreFilter = opts.ignoreFilter;\n\n const ignored = ignoreFilter\n ? (path: string): boolean => {\n const rel = relativePathFromRoots(path, absRoots);\n if (rel === null) return false;\n return ignoreFilter.ignores(rel);\n }\n : undefined;\n\n const watcher: FSWatcher = chokidar.watch(absRoots, {\n ignoreInitial: true,\n persistent: true,\n ...(ignored ? { ignored } : {}),\n });\n\n // Pending state for debouncing.\n let pending: IWatchEvent[] = [];\n let timer: NodeJS.Timeout | null = null;\n let inFlight: Promise<void> | null = null;\n let closed = false;\n\n const fire = async (): Promise<void> => {\n timer = null;\n if (pending.length === 0) return;\n if (inFlight) {\n // A previous batch is still running; let it finish first.\n // The current pending events stay queued and will fire in the\n // next tick once `inFlight` resolves.\n return;\n }\n const events = pending;\n pending = [];\n const seen = new Set<string>();\n const paths: string[] = [];\n for (const ev of events) {\n if (!seen.has(ev.absolutePath)) {\n seen.add(ev.absolutePath);\n paths.push(ev.absolutePath);\n }\n }\n inFlight = Promise.resolve(opts.onBatch({ events, paths }))\n .catch((err: unknown) => {\n if (opts.onError) {\n opts.onError(err instanceof Error ? err : new Error(String(err)));\n }\n })\n .finally(() => {\n inFlight = null;\n // If new events accumulated while we were busy, schedule\n // another fire. We respect the debounce window so a slow\n // `onBatch` doesn't immediately re-trigger.\n if (!closed && pending.length > 0 && timer === null) {\n schedule();\n }\n });\n };\n\n const schedule = (): void => {\n if (closed) return;\n if (opts.debounceMs <= 0) {\n void fire();\n return;\n }\n if (timer !== null) clearTimeout(timer);\n timer = setTimeout(() => {\n void fire();\n }, opts.debounceMs);\n };\n\n const enqueue = (kind: TWatchEventKind, absolutePath: string): void => {\n if (closed) return;\n pending.push({ kind, absolutePath });\n schedule();\n };\n\n watcher.on('add', (p) => enqueue('add', p));\n watcher.on('change', (p) => enqueue('change', p));\n watcher.on('unlink', (p) => enqueue('unlink', p));\n if (opts.onError) {\n watcher.on('error', (err) => {\n opts.onError?.(err instanceof Error ? err : new Error(String(err)));\n });\n }\n\n const ready: Promise<void> = new Promise((resolveReady) => {\n watcher.once('ready', () => resolveReady());\n });\n\n const close = async (): Promise<void> => {\n closed = true;\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n pending = [];\n if (inFlight) {\n try {\n await inFlight;\n } catch {\n // already routed through onError above\n }\n }\n await watcher.close();\n };\n\n return { ready, close };\n}\n\n// -----------------------------------------------------------------------------\n// Helpers\n// -----------------------------------------------------------------------------\n\n/**\n * Pick the matching root for `absolute` and return the path RELATIVE to\n * it, in POSIX form. Returns `null` when the path is outside every\n * supplied root (chokidar shouldn't emit those, but the contract on\n * `IIgnoreFilter.ignores` requires a relative path so we guard\n * defensively).\n */\nfunction relativePathFromRoots(absolute: string, absRoots: string[]): string | null {\n for (const root of absRoots) {\n const rel = relative(root, absolute);\n if (rel === '' || rel === '.') return '';\n if (!rel.startsWith('..') && !rel.startsWith(`..${sep}`)) {\n return rel.split(sep).join('/');\n }\n }\n return null;\n}\n","/**\n * Scan delta — pure comparison of two `ScanResult` snapshots. Drives\n * `sm scan --compare-with <path>` and is the single place the kernel\n * knows how to identify \"the same\" entity across two scans.\n *\n * **Identity contract** (mirrors decisions made at earlier sub-steps):\n *\n * - **Node**: `node.path`. The path is the only field stable across\n * edits — every other Node field is content-derived (hashes, counts,\n * denormalised frontmatter). Two nodes with the same path are the\n * \"same\" node; differences are reported as a `changed` entry with\n * a reason narrowing what diverged.\n *\n * - **Link**: `(source, target, kind, normalizedTrigger ?? '')`. This\n * mirrors the link-conflict rule and `sm show` aggregation —\n * two links with identical endpoints, kind, and (optional) trigger\n * are the same link, even if emitted by different extractors. The\n * `sources[]` union and confidence are NOT part of identity; they\n * are presentation facets that can churn without making the link\n * \"different\" for delta purposes.\n *\n * - **Issue**: `(ruleId, sorted nodeIds, message)`. Mirrors\n * `spec/job-events.md` §issue.* — same key → same issue, even when\n * `data` / `severity` / `linkIndices` shift. A meaningful change in\n * `message` (or a different set of node ids) is a different issue.\n * This is the same key future job events will use; keep it aligned\n * so consumers can reuse logic.\n *\n * No \"changed\" bucket for links / issues — identity already captures\n * everything that matters there. Nodes get a \"changed\" bucket because\n * the path stays stable while the body / frontmatter rewrite, and that\n * change is meaningful (formatters, summarisers, downstream consumers\n * all care about it).\n *\n * Pure: no IO, no DB, no FS. Safe to run in-memory inside `sm scan`\n * without polluting the persisted snapshot.\n */\n\nimport type { Issue, Link, Node, ScanResult } from '../types.js';\n\nexport type TNodeChangeReason = 'body' | 'frontmatter' | 'both';\n\nexport interface INodeChange {\n before: Node;\n after: Node;\n /**\n * Which hash diverged. `'body'` means body rewritten, frontmatter\n * untouched; `'frontmatter'` means metadata rewritten, body\n * untouched; `'both'` means both rewritten in the same edit.\n */\n reason: TNodeChangeReason;\n}\n\nexport interface IScanDelta {\n /** Path the current scan was compared against (echoed for the report header). */\n comparedWith: string;\n nodes: {\n added: Node[];\n removed: Node[];\n changed: INodeChange[];\n };\n links: {\n added: Link[];\n removed: Link[];\n };\n issues: {\n added: Issue[];\n removed: Issue[];\n };\n}\n\nexport function computeScanDelta(\n prior: ScanResult,\n current: ScanResult,\n comparedWith: string,\n): IScanDelta {\n return {\n comparedWith,\n nodes: diffNodes(prior.nodes, current.nodes),\n links: diffLinks(prior.links, current.links),\n issues: diffIssues(prior.issues, current.issues),\n };\n}\n\n/**\n * `true` iff every bucket is empty. Callers use this to decide the\n * exit code (`0` clean, `1` non-empty delta).\n */\nexport function isEmptyDelta(delta: IScanDelta): boolean {\n return (\n delta.nodes.added.length === 0 &&\n delta.nodes.removed.length === 0 &&\n delta.nodes.changed.length === 0 &&\n delta.links.added.length === 0 &&\n delta.links.removed.length === 0 &&\n delta.issues.added.length === 0 &&\n delta.issues.removed.length === 0\n );\n}\n\n// --- node delta ------------------------------------------------------------\n\nfunction diffNodes(\n priorNodes: Node[],\n currentNodes: Node[],\n): IScanDelta['nodes'] {\n const priorByPath = new Map(priorNodes.map((n) => [n.path, n]));\n const currentByPath = new Map(currentNodes.map((n) => [n.path, n]));\n\n const added: Node[] = [];\n const removed: Node[] = [];\n const changed: INodeChange[] = [];\n\n for (const [path, after] of currentByPath) {\n const before = priorByPath.get(path);\n if (!before) {\n added.push(after);\n continue;\n }\n const reason = compareNodeHashes(before, after);\n if (reason !== null) changed.push({ before, after, reason });\n }\n for (const [path, before] of priorByPath) {\n if (!currentByPath.has(path)) removed.push(before);\n }\n\n // Deterministic ordering — by path so two consumers comparing the same\n // pair of scans always see the same delta. Match the existing read-side\n // sort (used by `sm list`, ASCII formatter, etc.).\n added.sort(byPath);\n removed.sort(byPath);\n changed.sort((a, b) => byPath(a.after, b.after));\n\n return { added, removed, changed };\n}\n\nfunction compareNodeHashes(before: Node, after: Node): TNodeChangeReason | null {\n const bodyChanged = before.bodyHash !== after.bodyHash;\n const fmChanged = before.frontmatterHash !== after.frontmatterHash;\n if (bodyChanged && fmChanged) return 'both';\n if (bodyChanged) return 'body';\n if (fmChanged) return 'frontmatter';\n return null;\n}\n\nfunction byPath(a: { path: string }, b: { path: string }): number {\n return a.path.localeCompare(b.path);\n}\n\n// --- link delta ------------------------------------------------------------\n\nfunction diffLinks(\n priorLinks: Link[],\n currentLinks: Link[],\n): IScanDelta['links'] {\n const priorKeys = new Set(priorLinks.map(linkIdentity));\n const currentKeys = new Set(currentLinks.map(linkIdentity));\n\n const added: Link[] = [];\n const removed: Link[] = [];\n\n for (const link of currentLinks) {\n if (!priorKeys.has(linkIdentity(link))) added.push(link);\n }\n for (const link of priorLinks) {\n if (!currentKeys.has(linkIdentity(link))) removed.push(link);\n }\n\n added.sort(byLinkSort);\n removed.sort(byLinkSort);\n\n return { added, removed };\n}\n\nfunction linkIdentity(link: Link): string {\n // NUL separator — collision-free against any path (POSIX paths cannot\n // contain NUL) or trigger string. Same rule used by `sm show`'s\n // aggregation and by the link-conflict rule.\n const trigger = link.trigger?.normalizedTrigger ?? '';\n return `${link.source}\\x00${link.target}\\x00${link.kind}\\x00${trigger}`;\n}\n\nfunction byLinkSort(a: Link, b: Link): number {\n if (a.source !== b.source) return a.source.localeCompare(b.source);\n if (a.target !== b.target) return a.target.localeCompare(b.target);\n return a.kind.localeCompare(b.kind);\n}\n\n// --- issue delta -----------------------------------------------------------\n\nfunction diffIssues(\n priorIssues: Issue[],\n currentIssues: Issue[],\n): IScanDelta['issues'] {\n const priorKeys = new Set(priorIssues.map(issueIdentity));\n const currentKeys = new Set(currentIssues.map(issueIdentity));\n\n const added: Issue[] = [];\n const removed: Issue[] = [];\n\n for (const issue of currentIssues) {\n if (!priorKeys.has(issueIdentity(issue))) added.push(issue);\n }\n for (const issue of priorIssues) {\n if (!currentKeys.has(issueIdentity(issue))) removed.push(issue);\n }\n\n added.sort(byIssueSort);\n removed.sort(byIssueSort);\n\n return { added, removed };\n}\n\nfunction issueIdentity(issue: Issue): string {\n // Matches the spec/job-events.md §issue.* diff key so future job-event\n // consumers can reuse the same identity across the kernel.\n const ids = [...issue.nodeIds].sort().join(',');\n return `${issue.ruleId}\\x00${ids}\\x00${issue.message}`;\n}\n\nfunction byIssueSort(a: Issue, b: Issue): number {\n if (a.ruleId !== b.ruleId) return a.ruleId.localeCompare(b.ruleId);\n return a.message.localeCompare(b.message);\n}\n","/**\n * Kernel entry point. `createKernel()` returns a shell with an empty registry\n * and no bound ports. Driving adapters (CLI, Server, Skill) are expected to\n * wire adapters before invoking use cases.\n */\n\nimport { Registry } from './registry.js';\n\nexport interface Kernel {\n registry: Registry;\n}\n\nexport function createKernel(): Kernel {\n return { registry: new Registry() };\n}\n\n// Pre-1.0 export surface — every name is enumerated explicitly so a\n// rename / addition in any of the underlying modules requires an\n// explicit edit here. The previous `export type *` wildcards from\n// `./types.js` and `./ports/index.js` re-published every internal type\n// implicitly; pre-1.0 that's quiet drift, post-1.0 it would silently\n// turn refactors into major bumps. Group order: registry, domain\n// types, orchestrator, watcher / delta / query, ports, extension\n// kinds.\n\nexport { Registry, EXTENSION_KINDS, DuplicateExtensionError, qualifiedExtensionId } from './registry.js';\nexport type { Extension, ExtensionKind } from './registry.js';\n\n// --- domain types (./types.ts) -----------------------------------------\nexport type {\n // unions\n NodeKind,\n LinkKind,\n Confidence,\n Severity,\n Stability,\n TExecutionMode,\n ExecutionKind,\n ExecutionStatus,\n ExecutionFailureReason,\n ExecutionRunner,\n // value objects\n TripleSplit,\n LinkTrigger,\n LinkLocation,\n // graph\n Node,\n Link,\n IssueFix,\n Issue,\n ScanStats,\n ScanScannedBy,\n ScanResult,\n // history surface\n ExecutionRecord,\n HistoryStatsTotals,\n HistoryStatsTokensPerAction,\n HistoryStatsExecutionsPerPeriod,\n HistoryStatsTopNode,\n HistoryStatsPerActionRate,\n HistoryStatsErrorRates,\n HistoryStats,\n} from './types.js';\n\n// --- orchestrator (./orchestrator.ts) ---------------------------------\nexport {\n runScan,\n runScanWithRenames,\n detectRenamesAndOrphans,\n mergeNodeWithEnrichments,\n runExtractorsForNode,\n} from './orchestrator.js';\nexport type {\n RunScanOptions,\n RenameOp,\n IExtractorRunRecord,\n IEnrichmentRecord,\n IPersistedEnrichment,\n} from './orchestrator.js';\n\n// --- adapters (./adapters/...) -----------------------------------------\nexport { InMemoryProgressEmitter } from './adapters/in-memory-progress.js';\nexport {\n KV_SCHEMA_KEY,\n makeDedicatedStoreWrapper,\n makeKvStoreWrapper,\n makePluginStore,\n} from './adapters/plugin-store.js';\nexport type {\n IDedicatedStorePersist,\n IDedicatedStoreWrapper,\n IKvStorePersist,\n IKvStoreWrapper,\n IPluginStore,\n} from './adapters/plugin-store.js';\n\n// --- scan utilities (./scan/...) ---------------------------------------\nexport { createChokidarWatcher } from './scan/watcher.js';\nexport type {\n IFsWatcher,\n IWatchBatch,\n IWatchEvent,\n ICreateFsWatcherOptions,\n TWatchEventKind,\n} from './scan/watcher.js';\nexport { computeScanDelta, isEmptyDelta } from './scan/delta.js';\nexport type { IScanDelta, INodeChange, TNodeChangeReason } from './scan/delta.js';\nexport { parseExportQuery, applyExportQuery, ExportQueryError } from './scan/query.js';\nexport type { IExportQuery, IExportSubset } from './scan/query.js';\n\n// --- ports (./ports/...) -----------------------------------------------\nexport type { ITransactionalStorage, StoragePort } from './ports/storage.js';\nexport type {\n IIssueRow,\n INodeBundle,\n INodeCounts,\n INodeFilter,\n IPersistOptions,\n} from './types/storage.js';\nexport type { FilesystemPort, IWalkOptions, NodeStat } from './ports/filesystem.js';\nexport type {\n PluginLoaderPort,\n IDiscoveredPlugin,\n ILoadedExtension,\n IPluginManifest,\n IPluginStorageSchema,\n TGranularity,\n TPluginLoadStatus,\n TPluginStorage,\n} from './ports/plugin-loader.js';\nexport type { IRunOptions, IRunResult, RunnerPort } from './ports/runner.js';\nexport type {\n ProgressEmitterPort,\n ProgressEvent,\n ProgressListener,\n} from './ports/progress-emitter.js';\nexport type {\n LoggerPort,\n LogLevel,\n LogMethodLevel,\n LogRecord,\n} from './ports/logger.js';\nexport {\n LOG_LEVELS,\n isLogLevel,\n logLevelRank,\n parseLogLevel,\n} from './ports/logger.js';\nexport { SilentLogger } from './adapters/silent-logger.js';\nexport { log, configureLogger, resetLogger, getActiveLogger } from './util/logger.js';\n\n// --- extension kinds (./extensions/...) --------------------------------\nexport type {\n IProvider,\n IRawNode,\n IExtractor,\n IExtractorContext,\n IExtractorCallbacks,\n IRule,\n IRuleContext,\n IAction,\n IActionPrecondition,\n IFormatter,\n IFormatterContext,\n IHook,\n IHookContext,\n THookTrigger,\n THookFilter,\n IExtensionBase,\n} from './extensions/index.js';\nexport { HOOK_TRIGGERS } from './extensions/index.js';\n","/**\n * CLI strings emitted by `sm init` — `cli/commands/init.ts`.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n *\n * Where the live mode would render plural-vs-singular text (e.g.\n * \"1 entry\" / \"N entries\"), we keep TWO templates and let the caller\n * pick. Conditional logic does not live inside the template.\n */\n\nexport const INIT_TEXTS = {\n alreadyInitialised: 'sm init: {{settingsPath}} already exists. Pass --force to overwrite.\\n',\n\n gitignoreUpdatedSingular: 'Updated {{path}} (added 1 entry)\\n',\n gitignoreUpdatedPlural: 'Updated {{path}} (added {{count}} entries)\\n',\n\n initialised: 'Initialised {{skillMapDir}}\\n',\n\n runningFirstScan: 'Running first scan...\\n',\n\n configLoadFailure: 'sm init: {{message}}\\n',\n\n scanFailed: 'sm init: scan failed: {{message}}\\n',\n\n firstScanSummary: 'First scan: {{nodes}} node(s), {{links}} link(s), {{issues}} issue(s).\\n',\n\n // --- dry-run previews --------------------------------------------------\n dryRunHeader: '(dry-run — no files written, no DB provisioned)\\n',\n dryRunWouldCreateDir: 'would create {{path}}/\\n',\n dryRunWouldWriteFile: 'would write {{path}}\\n',\n dryRunWouldOverwriteFile: 'would overwrite {{path}}\\n',\n dryRunWouldLeaveGitignoreUnchanged:\n 'would leave {{path}} unchanged (entries already present)\\n',\n dryRunWouldUpdateGitignoreSingular:\n 'would update {{path}} (add 1 entry: {{entries}})\\n',\n dryRunWouldUpdateGitignorePlural:\n 'would update {{path}} (add {{count}} entries: {{entries}})\\n',\n dryRunWouldProvisionDb:\n 'would provision DB at {{path}} (apply pending migrations)\\n',\n dryRunWouldRunFirstScan: 'would run first scan (no persistence in dry-run)\\n',\n dryRunWouldSkipFirstScan: 'would skip first scan (--no-scan)\\n',\n} as const;\n","/**\n * CLI strings emitted by `cli/util/cli-progress-emitter.ts`.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n *\n * The progress emitter relays orchestrator `extension.error` events to\n * stderr so plugin authors see why a link / issue is silently dropped.\n */\n\nexport const CLI_PROGRESS_EMITTER_TEXTS = {\n extensionError: 'extension.error: {{message}}\\n',\n\n extensionErrorNoDetail: 'extension reported an error (no detail).',\n} as const;\n","/**\n * `createCliProgressEmitter(stderr)` — `ProgressEmitterPort` for CLI\n * commands that ALSO writes a stderr line every time the orchestrator\n * emits an `extension.error` event.\n *\n * Why: the orchestrator drops links / issues that violate their\n * extension's declared contract (e.g. an extractor emitting a kind it did\n * not declare in `emitsLinkKinds`, a rule emitting an issue with an\n * out-of-spec severity). Without surfacing the drop, a plugin author\n * sees their link / issue silently disappear from the result with no\n * explanation — the worst possible plugin-author UX. This helper wires\n * those events to stderr so authors get a clear pointer at the offending\n * extension.\n *\n * Other event kinds (`scan.started` / `scan.progress` / `scan.completed`)\n * stay in-memory: the CLI already prints a structured summary and we\n * don't want to flood stderr with progress noise.\n */\n\nimport { InMemoryProgressEmitter } from '../../kernel/adapters/in-memory-progress.js';\nimport type { ProgressEmitterPort, ProgressEvent } from '../../kernel/ports/progress-emitter.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { CLI_PROGRESS_EMITTER_TEXTS } from '../i18n/cli-progress-emitter.texts.js';\n\nconst EXTENSION_ERROR = 'extension.error';\n\ninterface IExtensionErrorData {\n kind: string;\n extensionId: string;\n message: string;\n [key: string]: unknown;\n}\n\nexport function createCliProgressEmitter(\n stderr: NodeJS.WritableStream,\n): ProgressEmitterPort {\n const inner = new InMemoryProgressEmitter();\n return {\n emit(event: ProgressEvent): void {\n if (event.type === EXTENSION_ERROR) {\n const data = event.data as IExtensionErrorData | undefined;\n const message = data?.message ?? CLI_PROGRESS_EMITTER_TEXTS.extensionErrorNoDetail;\n stderr.write(tx(CLI_PROGRESS_EMITTER_TEXTS.extensionError, { message }));\n }\n inner.emit(event);\n },\n subscribe: (listener) => inner.subscribe(listener),\n };\n}\n","/**\n * `sm history [-n <path>] [--action <id>] [--status <s,...>] [--since <ISO>] [--until <ISO>] [--json]`\n * `sm history stats [--since <ISO>] [--until <ISO>] [--period day|week|month] [--top N] [--json]`\n *\n * Read-side surfaces over `state_executions`. Step 5.3 ships the lister;\n * Step 5.4 ships the aggregator. Both share the date-window parsing and\n * the elapsed-time helpers.\n *\n * Exit codes (per `spec/cli-contract.md` §Exit codes):\n * 0 ok (including empty result)\n * 2 bad flag (unparseable date, unknown status, invalid --top)\n * 5 DB file missing — run `sm scan` first\n */\n\nimport { Command, Option } from 'clipanion';\n\nimport { loadSchemaValidators } from '../../kernel/adapters/schema-validators.js';\nimport type {\n IListExecutionsFilter,\n THistoryStatsPeriod,\n} from '../../kernel/ports/storage.js';\nimport type {\n ExecutionRecord,\n ExecutionStatus,\n HistoryStats,\n} from '../../kernel/types.js';\nimport { sanitizeForTerminal } from '../../kernel/util/safe-text.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { truncateHead } from '../util/text.js';\nimport { assertDbExists, resolveDbPath } from '../util/db-path.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\nimport { emitDoneStderr, formatElapsed, startElapsed } from '../util/elapsed.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport { parsePositiveIntegerOption } from '../util/option-validators.js';\nimport { withSqlite } from '../util/with-sqlite.js';\nimport { HISTORY_TEXTS } from '../i18n/history.texts.js';\n\nconst STATUSES: readonly ExecutionStatus[] = ['completed', 'failed', 'cancelled'];\nconst PERIODS: readonly THistoryStatsPeriod[] = ['day', 'week', 'month'];\n\n// --- helpers ---------------------------------------------------------------\n\n/**\n * Parse an ISO-8601 string into Unix ms. Rejects unparseable input via\n * stderr + exit 2 — caller propagates the return value.\n *\n * Returns `null` on parse error so callers can short-circuit.\n */\nfunction parseIsoMs(\n input: string,\n flag: string,\n stderr: NodeJS.WritableStream,\n): number | null {\n const ms = Date.parse(input);\n if (!Number.isFinite(ms)) {\n stderr.write(tx(HISTORY_TEXTS.invalidIsoDateTime, { flag, value: input }));\n return null;\n }\n return ms;\n}\n\nfunction parseStatuses(\n input: string,\n stderr: NodeJS.WritableStream,\n): ExecutionStatus[] | null {\n const parts = input.split(',').map((s) => s.trim()).filter((s) => s.length > 0);\n if (parts.length === 0) {\n stderr.write(tx(HISTORY_TEXTS.statusEmpty, { allowed: STATUSES.join(', ') }));\n return null;\n }\n for (const p of parts) {\n if (!STATUSES.includes(p as ExecutionStatus)) {\n stderr.write(tx(HISTORY_TEXTS.statusInvalid, { value: p, allowed: STATUSES.join(', ') }));\n return null;\n }\n }\n return parts as ExecutionStatus[];\n}\n\n// --- sm history ------------------------------------------------------------\n\nexport class HistoryCommand extends Command {\n static override paths = [['history']];\n static override usage = Command.Usage({\n category: 'History',\n description:\n 'Filter execution records. --json emits an array conforming to execution-record.schema.json.',\n details: `\n Reads from state_executions. Filters:\n -n <path> restrict to executions whose nodeIds[] contains <path>\n --action <id> restrict to a specific action extension id\n --status <s,...> restrict to one or more of completed,failed,cancelled\n --since <ISO> lower bound on startedAt (inclusive, ISO-8601)\n --until <ISO> upper bound on startedAt (exclusive, ISO-8601)\n --limit N cap result count\n\n Output is most-recent-first. Run \\`sm scan\\` first to provision the DB.\n `,\n examples: [\n ['Recent executions', '$0 history --limit 10'],\n ['Failures in the last week', '$0 history --status failed --since 2026-04-19T00:00:00Z'],\n ['Machine-readable, scoped to one node', '$0 history -n skills/foo.md --json'],\n ],\n });\n\n global = Option.Boolean('-g,--global', false);\n db = Option.String('--db', { required: false });\n node = Option.String('-n', { required: false });\n action = Option.String('--action', { required: false });\n status = Option.String('--status', { required: false });\n since = Option.String('--since', { required: false });\n until = Option.String('--until', { required: false });\n limit = Option.String('--limit', { required: false });\n json = Option.Boolean('--json', false);\n quiet = Option.Boolean('--quiet', false);\n\n // CLI list verb: many optional filter flags (`--node`, `--action`,\n // `--status`, `--since`, `--until`, `--limit`, `--json`, `--quiet`)\n // each adding a guarded mutation to the filter or render path. Each\n // branch is single-purpose; splitting per flag would distance the\n // validations from the filter they shape.\n // eslint-disable-next-line complexity\n async execute(): Promise<number> {\n const elapsed = startElapsed();\n\n // --- flag validation -------------------------------------------------\n const filter: IListExecutionsFilter = {};\n if (this.node !== undefined) filter.nodePath = this.node;\n if (this.action !== undefined) filter.actionId = this.action;\n if (this.status !== undefined) {\n const parsed = parseStatuses(this.status, this.context.stderr);\n if (parsed === null) return ExitCode.Error;\n filter.statuses = parsed;\n }\n if (this.since !== undefined) {\n const ms = parseIsoMs(this.since, '--since', this.context.stderr);\n if (ms === null) return ExitCode.Error;\n filter.sinceMs = ms;\n }\n if (this.until !== undefined) {\n const ms = parseIsoMs(this.until, '--until', this.context.stderr);\n if (ms === null) return ExitCode.Error;\n filter.untilMs = ms;\n }\n if (this.limit !== undefined) {\n const parsed = parsePositiveIntegerOption(this.limit, '--limit', this.context.stderr);\n if (parsed === null) return ExitCode.Error;\n filter.limit = parsed;\n }\n\n // --- DB --------------------------------------------------------------\n const dbPath = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });\n if (!assertDbExists(dbPath, this.context.stderr)) return ExitCode.NotFound;\n\n return withSqlite({ databasePath: dbPath, autoBackup: false }, async (adapter) => {\n const rows = await adapter.history.list(filter);\n\n if (this.json) {\n // Array output — no top-level elapsedMs per cli-contract.md\n // §Elapsed time. The `done in <…>` stderr line still fires.\n this.context.stdout.write(JSON.stringify(rows.map(toExecutionRecord)) + '\\n');\n } else if (rows.length === 0) {\n this.context.stdout.write(HISTORY_TEXTS.noExecutionsFound);\n } else {\n this.context.stdout.write(renderTable(rows));\n }\n\n emitDoneStderr(this.context.stderr, elapsed, this.quiet);\n return ExitCode.Ok;\n });\n }\n}\n\n// --- sm history stats ------------------------------------------------------\n\nexport class HistoryStatsCommand extends Command {\n static override paths = [['history', 'stats']];\n static override usage = Command.Usage({\n category: 'History',\n description:\n 'Aggregate counts, tokens, periods, top nodes, and error rates over state_executions. --json conforms to history-stats.schema.json.',\n details: `\n Defaults: --period month, --top 10, all-time when --since omitted.\n\n Window: --since is inclusive, --until is exclusive. Both ISO-8601.\n\n The --json output ALWAYS includes the full per-failure-reason key\n set (zero-filled if a reason has no occurrences) so dashboards see\n a predictable shape.\n `,\n examples: [\n ['All-time stats', '$0 history stats'],\n ['Last 30 days, daily buckets', '$0 history stats --since 2026-03-26T00:00:00Z --period day'],\n ['Top 5 nodes, JSON', '$0 history stats --top 5 --json'],\n ],\n });\n\n global = Option.Boolean('-g,--global', false);\n db = Option.String('--db', { required: false });\n since = Option.String('--since', { required: false });\n until = Option.String('--until', { required: false });\n period = Option.String('--period', { required: false });\n top = Option.String('--top', { required: false });\n json = Option.Boolean('--json', false);\n quiet = Option.Boolean('--quiet', false);\n\n // CLI stats verb: range parsing + window flags + period flag + JSON\n // branch + per-period iteration. Each branch is a single-purpose\n // gate; the data work lives in `aggregateHistoryStats`.\n // eslint-disable-next-line complexity\n async execute(): Promise<number> {\n const elapsed = startElapsed();\n\n // --- flag validation -------------------------------------------------\n let sinceMs: number | null = null;\n let untilMs: number = Date.now();\n if (this.since !== undefined) {\n const parsed = parseIsoMs(this.since, '--since', this.context.stderr);\n if (parsed === null) return ExitCode.Error;\n sinceMs = parsed;\n }\n if (this.until !== undefined) {\n const parsed = parseIsoMs(this.until, '--until', this.context.stderr);\n if (parsed === null) return ExitCode.Error;\n untilMs = parsed;\n }\n let period: THistoryStatsPeriod = 'month';\n if (this.period !== undefined) {\n if (!PERIODS.includes(this.period as THistoryStatsPeriod)) {\n this.context.stderr.write(\n tx(HISTORY_TEXTS.periodInvalid, { value: this.period, allowed: PERIODS.join(', ') }),\n );\n return ExitCode.Error;\n }\n period = this.period as THistoryStatsPeriod;\n }\n let topN = 10;\n if (this.top !== undefined) {\n const parsed = parsePositiveIntegerOption(this.top, '--top', this.context.stderr);\n if (parsed === null) return ExitCode.Error;\n topN = parsed;\n }\n\n // --- DB --------------------------------------------------------------\n const dbPath = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });\n if (!assertDbExists(dbPath, this.context.stderr)) return ExitCode.NotFound;\n\n return withSqlite({ databasePath: dbPath, autoBackup: false }, async (adapter) => {\n const aggregated = await adapter.history.aggregateStats(\n { sinceMs, untilMs },\n period,\n topN,\n );\n\n const stats: HistoryStats = {\n schemaVersion: 1,\n range: {\n since: sinceMs === null ? null : new Date(sinceMs).toISOString(),\n until: new Date(untilMs).toISOString(),\n },\n totals: aggregated.totals,\n tokensPerAction: aggregated.tokensPerAction,\n executionsPerPeriod: aggregated.executionsPerPeriod,\n topNodes: aggregated.topNodes,\n errorRates: aggregated.errorRates,\n elapsedMs: elapsed.ms(),\n };\n\n if (this.json) {\n // Self-validate against history-stats.schema.json so a runtime\n // shape regression is caught at the boundary (existing pattern\n // from src/test/self-scan.test.ts).\n const validators = loadSchemaValidators();\n // Step 5.10: re-stamp `elapsedMs` after the validator load\n // (which dominates wall-clock at cold start, ~100ms in cold-cache\n // CLI runs). Captured at construction time, the field understated\n // the user-perceived duration vs `done in <…>` on stderr by the\n // schema-load delta. Doing it after validate but before serialise\n // captures the heavy work; serialisation itself is microseconds.\n stats.elapsedMs = elapsed.ms();\n const result = validators.validate('history-stats', stats);\n if (!result.ok) {\n this.context.stderr.write(\n tx(HISTORY_TEXTS.schemaValidationFailed, { errors: String(result.errors) }),\n );\n return ExitCode.Error;\n }\n this.context.stdout.write(JSON.stringify(stats) + '\\n');\n } else {\n this.context.stdout.write(renderStats(stats));\n }\n\n emitDoneStderr(this.context.stderr, elapsed, this.quiet);\n return ExitCode.Ok;\n });\n }\n}\n\n// --- renderers -------------------------------------------------------------\n\nconst COL_ID = 26;\nconst COL_ACTION = 24;\n// Per-column widths for `renderTable`. Step 5.10: previous version\n// padded every non-ID column to a flat 11 chars, which collapsed the\n// STARTED column (20 chars for an ISO-8601 timestamp) against ACTION.\n// Widths sized so the longest expected content fits with at least 2\n// trailing spaces between columns. Step 5.11 widened STATUS from 12\n// to 30 to fit `cancelled (user-cancelled)` and the longest enum\n// `failed (job-file-missing)` (25 chars + 2 padding rounded up).\n// ID STARTED ACTION STATUS DUR. TOKENS NODES\nconst COL_WIDTHS: number[] = [COL_ID + 2, 22, COL_ACTION + 2, 30, 10, 14, 6];\n\nfunction toExecutionRecord(r: ExecutionRecord): ExecutionRecord {\n // listExecutions already returns the camelCased domain shape; we just\n // emit it as-is. The function name advertises intent for the JSON path.\n return r;\n}\n\n// eslint-disable-next-line complexity\nfunction renderTable(rows: ExecutionRecord[]): string {\n const header = formatRow(\n HISTORY_TEXTS.tableHeaderId,\n HISTORY_TEXTS.tableHeaderStarted,\n HISTORY_TEXTS.tableHeaderAction,\n HISTORY_TEXTS.tableHeaderStatus,\n HISTORY_TEXTS.tableHeaderDuration,\n HISTORY_TEXTS.tableHeaderTokens,\n HISTORY_TEXTS.tableHeaderNodes,\n );\n const sep = '-'.repeat(header.length);\n const lines = [header, sep];\n for (const r of rows) {\n const tokens = `${r.tokensIn ?? 0}/${r.tokensOut ?? 0}`;\n const duration = r.durationMs === null || r.durationMs === undefined\n ? '-'\n : formatElapsed(r.durationMs);\n // Step 5.11 — show `failureReason` inline when present so the human\n // path stops hiding info that's already in --json. Format:\n // completed (no reason ever)\n // failed (timeout) (reason populated)\n // cancelled (user-cancelled) (reason populated)\n // failed (reason missing — defensive)\n // Defence in depth: `id`, `extensionId`, and `failureReason` are\n // sourced from rows persisted by extension code; sanitize before\n // printing so a hostile plugin cannot inject terminal escapes via\n // its own action ids or failure reasons.\n const status =\n r.failureReason !== null && r.failureReason !== undefined && r.failureReason.length > 0\n ? tx(HISTORY_TEXTS.statusWithReason, {\n status: r.status,\n reason: sanitizeForTerminal(r.failureReason),\n })\n : r.status;\n lines.push(\n formatRow(\n truncateHead(sanitizeForTerminal(r.id), COL_ID),\n new Date(r.startedAt).toISOString().slice(0, 19) + 'Z',\n truncateHead(sanitizeForTerminal(r.extensionId), COL_ACTION),\n status,\n duration,\n tokens,\n String((r.nodeIds ?? []).length),\n ),\n );\n }\n return lines.join('\\n') + '\\n';\n}\n\nfunction renderStats(stats: HistoryStats): string {\n // Templates in HISTORY_TEXTS terminate with `\\n`; section breaks are\n // explicit blank `\\n` strings between blocks. The final block does NOT\n // append a trailing blank — matches the pre-i18n shape of `lines.push('')`\n // + `lines.join('\\n')` which produced exactly one trailing newline.\n const out: string[] = [];\n const since = stats.range.since ?? HISTORY_TEXTS.statsAllTimeWindow;\n out.push(tx(HISTORY_TEXTS.statsWindow, { since, until: stats.range.until }));\n out.push('\\n');\n out.push(\n tx(HISTORY_TEXTS.statsTotals, {\n count: stats.totals.executionsCount,\n ok: stats.totals.completedCount,\n failed: stats.totals.failedCount,\n tokensIn: stats.totals.tokensIn,\n tokensOut: stats.totals.tokensOut,\n duration: formatElapsed(stats.totals.durationMsTotal),\n }),\n );\n out.push(\n tx(HISTORY_TEXTS.statsGlobalErrorRate, { rate: (stats.errorRates.global * 100).toFixed(1) }),\n );\n\n // Defence in depth: `actionId`, `actionVersion`, `nodePath`, and the\n // failure-reason key are all sourced from rows persisted by extension\n // code (or — in the case of `failureReason` — a closed enum the\n // kernel writes itself). Sanitize before interpolation so a hostile\n // plugin cannot smuggle terminal escapes via its own action ids /\n // versions; the enum value is sanitized for symmetry with `renderTable`\n // and so future widening of the enum can't regress the gate.\n if (stats.tokensPerAction.length > 0) {\n out.push('\\n');\n out.push(HISTORY_TEXTS.statsTopActionsHeader);\n for (const a of stats.tokensPerAction.slice(0, 5)) {\n out.push(\n tx(HISTORY_TEXTS.statsTopActionsRow, {\n id: sanitizeForTerminal(a.actionId),\n version: sanitizeForTerminal(a.actionVersion),\n runs: a.executionsCount,\n tokensIn: a.tokensIn,\n tokensOut: a.tokensOut,\n }),\n );\n }\n }\n if (stats.topNodes.length > 0) {\n out.push('\\n');\n out.push(HISTORY_TEXTS.statsTopNodesHeader);\n for (const n of stats.topNodes.slice(0, 5)) {\n out.push(\n tx(HISTORY_TEXTS.statsTopNodesRow, {\n path: sanitizeForTerminal(n.nodePath),\n runs: n.executionsCount,\n }),\n );\n }\n }\n const failures = Object.entries(stats.errorRates.perFailureReason).filter(\n ([, v]) => v > 0,\n );\n if (failures.length > 0) {\n out.push('\\n');\n out.push(HISTORY_TEXTS.statsFailuresByReasonHeader);\n for (const [reason, count] of failures) {\n out.push(\n tx(HISTORY_TEXTS.statsFailuresByReasonRow, {\n reason: sanitizeForTerminal(reason),\n count,\n }),\n );\n }\n }\n return out.join('');\n}\n\nfunction formatRow(...cols: string[]): string {\n return cols.map((c, i) => c.padEnd(COL_WIDTHS[i] ?? 10)).join('');\n}\n","/**\n * Strings emitted by the shared CLI option validators\n * (`cli/util/option-validators.ts`).\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const OPTION_VALIDATORS_TEXTS = {\n /**\n * Generic \"expected a positive integer\" line. `{{label}}` is the\n * flag identifier the verb uses (e.g. `--limit`, `--top`). Replaces\n * the three near-duplicates that lived in\n * `LIST_TEXTS.invalidLimit`, `HISTORY_TEXTS.limitNotPositiveInt`,\n * and `HISTORY_TEXTS.topNotPositiveInt`.\n */\n notPositiveInt: '{{label}}: expected a positive integer, got \"{{value}}\".\\n',\n} as const;\n","/**\n * Shared option-value validators for CLI verbs.\n *\n * Two near-duplicate \"must be a positive integer\" checks lived inline\n * in `sm list` (`--limit`) and `sm history` (`--limit`, `--top`), each\n * with its own i18n catalog entry. Consolidating here keeps the\n * acceptance rules in lock-step (a permissive `Number.parseInt` parse\n * accepts `'12abc'` as `12` — every call site needs the same defensive\n * checks against trim + signed input + non-integer).\n *\n * The helpers stay close to the call site (a CLI-style \"validate +\n * write to stderr + return null\" pattern) rather than throwing because\n * Clipanion's `Option.String({ validator: ... })` cascades reject\n * before the verb's `execute` runs, which collides with the existing\n * shape of these flags (they are read inside `execute()` and only\n * validated when the user passed them).\n */\n\nimport { tx } from '../../kernel/util/tx.js';\nimport { OPTION_VALIDATORS_TEXTS } from '../i18n/option-validators.texts.js';\n\n/**\n * Parse `raw` as a strict positive integer (`>= 1`). Writes a\n * scoped-by-`label` error line to `stderr` on rejection and returns\n * `null` so the caller can short-circuit to the appropriate exit\n * code (typically `ExitCode.Error`).\n *\n * Accepts: `'1'`, `'42'`, `' 100 '` (leading/trailing whitespace\n * trimmed for symmetry with the pre-consolidation behaviour).\n *\n * Rejects: `''`, `'0'`, `'-3'`, `'1.5'`, `'12abc'`, `'NaN'`, `'inf'`.\n */\nexport function parsePositiveIntegerOption(\n raw: string,\n label: string,\n stderr: NodeJS.WritableStream,\n): number | null {\n const trimmed = raw.trim();\n const parsed = Number.parseInt(trimmed, 10);\n // Every leg below is one of the failure modes the inline validators\n // were already catching:\n // - `Number.isInteger` rejects NaN / Infinity / floats.\n // - `parsed <= 0` rejects zero and negatives.\n // - `String(parsed) !== trimmed` rejects `'12abc'`-style trailing\n // garbage that `parseInt` happily eats.\n if (!Number.isInteger(parsed) || parsed <= 0 || String(parsed) !== trimmed) {\n stderr.write(\n tx(OPTION_VALIDATORS_TEXTS.notPositiveInt, { label, value: raw }),\n );\n return null;\n }\n return parsed;\n}\n","/**\n * Strings emitted by `cli/commands/history.ts`.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const HISTORY_TEXTS = {\n noExecutionsFound: 'No executions found.\\n',\n\n invalidIsoDateTime: '{{flag}}: expected an ISO-8601 date-time, got \"{{value}}\".\\n',\n statusEmpty: '--status: expected one or more of {{allowed}}.\\n',\n statusInvalid: '--status: invalid value \"{{value}}\". Allowed: {{allowed}}.\\n',\n\n periodInvalid: '--period: invalid value \"{{value}}\". Allowed: {{allowed}}.\\n',\n schemaValidationFailed: 'internal: history-stats output failed schema validation — {{errors}}\\n',\n\n // --- renderStats labels ------------------------------------------------\n statsAllTimeWindow: '(all time)',\n statsWindow: 'Window: {{since}} → {{until}}\\n',\n statsTotals:\n 'Totals: {{count}} executions ({{ok}} ok, {{failed}} failed) — ' +\n 'tokens {{tokensIn}} in / {{tokensOut}} out — duration {{duration}}\\n',\n statsGlobalErrorRate: 'Global error rate: {{rate}}%\\n',\n statsTopActionsHeader: 'Top actions by tokens:\\n',\n statsTopActionsRow: ' {{id}}@{{version}}: {{runs}} runs, {{tokensIn}} in / {{tokensOut}} out\\n',\n statsTopNodesHeader: 'Top nodes:\\n',\n statsTopNodesRow: ' {{path}}: {{runs}} runs\\n',\n statsFailuresByReasonHeader: 'Failures by reason:\\n',\n statsFailuresByReasonRow: ' {{reason}}: {{count}}\\n',\n\n /**\n * Status cell composition: `<status> (<failureReason>)` when a failure\n * reason is present, plain `<status>` otherwise. Caller picks the\n * variant.\n */\n statusWithReason: '{{status}} ({{reason}})',\n\n // --- renderTable labels ------------------------------------------------\n tableHeaderId: 'ID',\n tableHeaderStarted: 'STARTED',\n tableHeaderAction: 'ACTION',\n tableHeaderStatus: 'STATUS',\n tableHeaderDuration: 'DURATION',\n tableHeaderTokens: 'TOKENS',\n tableHeaderNodes: 'NODES',\n} as const;\n","/**\n * `sm job prune` — retention GC for `state_jobs` rows + orphan job-file\n * cleanup. Lands in Step 7.3; the stub it replaces lived in\n * `commands/stubs.ts`.\n *\n * Default behaviour (no flags):\n * - Read `jobs.retention.completed` and `jobs.retention.failed` from\n * the layered config. Each is `seconds | null` — `null` means\n * \"never auto-prune\".\n * - For each terminal status with a non-null retention:\n * cutoffMs = Date.now() - retentionSeconds * 1000\n * Delete `state_jobs` rows in that status with `finished_at <\n * cutoffMs`. Unlink the matching MD files in `.skill-map/jobs/`.\n * - `state_executions` is NOT touched (append-only through v1.0 per\n * `spec/db-schema.md`).\n *\n * `--orphan-files`: ALSO scan `.skill-map/jobs/` for MD files whose\n * absolute path is not referenced by any `state_jobs.file_path`, and\n * delete them. Useful when the DB was wiped manually but the file\n * tree is still around (or vice versa, recovered DB but the runner\n * crashed mid-render and the file never made it into the row). When\n * combined with retention, both passes run; orphan detection happens\n * AFTER retention so files released by pruned rows don't show up as\n * orphans.\n *\n * `--dry-run`: print what would happen and touch nothing — neither DB\n * nor FS. Output shape is identical to the live mode.\n *\n * `--json`: emit a single document on stdout shaped as\n *\n * {\n * dryRun: boolean,\n * retention: {\n * completed: { policySeconds: 2592000 | null, deleted: 4, files: 4 },\n * failed: { policySeconds: null, deleted: 0, files: 0 }\n * },\n * orphanFiles: { scanned: true, deleted: 2 } | { scanned: false }\n * }\n *\n * Exit codes (per `spec/cli-contract.md` §Exit codes):\n * 0 on success (or no-op).\n * 2 config load failure / IO error.\n * 5 DB missing — run `sm init` first.\n */\n\nimport { unlink } from 'node:fs/promises';\n\nimport { Command, Option } from 'clipanion';\n\nimport type { IPruneResult, StoragePort } from '../../kernel/ports/storage.js';\nimport { findOrphanJobFiles } from '../../kernel/jobs/orphan-files.js';\nimport { loadConfig } from '../../kernel/config/loader.js';\nimport {\n assertDbExists,\n defaultProjectDbPath,\n defaultProjectJobsDir,\n} from '../util/db-path.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport { formatErrorMessage } from '../util/error-reporter.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { JOBS_TEXTS } from '../i18n/jobs.texts.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\nimport { withSqlite } from '../util/with-sqlite.js';\n\ninterface IRetentionStatusOutput {\n policySeconds: number | null;\n deleted: number;\n files: number;\n}\n\ninterface IPruneOutput {\n dryRun: boolean;\n retention: {\n completed: IRetentionStatusOutput;\n failed: IRetentionStatusOutput;\n };\n orphanFiles:\n | { scanned: true; deleted: number }\n | { scanned: false };\n}\n\nexport class JobPruneCommand extends Command {\n static override paths = [['job', 'prune']];\n static override usage = Command.Usage({\n category: 'Jobs',\n description: 'Retention GC for completed / failed jobs (per config policy). --orphan-files removes MD files with no DB row.',\n details: `\n Reads jobs.retention.completed and jobs.retention.failed from the\n layered config. For each non-null policy, deletes terminal jobs\n whose finishedAt is older than the cutoff and unlinks their MD\n files in .skill-map/jobs/.\n\n With --orphan-files: ALSO scans .skill-map/jobs/ for MD files not\n referenced by any state_jobs row and deletes them. Both passes\n run; orphans are scanned AFTER retention so freshly-pruned\n files don't double-count.\n\n With --dry-run: counts and reports what would happen without\n touching the DB or the FS.\n\n Exits 0 on success, 5 if the DB is missing (run \\`sm init\\`\n first), 2 on any other operational failure (malformed config,\n IO error).\n `,\n examples: [\n ['Apply retention policy', '$0 job prune'],\n ['Apply retention + clean orphan files', '$0 job prune --orphan-files'],\n ['Preview without touching the DB', '$0 job prune --dry-run --json'],\n ],\n });\n\n orphanFiles = Option.Boolean('--orphan-files', false, {\n description: 'Also remove MD files in .skill-map/jobs/ that have no matching state_jobs row.',\n });\n dryRun = Option.Boolean('-n,--dry-run', false, {\n description: 'Report what would be pruned without touching the DB or filesystem.',\n });\n json = Option.Boolean('--json', false, {\n description: 'Emit a structured prune-result document on stdout.',\n });\n\n async execute(): Promise<number> {\n const ctx = defaultRuntimeContext();\n const dbPath = defaultProjectDbPath(ctx);\n const jobsDir = defaultProjectJobsDir(ctx);\n\n if (!assertDbExists(dbPath, this.context.stderr)) return ExitCode.NotFound;\n\n let cfg;\n try {\n cfg = loadConfig({ scope: 'project', ...defaultRuntimeContext() }).effective;\n } catch (err) {\n const message = formatErrorMessage(err);\n this.context.stderr.write(tx(JOBS_TEXTS.pruneErrorPrefix, { message }));\n return ExitCode.Error;\n }\n\n const completedPolicy = cfg.jobs.retention.completed;\n const failedPolicy = cfg.jobs.retention.failed;\n const now = Date.now();\n\n const out: IPruneOutput = {\n dryRun: this.dryRun,\n retention: {\n completed: { policySeconds: completedPolicy, deleted: 0, files: 0 },\n failed: { policySeconds: failedPolicy, deleted: 0, files: 0 },\n },\n orphanFiles: this.orphanFiles ? { scanned: true, deleted: 0 } : { scanned: false },\n };\n\n try {\n await withSqlite({ databasePath: dbPath, autoBackup: false }, async (adapter) => {\n // --- retention pass ------------------------------------------------\n // Two independent passes (one per terminal status). For dry-run we\n // mirror the same query but stop before DELETE / unlink.\n if (completedPolicy !== null) {\n const cutoff = now - completedPolicy * 1000;\n const result = await this.pruneOrPreview('completed', cutoff, adapter, this.dryRun);\n out.retention.completed.deleted = result.deletedCount;\n out.retention.completed.files = await this.unlinkFiles(result.filePaths, this.dryRun);\n }\n if (failedPolicy !== null) {\n const cutoff = now - failedPolicy * 1000;\n const result = await this.pruneOrPreview('failed', cutoff, adapter, this.dryRun);\n out.retention.failed.deleted = result.deletedCount;\n out.retention.failed.files = await this.unlinkFiles(result.filePaths, this.dryRun);\n }\n\n // --- orphan-files pass ---------------------------------------------\n // Runs AFTER retention so freshly-pruned files are seen by the\n // FS scan only if their `state_jobs` row was already gone\n // (which it isn't, after we just deleted it — they would qualify).\n // We don't double-count: retention unlinked them, the FS scan\n // won't find them anymore.\n if (this.orphanFiles && out.orphanFiles.scanned) {\n const referenced = await adapter.jobs.listReferencedFilePaths();\n const orphans = findOrphanJobFiles(jobsDir, referenced);\n const removed = await this.unlinkFiles(orphans.orphanFilePaths, this.dryRun);\n out.orphanFiles = { scanned: true, deleted: removed };\n }\n });\n } catch (err) {\n const message = formatErrorMessage(err);\n this.context.stderr.write(tx(JOBS_TEXTS.pruneErrorPrefix, { message }));\n return ExitCode.Error;\n }\n\n if (this.json) {\n this.context.stdout.write(JSON.stringify(out) + '\\n');\n return ExitCode.Ok;\n }\n this.printPretty(out);\n return ExitCode.Ok;\n }\n\n private async pruneOrPreview(\n status: 'completed' | 'failed',\n cutoffMs: number,\n adapter: StoragePort,\n dryRun: boolean,\n ): Promise<IPruneResult> {\n return dryRun\n ? adapter.jobs.listTerminalCandidates(status, cutoffMs)\n : adapter.jobs.pruneTerminal(status, cutoffMs);\n }\n\n private async unlinkFiles(paths: string[], dryRun: boolean): Promise<number> {\n if (dryRun) return paths.length;\n let removed = 0;\n for (const p of paths) {\n try {\n await unlink(p);\n removed += 1;\n } catch {\n // Already missing or permission denied — count it as \"not removed\"\n // but keep going. The DB row is already gone (or about to be);\n // a stale file path is a tolerable inconsistency.\n }\n }\n return removed;\n }\n\n private printPretty(out: IPruneOutput): void {\n const tag = out.dryRun ? JOBS_TEXTS.pruneTagDryRun : JOBS_TEXTS.pruneTagApply;\n const c = out.retention.completed;\n const f = out.retention.failed;\n const rowsVerb = out.dryRun ? JOBS_TEXTS.pruneRowsVerbDryRun : JOBS_TEXTS.pruneRowsVerbApply;\n const filesVerb = out.dryRun ? JOBS_TEXTS.pruneFilesVerbDryRun : JOBS_TEXTS.pruneFilesVerbApply;\n this.context.stdout.write(\n `${tag}\\n` +\n tx(JOBS_TEXTS.pruneRetentionRow, {\n label: JOBS_TEXTS.pruneLabelCompleted,\n policy: formatPolicy(c.policySeconds),\n rows: c.deleted,\n rowsVerb,\n files: c.files,\n filesVerb,\n }) +\n tx(JOBS_TEXTS.pruneRetentionRow, {\n label: JOBS_TEXTS.pruneLabelFailed,\n policy: formatPolicy(f.policySeconds),\n rows: f.deleted,\n rowsVerb,\n files: f.files,\n filesVerb,\n }),\n );\n if (out.orphanFiles.scanned) {\n this.context.stdout.write(\n tx(JOBS_TEXTS.pruneOrphanFilesRow, {\n count: out.orphanFiles.deleted,\n verb: out.dryRun ? JOBS_TEXTS.pruneOrphanFilesVerbDryRun : JOBS_TEXTS.pruneOrphanFilesVerbApply,\n }),\n );\n }\n }\n}\n\nfunction formatPolicy(seconds: number | null): string {\n if (seconds === null) return JOBS_TEXTS.pruneRetentionPolicyNever;\n if (seconds % 86400 === 0) return `${seconds / 86400}d`;\n if (seconds % 3600 === 0) return `${seconds / 3600}h`;\n return `${seconds}s`;\n}\n\n","/**\n * Orphan job-file detector. Pairs with\n * `kernel/adapters/sqlite/jobs.ts:selectReferencedJobFilePaths` to find\n * MD files in `<scope>/.skill-map/jobs/` that no `state_jobs.filePath`\n * references — `sm job prune --orphan-files` consumes the result.\n *\n * The split keeps the storage layer FS-free: the SQLite adapter (or any\n * future adapter) returns the *referenced* set; this helper performs\n * the directory walk and computes the set difference. A second adapter\n * (Postgres, in-memory test harness) inherits no `node:fs` dependency.\n *\n * Walk shape: shallow — job files live directly under\n * `.skill-map/jobs/` per `spec/job-lifecycle.md`, no subdirectories.\n * Symlinks are NOT followed. If `jobsDir` does not exist or is not a\n * directory, returns an empty list (a fresh scope with no jobs ever\n * submitted is a valid no-op).\n */\n\nimport { readdirSync, statSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\n\nexport interface IOrphanFilesResult {\n /** Absolute paths of MD files in `jobsDir` that have no matching DB row. */\n orphanFilePaths: string[];\n /** Count of `state_jobs.filePath` values currently referenced (echoed for the JSON output). */\n referencedCount: number;\n}\n\n/**\n * Walk `jobsDir` and return every `*.md` whose absolute path is not in\n * `referencedPaths`. Caller obtains `referencedPaths` from\n * `port.jobs.listReferencedFilePaths()`.\n */\nexport function findOrphanJobFiles(\n jobsDir: string,\n referencedPaths: Set<string>,\n): IOrphanFilesResult {\n let entries: string[];\n try {\n const stat = statSync(jobsDir);\n if (!stat.isDirectory()) {\n return { orphanFilePaths: [], referencedCount: referencedPaths.size };\n }\n entries = readdirSync(jobsDir);\n } catch {\n // ENOENT / permission errors → no orphans we can see.\n return { orphanFilePaths: [], referencedCount: referencedPaths.size };\n }\n\n const orphans: string[] = [];\n for (const name of entries) {\n if (!name.endsWith('.md')) continue;\n const abs = resolve(join(jobsDir, name));\n if (!referencedPaths.has(abs)) orphans.push(abs);\n }\n orphans.sort();\n return { orphanFilePaths: orphans, referencedCount: referencedPaths.size };\n}\n","/**\n * Strings emitted by `cli/commands/jobs.ts`.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const JOBS_TEXTS = {\n pruneErrorPrefix: 'sm job prune: {{message}}\\n',\n\n // --- printPretty (sm job prune human output) ---------------------------\n pruneTagDryRun: 'sm job prune (dry-run)',\n pruneTagApply: 'sm job prune',\n pruneRetentionRow:\n ' {{label}} policy {{policy}}, {{rows}} row(s) {{rowsVerb}}, {{files}} file(s) {{filesVerb}}\\n',\n pruneOrphanFilesRow: ' orphan-files: {{count}} file(s) {{verb}}\\n',\n\n pruneRowsVerbDryRun: 'would be deleted',\n pruneRowsVerbApply: 'deleted',\n pruneFilesVerbDryRun: 'would be unlinked',\n pruneFilesVerbApply: 'unlinked',\n pruneOrphanFilesVerbDryRun: 'would be removed',\n pruneOrphanFilesVerbApply: 'removed',\n\n pruneLabelCompleted: 'completed:',\n pruneLabelFailed: 'failed: ',\n\n pruneRetentionPolicyNever: 'never',\n} as const;\n","/**\n * `sm list [--kind <k>] [--issue] [--sort-by ...] [--limit N] [--json]`\n *\n * Tabular listing of nodes from the persisted snapshot. `--json` emits an\n * array conforming to `spec/schemas/node.schema.json` (one Node per row,\n * no envelope).\n *\n * Exit codes (per `spec/cli-contract.md` §Exit codes):\n * 0 ok (including empty result)\n * 2 bad flag (unknown --sort-by, non-numeric --limit)\n * 5 DB file missing — run `sm scan` first\n */\n\nimport { Command, Option } from 'clipanion';\n\nimport type { StoragePort } from '../../kernel/ports/storage.js';\nimport type { Node } from '../../kernel/types.js';\nimport { sanitizeForTerminal } from '../../kernel/util/safe-text.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { LIST_TEXTS } from '../i18n/list.texts.js';\nimport { assertDbExists, resolveDbPath } from '../util/db-path.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport { parsePositiveIntegerOption } from '../util/option-validators.js';\nimport { truncateTail } from '../util/text.js';\nimport { withSqlite } from '../util/with-sqlite.js';\n\n// Whitelist of sortable columns. NEVER interpolate user input into SQL —\n// `--sort-by` is rejected with exit 2 if it isn't in this map. Each entry\n// pairs the camelCase Kysely column name (CamelCasePlugin rewrites to\n// snake_case for SQL) with a sensible default direction: ASC for textual\n// columns (alphabetic browsing), DESC for numeric columns (largest /\n// most-active first, which is the obvious \"show me what matters\" intent\n// when a user pairs --sort-by bytes_total with --limit N).\nconst SORT_BY: Record<string, { column: string; direction: 'asc' | 'desc' }> = {\n path: { column: 'path', direction: 'asc' },\n kind: { column: 'kind', direction: 'asc' },\n bytes_total: { column: 'bytesTotal', direction: 'desc' },\n links_out_count: { column: 'linksOutCount', direction: 'desc' },\n links_in_count: { column: 'linksInCount', direction: 'desc' },\n external_refs_count: { column: 'externalRefsCount', direction: 'desc' },\n};\n\nconst PATH_COL_WIDTH = 50;\n\nexport class ListCommand extends Command {\n static override paths = [['list']];\n static override usage = Command.Usage({\n category: 'Browse',\n description: 'Tabular listing of nodes. --json emits an array conforming to node.schema.json.',\n details: `\n Reads from the persisted scan snapshot (scan_nodes). Filters:\n --kind <k> restricts to one node kind; --issue keeps only nodes\n that touch at least one current issue.\n\n --sort-by accepts: path, kind, bytes_total, links_out_count,\n links_in_count, external_refs_count. Default: path. --limit N caps\n the result; default is no limit.\n\n Run \\`sm scan\\` first to populate the DB.\n `,\n examples: [\n ['List every node', '$0 list'],\n ['List only agents', '$0 list --kind agent'],\n ['Top 5 by total bytes', '$0 list --sort-by bytes_total --limit 5'],\n ['Only nodes with issues, machine-readable', '$0 list --issue --json'],\n ],\n });\n\n global = Option.Boolean('-g,--global', false);\n db = Option.String('--db', { required: false });\n kind = Option.String('--kind', { required: false });\n issue = Option.Boolean('--issue', false);\n sortBy = Option.String('--sort-by', { required: false });\n limit = Option.String('--limit', { required: false });\n json = Option.Boolean('--json', false);\n\n async execute(): Promise<number> {\n // --- flag validation ---------------------------------------------------\n let sortColumn = 'path';\n let sortDirection: 'asc' | 'desc' = 'asc';\n if (this.sortBy !== undefined) {\n const resolved = SORT_BY[this.sortBy];\n if (!resolved) {\n this.context.stderr.write(\n tx(LIST_TEXTS.invalidSortBy, {\n value: this.sortBy,\n allowed: Object.keys(SORT_BY).join(', '),\n }),\n );\n return ExitCode.Error;\n }\n sortColumn = resolved.column;\n sortDirection = resolved.direction;\n }\n\n let limitValue: number | undefined;\n if (this.limit !== undefined) {\n const parsed = parsePositiveIntegerOption(this.limit, '--limit', this.context.stderr);\n if (parsed === null) return ExitCode.Error;\n limitValue = parsed;\n }\n\n // --- DB ----------------------------------------------------------------\n const dbPath = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });\n if (!assertDbExists(dbPath, this.context.stderr)) return ExitCode.NotFound;\n\n return withSqlite({ databasePath: dbPath, autoBackup: false }, async (adapter) => {\n const filter: { kind?: string; hasIssues?: boolean; sortBy: string; sortDirection: 'asc' | 'desc'; limit?: number } = {\n sortBy: sortColumn,\n sortDirection,\n };\n if (this.kind !== undefined) filter.kind = this.kind;\n if (this.issue) filter.hasIssues = true;\n if (limitValue !== undefined) filter.limit = limitValue;\n const nodes = await adapter.scans.findNodes(filter);\n\n // Per-row issue count (used by both renderers). Keep it cheap by\n // computing once for every node returned rather than joining in SQL.\n const issuesByNode = await this.#countIssuesPerNode(adapter, nodes.map((n) => n.path));\n\n if (this.json) {\n this.context.stdout.write(JSON.stringify(nodes) + '\\n');\n return ExitCode.Ok;\n }\n\n if (nodes.length === 0) {\n this.context.stdout.write(LIST_TEXTS.noNodesFound);\n return ExitCode.Ok;\n }\n\n this.context.stdout.write(renderTable(nodes, issuesByNode));\n return ExitCode.Ok;\n });\n }\n\n async #countIssuesPerNode(\n adapter: StoragePort,\n paths: string[],\n ): Promise<Map<string, number>> {\n const out = new Map<string, number>();\n if (paths.length === 0) return out;\n\n // Pull every issue and tally locally. Dataset is small (issue\n // counts are O(nodes), not O(N*M)) and avoids per-row subqueries.\n const issues = await adapter.issues.listAll();\n const wanted = new Set(paths);\n for (const issue of issues) {\n for (const id of issue.nodeIds) {\n if (!wanted.has(id)) continue;\n out.set(id, (out.get(id) ?? 0) + 1);\n }\n }\n return out;\n }\n}\n\n// --- human renderer -------------------------------------------------------\n\nfunction renderTable(\n nodes: Node[],\n issuesByNode: Map<string, number>,\n): string {\n // Fixed-width columns. The path column truncates with an ellipsis to\n // keep the table readable on standard 100-column terminals; the JSON\n // mode preserves full paths.\n const header = formatRow(\n LIST_TEXTS.tableHeaderPath,\n LIST_TEXTS.tableHeaderKind,\n LIST_TEXTS.tableHeaderOut,\n LIST_TEXTS.tableHeaderIn,\n LIST_TEXTS.tableHeaderExt,\n LIST_TEXTS.tableHeaderIssues,\n LIST_TEXTS.tableHeaderBytes,\n );\n const sep = '-'.repeat(header.length);\n const lines = [header, sep];\n for (const node of nodes) {\n // Defence in depth: `path` and `kind` originate from extension code\n // (Provider classification) and persisted SQLite rows. Sanitize\n // before rendering so a hostile Provider cannot slip ANSI / C0\n // bytes through `sm list`.\n lines.push(\n formatRow(\n truncateTail(sanitizeForTerminal(node.path), PATH_COL_WIDTH),\n sanitizeForTerminal(node.kind),\n String(node.linksOutCount),\n String(node.linksInCount),\n String(node.externalRefsCount),\n String(issuesByNode.get(node.path) ?? 0),\n String(node.bytes.total),\n ),\n );\n }\n return lines.join('\\n') + '\\n';\n}\n\nfunction formatRow(\n path: string,\n kind: string,\n out: string,\n inCount: string,\n ext: string,\n issues: string,\n bytes: string,\n): string {\n return [\n path.padEnd(PATH_COL_WIDTH),\n kind.padEnd(8),\n out.padStart(4),\n inCount.padStart(4),\n ext.padStart(4),\n issues.padStart(7),\n bytes.padStart(8),\n ].join(' ');\n}\n\n","/**\n * CLI strings emitted by `sm list` (`cli/commands/list.ts`).\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const LIST_TEXTS = {\n invalidSortBy:\n '--sort-by: invalid sort field \"{{value}}\". Allowed: {{allowed}}.\\n',\n\n noNodesFound: 'No nodes found.\\n',\n\n // --- renderTable column headers ----------------------------------------\n tableHeaderPath: 'PATH',\n tableHeaderKind: 'KIND',\n tableHeaderOut: 'OUT',\n tableHeaderIn: 'IN',\n tableHeaderExt: 'EXT',\n tableHeaderIssues: 'ISSUES',\n tableHeaderBytes: 'BYTES',\n} as const;\n","/**\n * `sm orphans` — list active orphan / auto-rename issues.\n * `sm orphans reconcile <orphan.path> --to <new.path>`\n * `sm orphans undo-rename <new.path> [--from <old.path>] [--force]`\n *\n * Step 5.6. The verbs operate on FK ownership only — neither edits\n * files on disk. They consume the `orphan` / `auto-rename-medium` /\n * `auto-rename-ambiguous` issues emitted by the rename heuristic\n * (Step 5.5) and are the manual escape hatches for cases the heuristic\n * could not resolve automatically.\n *\n * Exit codes (per `spec/cli-contract.md` §Exit codes):\n * 0 ok\n * 2 bad flag (e.g. `--from` mismatch on auto-rename-medium)\n * 5 not-found (target node missing, no active issue, --from not in candidates)\n */\n\nimport { Command, Option } from 'clipanion';\n\nimport type {\n IIssueRow,\n IMigrateNodeFksReport,\n ITransactionalStorage,\n StoragePort,\n} from '../../kernel/ports/storage.js';\nimport type { Issue } from '../../kernel/types.js';\nimport { sanitizeForTerminal } from '../../kernel/util/safe-text.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { assertDbExists, resolveDbPath } from '../util/db-path.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\nimport { confirm } from '../util/confirm.js';\nimport { emitDoneStderr, startElapsed } from '../util/elapsed.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport { ORPHANS_TEXTS } from '../i18n/orphans.texts.js';\nimport { withSqlite } from '../util/with-sqlite.js';\n\nconst ORPHAN_RULE_IDS = ['orphan', 'auto-rename-medium', 'auto-rename-ambiguous'] as const;\ntype OrphanRuleId = typeof ORPHAN_RULE_IDS[number];\n\n// --- shared helpers -------------------------------------------------------\n\n/**\n * Find every active orphan / auto-rename issue whose runtime shape\n * passes `predicate`. Wraps `port.issues.findActive(...)` with the\n * `ruleId in ORPHAN_RULE_IDS` gate, so callers only spell out the\n * predicate they care about (path equality, candidate match, etc.).\n */\nasync function findActiveOrphanIssues(\n adapter: StoragePort,\n predicate: (issue: Issue) => boolean,\n): Promise<IIssueRow[]> {\n return adapter.issues.findActive(\n (issue) =>\n (ORPHAN_RULE_IDS as readonly string[]).includes(issue.ruleId) &&\n predicate(issue),\n );\n}\n\nfunction isStringArray(v: unknown): v is string[] {\n return Array.isArray(v) && v.every((s) => typeof s === 'string');\n}\n\n// --- sm orphans -----------------------------------------------------------\n\nexport class OrphansCommand extends Command {\n static override paths = [['orphans']];\n static override usage = Command.Usage({\n category: 'Browse',\n description:\n 'List orphan / auto-rename issues from the last scan. --json emits an array conforming to issue.schema.json.',\n details: `\n Surfaces every active issue with ruleId in\n (orphan, auto-rename-medium, auto-rename-ambiguous) so the user\n can decide whether to reconcile (forward) or undo-rename (reverse).\n\n Filter with --kind: orphan | medium | ambiguous.\n `,\n examples: [\n ['List every orphan / auto-rename issue', '$0 orphans'],\n ['Just the ambiguous ones, JSON', '$0 orphans --kind ambiguous --json'],\n ],\n });\n\n global = Option.Boolean('-g,--global', false);\n db = Option.String('--db', { required: false });\n kind = Option.String('--kind', { required: false });\n json = Option.Boolean('--json', false);\n quiet = Option.Boolean('--quiet', false);\n\n async execute(): Promise<number> {\n const elapsed = startElapsed();\n\n let ruleFilter: OrphanRuleId | null = null;\n if (this.kind !== undefined) {\n const map: Record<string, OrphanRuleId> = {\n orphan: 'orphan',\n medium: 'auto-rename-medium',\n ambiguous: 'auto-rename-ambiguous',\n };\n const resolved = map[this.kind];\n if (!resolved) {\n this.context.stderr.write(tx(ORPHANS_TEXTS.invalidKind, { kind: this.kind }));\n return ExitCode.Error;\n }\n ruleFilter = resolved;\n }\n\n const dbPath = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });\n if (!assertDbExists(dbPath, this.context.stderr)) return ExitCode.NotFound;\n\n return withSqlite({ databasePath: dbPath, autoBackup: false }, async (adapter) => {\n const found = await findActiveOrphanIssues(adapter, (issue) => {\n if (ruleFilter !== null) return issue.ruleId === ruleFilter;\n return true;\n });\n\n if (this.json) {\n this.context.stdout.write(\n JSON.stringify(found.map((f) => f.issue)) + '\\n',\n );\n } else if (found.length === 0) {\n this.context.stdout.write(ORPHANS_TEXTS.noIssues);\n } else {\n this.context.stdout.write(renderOrphans(found.map((f) => f.issue)));\n }\n\n emitDoneStderr(this.context.stderr, elapsed, this.quiet);\n return ExitCode.Ok;\n });\n }\n}\n\n// --- sm orphans reconcile -------------------------------------------------\n\nexport class OrphansReconcileCommand extends Command {\n static override paths = [['orphans', 'reconcile']];\n static override usage = Command.Usage({\n category: 'Browse',\n description:\n 'Migrate state_* FKs from an orphan path to a live node, resolving the orphan issue.',\n details: `\n Forward direction: when the rename heuristic could not find a match\n (e.g. semantic-only rename, body rewrite), use this verb to attach\n the orphan's history to a live node.\n\n Validates that <new.path> exists in scan_nodes (exit 5 otherwise)\n and that an active orphan issue exists for <orphan.path> (exit 5\n otherwise). Migration is atomic via a single transaction.\n `,\n examples: [\n ['Reattach orphan history', '$0 orphans reconcile skills/old.md --to skills/new.md'],\n ],\n });\n\n global = Option.Boolean('-g,--global', false);\n db = Option.String('--db', { required: false });\n orphanPath = Option.String({ required: true });\n to = Option.String('--to', { required: true });\n dryRun = Option.Boolean('-n,--dry-run', false);\n quiet = Option.Boolean('--quiet', false);\n\n async execute(): Promise<number> {\n const elapsed = startElapsed();\n\n const dbPath = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });\n if (!assertDbExists(dbPath, this.context.stderr)) return ExitCode.NotFound;\n\n return withSqlite({ databasePath: dbPath, autoBackup: false }, async (adapter) => {\n // 1. Validate <new.path> is a live node.\n const target = await adapter.scans.findNode(this.to);\n if (!target) {\n this.context.stderr.write(\n tx(ORPHANS_TEXTS.reconcileTargetNotFound, { path: this.to }),\n );\n return ExitCode.NotFound;\n }\n\n // 2. Find the active orphan issue for <orphan.path>.\n const candidates = await findActiveOrphanIssues(adapter, (issue) => {\n if (issue.ruleId !== 'orphan') return false;\n const dataPath = issue.data ? (issue.data['path'] as unknown) : undefined;\n return typeof dataPath === 'string' && dataPath === this.orphanPath;\n });\n if (candidates.length === 0) {\n this.context.stderr.write(\n tx(ORPHANS_TEXTS.reconcileNoActiveIssue, { path: this.orphanPath }),\n );\n return ExitCode.NotFound;\n }\n\n // 3. Migrate FKs and resolve every matching issue inside one tx.\n // `--dry-run` runs the same migration inside the transaction so the\n // same code path produces the report, then forces a rollback via\n // a sentinel throw — the spec § Dry-run contract is \"no observable\n // side effects\" and rolling back the transaction guarantees that\n // even if SQLite touched any pages they are reverted before commit.\n const orphanPath = this.orphanPath;\n const toPath = this.to;\n // Strict-equality check: Clipanion's `Option.Boolean` evaluator\n // returns a placeholder symbol BEFORE the parser runs (the field\n // type stays `boolean` from TS's view but the runtime value is\n // not `true` / `false`). Direct `if (this.dryRun)` would treat\n // that placeholder as truthy and silently flip every test that\n // constructs the command without going through Clipanion.\n const dryRun = this.dryRun === true;\n const summary = await runWithOptionalRollback(\n adapter,\n async (tx) => {\n const report = await tx.history.migrateNodeFks(orphanPath, toPath);\n if (!dryRun) {\n for (const cand of candidates) {\n await tx.issues.deleteById(cand.id);\n }\n }\n return report;\n },\n dryRun,\n );\n\n const totalRows = summaryTotal(summary);\n const summaryVars = {\n from: this.orphanPath,\n to: this.to,\n rows: totalRows,\n jobs: summary.jobs,\n execs: summary.executions,\n summaries: summary.summaries,\n enrichments: summary.enrichments,\n kv: summary.pluginKvs,\n };\n this.context.stdout.write(\n tx(\n dryRun ? ORPHANS_TEXTS.reconcileWouldMigrate : ORPHANS_TEXTS.reconcileSummary,\n summaryVars,\n ),\n );\n if (summary.collisions.length > 0) {\n this.context.stderr.write(\n tx(\n dryRun\n ? ORPHANS_TEXTS.reconcileCollisionsNoteDryRun\n : ORPHANS_TEXTS.reconcileCollisionsNote,\n { count: summary.collisions.length },\n ),\n );\n }\n emitDoneStderr(this.context.stderr, elapsed, this.quiet);\n return ExitCode.Ok;\n });\n }\n}\n\n// --- sm orphans undo-rename ----------------------------------------------\n\nexport class OrphansUndoRenameCommand extends Command {\n static override paths = [['orphans', 'undo-rename']];\n static override usage = Command.Usage({\n category: 'Browse',\n description:\n 'Reverse a medium- or ambiguous-confidence auto-rename. Migrates state_* FKs back, emits a new orphan on the prior path.',\n details: `\n Use when the rename heuristic auto-migrated history to a node that\n turned out to be unrelated.\n\n For an active auto-rename-medium issue on <new.path>, the prior\n path is read from issue.data.from — omit --from. For an active\n auto-rename-ambiguous issue, --from <old.path> is REQUIRED to\n pick a candidate from data.candidates.\n\n Destructive (changes FK ownership). Prompts for confirmation\n unless --force.\n `,\n examples: [\n ['Undo a medium-confidence auto-rename', '$0 orphans undo-rename skills/new.md'],\n ['Undo an ambiguous, picking a candidate', '$0 orphans undo-rename skills/new.md --from skills/old-a.md'],\n ],\n });\n\n global = Option.Boolean('-g,--global', false);\n db = Option.String('--db', { required: false });\n newPath = Option.String({ required: true });\n from = Option.String('--from', { required: false });\n force = Option.Boolean('--force', false);\n dryRun = Option.Boolean('-n,--dry-run', false);\n quiet = Option.Boolean('--quiet', false);\n\n async execute(): Promise<number> {\n const elapsed = startElapsed();\n\n const dbPath = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });\n if (!assertDbExists(dbPath, this.context.stderr)) return ExitCode.NotFound;\n\n return withSqlite({ databasePath: dbPath, autoBackup: false }, async (adapter) => {\n // Find the active auto-rename-medium / -ambiguous issue on <new.path>.\n const candidates = await findActiveOrphanIssues(adapter, (issue) => {\n if (issue.ruleId !== 'auto-rename-medium' && issue.ruleId !== 'auto-rename-ambiguous') {\n return false;\n }\n return issue.nodeIds.includes(this.newPath);\n });\n\n if (candidates.length === 0) {\n this.context.stderr.write(\n tx(ORPHANS_TEXTS.undoNoActiveIssue, { path: this.newPath }),\n );\n return ExitCode.NotFound;\n }\n if (candidates.length > 1) {\n this.context.stderr.write(\n tx(ORPHANS_TEXTS.undoMultipleActive, {\n count: candidates.length,\n path: this.newPath,\n }),\n );\n return ExitCode.Error;\n }\n\n const candidate = candidates[0]!;\n const issue = candidate.issue;\n\n const resolved = this.#resolveFrom(issue);\n if (!resolved.ok) return resolved.exitCode;\n const resolvedFrom = resolved.from;\n\n // Destructive — confirm unless --force OR --dry-run. Per spec\n // § Dry-run: \"Dry-run MUST NOT depend on --yes / --force ...\n // (no confirmation needed when nothing is being destroyed)\".\n // Strict equality: see the placeholder note further down.\n //\n // `resolvedFrom` originates from either `--from` (CLI flag) or\n // `issue.data['from']` (DB-sourced, plugin-authored). Sanitize\n // for the terminal-output paths even though the CLI-flag branch\n // is already trusted — defence-in-depth keeps the gate uniform.\n const safeFrom = sanitizeForTerminal(resolvedFrom);\n if (this.force !== true && this.dryRun !== true) {\n const ok = await confirm(\n tx(ORPHANS_TEXTS.undoConfirmPrompt, {\n newPath: this.newPath,\n from: safeFrom,\n }),\n { stdin: this.context.stdin, stderr: this.context.stderr },\n );\n if (!ok) {\n this.context.stderr.write(ORPHANS_TEXTS.aborted);\n return ExitCode.Error;\n }\n }\n\n const newPath = this.newPath;\n const toPath = resolvedFrom;\n // Strict-equality check: Clipanion's `Option.Boolean` evaluator\n // returns a placeholder symbol BEFORE the parser runs (the field\n // type stays `boolean` from TS's view but the runtime value is\n // not `true` / `false`). Direct `if (this.dryRun)` would treat\n // that placeholder as truthy and silently flip every test that\n // constructs the command without going through Clipanion.\n const dryRun = this.dryRun === true;\n const summary = await runWithOptionalRollback(\n adapter,\n async (txStore) => {\n const report = await txStore.history.migrateNodeFks(newPath, toPath);\n if (!dryRun) {\n await txStore.issues.deleteById(candidate.id);\n // Per spec: \"the previous path becomes an `orphan`\". The\n // new path (which the file in FS still has) inherits no\n // rows, so the orphan path is the OLD path the FKs just\n // landed on.\n await txStore.issues.insert({\n ruleId: 'orphan',\n severity: 'info',\n nodeIds: [toPath],\n message: tx(ORPHANS_TEXTS.undoRenameOrphanMessage, { toPath, newPath }).trimEnd(),\n data: { path: toPath },\n });\n }\n return report;\n },\n dryRun,\n );\n\n this.context.stdout.write(\n tx(dryRun ? ORPHANS_TEXTS.undoWouldMigrate : ORPHANS_TEXTS.undoSummary, {\n newPath: this.newPath,\n from: safeFrom,\n rows: summaryTotal(summary),\n }),\n );\n emitDoneStderr(this.context.stderr, elapsed, this.quiet);\n return ExitCode.Ok;\n });\n }\n\n /**\n * Resolve the prior path the FK migration should target. Pulled out of\n * `execute()` so the destructive verb's main control flow stays\n * linear (validate → resolve → confirm → migrate). Dispatches to a\n * per-ruleId helper to keep cyclomatic complexity below the lint\n * threshold; the dispatcher itself is the discriminated-union pattern\n * AGENTS.md whitelists, but here we keep it simple.\n */\n #resolveFrom(\n issue: Issue,\n ): { ok: true; from: string } | { ok: false; exitCode: number } {\n if (issue.ruleId === 'auto-rename-medium') return this.#resolveFromMedium(issue);\n return this.#resolveFromAmbiguous(issue);\n }\n\n #resolveFromMedium(\n issue: Issue,\n ): { ok: true; from: string } | { ok: false; exitCode: number } {\n const dataFrom = issue.data ? (issue.data['from'] as unknown) : undefined;\n if (typeof dataFrom !== 'string') {\n this.context.stderr.write(ORPHANS_TEXTS.undoMediumMissingFrom);\n return { ok: false, exitCode: ExitCode.Error };\n }\n if (this.from !== undefined && this.from !== dataFrom) {\n // `dataFrom` is plugin-authored (persisted in `scan_issues.data_json`\n // by the rename heuristic). Sanitize before interpolating into the\n // stderr template — the path itself is part of the user-visible\n // diagnostic, but a hostile plugin could plant terminal escapes\n // in `issue.data.from` to repaint the user's screen.\n this.context.stderr.write(\n tx(ORPHANS_TEXTS.undoMediumFromMismatch, {\n from: this.from,\n dataFrom: sanitizeForTerminal(dataFrom),\n }),\n );\n return { ok: false, exitCode: ExitCode.Error };\n }\n return { ok: true, from: dataFrom };\n }\n\n #resolveFromAmbiguous(\n issue: Issue,\n ): { ok: true; from: string } | { ok: false; exitCode: number } {\n if (this.from === undefined) {\n this.context.stderr.write(ORPHANS_TEXTS.undoAmbiguousRequiresFrom);\n return { ok: false, exitCode: ExitCode.NotFound };\n }\n const dataCandidates = issue.data ? issue.data['candidates'] : undefined;\n if (!isStringArray(dataCandidates) || !dataCandidates.includes(this.from)) {\n this.context.stderr.write(\n tx(ORPHANS_TEXTS.undoAmbiguousNotInCandidates, { from: this.from }),\n );\n return { ok: false, exitCode: ExitCode.NotFound };\n }\n return { ok: true, from: this.from };\n }\n}\n\n// --- shared dry-run helper ------------------------------------------------\n\n/**\n * Sentinel symbol used to force a Kysely transaction rollback in\n * `--dry-run` mode without conflating with real errors. The caller\n * captures it after the transaction promise rejects and rethrows\n * anything else.\n */\nconst DRY_RUN_ROLLBACK = Symbol('orphans:dry-run-rollback');\n\n/**\n * Run `body` inside a `port.transaction(...)` callback. When `dryRun`\n * is true, the helper throws the rollback sentinel after capturing\n * `body`'s return value so the adapter rolls back the transaction —\n * the spec § Dry-run \"no observable side effects\" guarantee. The\n * return value is propagated either way.\n *\n * Reasoning: `migrateNodeFks` is a complex multi-table mutation; we\n * want the dry-run preview to come from the SAME code path as the\n * live mode (no parallel \"count\" implementation that could drift).\n * A throw-to-rollback gives that for free.\n */\nasync function runWithOptionalRollback(\n adapter: StoragePort,\n body: (tx: ITransactionalStorage) => Promise<IMigrateNodeFksReport>,\n dryRun: boolean,\n): Promise<IMigrateNodeFksReport> {\n let captured: IMigrateNodeFksReport | undefined;\n try {\n return await adapter.transaction(async (tx) => {\n const report = await body(tx);\n if (dryRun) {\n captured = report;\n throw DRY_RUN_ROLLBACK;\n }\n return report;\n });\n } catch (err) {\n if (err === DRY_RUN_ROLLBACK && captured !== undefined) return captured;\n throw err;\n }\n}\n\nfunction summaryTotal(s: IMigrateNodeFksReport): number {\n return s.jobs + s.executions + s.summaries + s.enrichments + s.pluginKvs;\n}\n\n// --- renderers ------------------------------------------------------------\n\nfunction renderOrphans(issues: Issue[]): string {\n const lines: string[] = [];\n lines.push(ORPHANS_TEXTS.activeIssuesHeader);\n for (const issue of issues) {\n // Defence in depth: `ruleId`, the subject path (a node id), and the\n // issue message all originate from plugin-authored strings persisted\n // in the DB. Sanitize before rendering.\n const rawSubject = issue.nodeIds[0];\n const subject = rawSubject !== undefined\n ? sanitizeForTerminal(rawSubject)\n : ORPHANS_TEXTS.noNodePlaceholder;\n lines.push(\n tx(ORPHANS_TEXTS.activeIssueRow, {\n ruleId: sanitizeForTerminal(issue.ruleId),\n subject,\n message: sanitizeForTerminal(issue.message),\n }),\n );\n }\n lines.push('');\n return lines.join('\\n');\n}\n\nexport const ORPHANS_COMMANDS = [\n OrphansCommand,\n OrphansReconcileCommand,\n OrphansUndoRenameCommand,\n];\n","/**\n * Strings emitted by `cli/commands/orphans.ts`.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const ORPHANS_TEXTS = {\n noIssues: 'No orphan / auto-rename issues.\\n',\n aborted: 'Aborted.\\n',\n // --- reconcile ---------------------------------------------------------\n reconcileTargetNotFound:\n 'sm orphans reconcile: target node \"{{path}}\" not found in scan_nodes.\\n',\n reconcileNoActiveIssue:\n 'sm orphans reconcile: no active orphan issue found for \"{{path}}\".\\n',\n reconcileSummary:\n 'Reconciled {{from}} → {{to}}. Migrated {{rows}} state rows ' +\n '(jobs:{{jobs}}, execs:{{execs}}, summaries:{{summaries}}, ' +\n 'enrichments:{{enrichments}}, kv:{{kv}}).\\n',\n reconcileWouldMigrate:\n '(dry-run) Would reconcile {{from}} → {{to}}. Would migrate {{rows}} state rows ' +\n '(jobs:{{jobs}}, execs:{{execs}}, summaries:{{summaries}}, ' +\n 'enrichments:{{enrichments}}, kv:{{kv}}).\\n',\n reconcileCollisionsNote:\n 'Note: {{count}} composite-PK collision(s); destination rows preserved ' +\n '(see spec/db-schema.md §Rename detection).\\n',\n reconcileCollisionsNoteDryRun:\n '(dry-run) Note: {{count}} composite-PK collision(s) would be skipped; ' +\n 'destination rows would be preserved.\\n',\n // --- undo-rename -------------------------------------------------------\n undoNoActiveIssue:\n 'sm orphans undo-rename: no active auto-rename issue targets \"{{path}}\".\\n',\n undoMultipleActive:\n 'sm orphans undo-rename: {{count}} active auto-rename issues target \"{{path}}\"; ' +\n 'the rename heuristic should have produced at most one. Run `sm scan` and retry.\\n',\n undoMediumMissingFrom:\n 'sm orphans undo-rename: auto-rename-medium issue is missing data.from; ' +\n 'cannot revert without --from.\\n',\n undoMediumFromMismatch:\n 'sm orphans undo-rename: --from \"{{from}}\" does not match auto-rename-medium ' +\n 'data.from \"{{dataFrom}}\".\\n',\n undoAmbiguousRequiresFrom:\n 'sm orphans undo-rename: --from <old.path> is REQUIRED for auto-rename-ambiguous ' +\n '(pick one of data.candidates).\\n',\n undoAmbiguousNotInCandidates:\n 'sm orphans undo-rename: --from \"{{from}}\" not in auto-rename-ambiguous candidates.\\n',\n undoConfirmPrompt:\n 'Undo auto-rename: migrate state_* FKs from {{newPath}} back to {{from}}?',\n undoSummary:\n 'Reverted {{newPath}} → {{from}}. Migrated {{rows}} state rows; ' +\n 'new orphan issue emitted on {{from}}.\\n',\n undoWouldMigrate:\n '(dry-run) Would revert {{newPath}} → {{from}}. Would migrate {{rows}} state rows; ' +\n 'would emit a new orphan issue on {{from}}.\\n',\n /**\n * Message persisted into `scan_issues.message` for the orphan issue\n * emitted after `sm orphans undo-rename`. The string lands in DB rows\n * and travels through `--json`, `sm check`, and downstream consumers,\n * so localising it requires a kernel-side template (not just a CLI\n * catalog) — kept here for now so the wording lives in one greppable\n * place even if the layering is imperfect.\n */\n undoRenameOrphanMessage:\n 'Orphan history: {{toPath}} (was reverted from auto-rename to {{newPath}}).',\n // --- shared ------------------------------------------------------------\n invalidKind:\n '--kind: invalid value \"{{kind}}\". Allowed: orphan, medium, ambiguous.\\n',\n\n // --- renderOrphans (pretty listing) ------------------------------------\n /** Header line for the active orphan / auto-rename issues block. */\n activeIssuesHeader: 'Active orphan / auto-rename issues:',\n /** One row per issue. `{{subject}}` is the first nodeId (or the no-node placeholder). */\n activeIssueRow: ' [{{ruleId}}] {{subject}} — {{message}}',\n /** Placeholder used when an issue has no associated node id. */\n noNodePlaceholder: '(no node)',\n} as const;\n","/**\n * `sm plugins` — discover, inspect, and toggle plugins.\n *\n * sm plugins list tabulate discovered plugins with status (and DB / settings overrides)\n * sm plugins show X dump one plugin's manifest + loaded extensions\n * sm plugins doctor full load pass + summary by failure mode\n * sm plugins enable <id> | --all write `enabled: true` to config_plugins\n * sm plugins disable <id> | --all write `enabled: false` to config_plugins\n *\n * Step 6.6 wires the enable/disable verbs and respects the resolution\n * order spec'd in `kernel/config/plugin-resolver.ts`:\n *\n * DB override (config_plugins) > settings.json (#/plugins/<id>/enabled) > installed default (true)\n *\n * Spec § A.7 — granularity. Each plugin / built-in bundle declares a\n * granularity (`bundle` or `extension`). The CLI surfaces both kinds:\n *\n * - bundle granularity ('claude', and most user plugins by default):\n * the bundle id is the only toggle-able key. `sm plugins disable\n * claude` works; `sm plugins disable claude/slash` is rejected as a\n * misuse.\n * - extension granularity ('core', plus user plugins that opt in):\n * the bundle id alone is NOT toggle-able. `sm plugins disable core`\n * is rejected; `sm plugins disable core/superseded` works.\n *\n * `--all` operates only on top-level plugin / bundle ids (never expands\n * to qualified `<bundle>/<ext>` keys); the user loses no expressivity\n * because granularity=extension bundles surface every extension in\n * `--all` only via their bundle id, which is rejected with directed\n * guidance — the right tool for the \"disable every core extension\"\n * intent is `--no-built-ins` on `sm scan`.\n */\n\nimport { existsSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\n\nimport { Command, Option } from 'clipanion';\n\nimport { builtInBundles } from '../../built-in-plugins/built-ins.js';\nimport type {\n IProvider,\n IExtractor,\n} from '../../kernel/extensions/index.js';\nimport type { ILoadedExtension } from '../../kernel/types/plugin.js';\nimport {\n createPluginLoader,\n installedSpecVersion,\n type IPluginLoaderOptions,\n} from '../../kernel/adapters/plugin-loader.js';\nimport { loadSchemaValidators } from '../../kernel/adapters/schema-validators.js';\nimport { loadConfig } from '../../kernel/config/loader.js';\nimport { makeEnabledResolver } from '../../kernel/config/plugin-resolver.js';\nimport { qualifiedExtensionId } from '../../kernel/registry.js';\nimport type {\n IDiscoveredPlugin,\n TGranularity,\n} from '../../kernel/types/plugin.js';\nimport { sanitizeForTerminal } from '../../kernel/util/safe-text.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { PLUGINS_TEXTS } from '../i18n/plugins.texts.js';\nimport {\n defaultProjectPluginsDir,\n defaultUserPluginsDir,\n resolveDbPath,\n} from '../util/db-path.js';\nimport { emitDoneStderr, startElapsed } from '../util/elapsed.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\nimport { tryWithSqlite, withSqlite } from '../util/with-sqlite.js';\n\ninterface IScopeOptions {\n global: boolean;\n pluginDir: string | undefined;\n}\n\nfunction resolveSearchPaths(opts: IScopeOptions, cwd: string, homedir: string): string[] {\n if (opts.pluginDir) return [resolve(opts.pluginDir)];\n const ctx = { cwd, homedir };\n const project = defaultProjectPluginsDir(ctx);\n const user = defaultUserPluginsDir(ctx);\n return opts.global ? [user] : [project, user];\n}\n\n/**\n * Build a resolver from the layered config (settings.json) + the DB\n * overrides (config_plugins). Either layer may be absent (no\n * settings.json, no DB) — both fall through gracefully.\n */\nasync function buildResolver(global: boolean): Promise<(id: string) => boolean> {\n const ctx = defaultRuntimeContext();\n const { effective: cfg } = loadConfig({\n scope: global ? 'global' : 'project',\n cwd: ctx.cwd,\n homedir: ctx.homedir,\n });\n const dbPath = resolveDbPath({ global, db: undefined, cwd: ctx.cwd, homedir: ctx.homedir });\n const dbOverrides =\n (await tryWithSqlite(\n { databasePath: dbPath, autoBackup: false },\n (adapter) => adapter.pluginConfig.loadOverrideMap(),\n )) ?? new Map<string, boolean>();\n return makeEnabledResolver(cfg, dbOverrides);\n}\n\nasync function loadAll(opts: IScopeOptions): Promise<IDiscoveredPlugin[]> {\n const ctx = defaultRuntimeContext();\n const validators = loadSchemaValidators();\n const loaderOpts: IPluginLoaderOptions = {\n searchPaths: resolveSearchPaths(opts, ctx.cwd, ctx.homedir),\n validators,\n specVersion: installedSpecVersion(),\n resolveEnabled: await buildResolver(opts.global),\n };\n const loader = createPluginLoader(loaderOpts);\n return loader.discoverAndLoadAll();\n}\n\nfunction statusIcon(status: IDiscoveredPlugin['status']): string {\n switch (status) {\n case 'enabled': return 'ok';\n case 'disabled': return 'off';\n case 'incompatible-spec': return 'spec!';\n case 'invalid-manifest': return 'mani!';\n case 'load-error': return 'load!';\n case 'id-collision': return 'dup!';\n }\n}\n\n// --- built-in bundle synthesis -------------------------------------------\n\ninterface IBuiltInBundleRow {\n id: string;\n granularity: TGranularity;\n enabled: boolean;\n extensions: ReadonlyArray<{\n id: string;\n kind: string;\n version: string;\n enabled: boolean;\n }>;\n /** Per-extension version+kind catalogue, used by `sm plugins show`. */\n manifestSummary: string;\n}\n\n/**\n * Build a synthesised view over the two built-in bundles, with the\n * resolved enabled-state for the bundle (granularity=bundle) or each\n * extension (granularity=extension). This lets the CLI list / show /\n * doctor / enable / disable verbs treat built-ins as first-class\n * citizens of the plugin surface — the spec promise that \"no extension\n * is privileged, removable\" only holds if the user can see and toggle\n * them through the same commands as their own plugins.\n */\nfunction builtInRows(resolveEnabled: (id: string) => boolean): IBuiltInBundleRow[] {\n return builtInBundles.map((bundle) => {\n const bundleEnabled = resolveEnabled(bundle.id);\n const extensions = bundle.extensions.map((ext) => ({\n id: ext.id,\n kind: ext.kind,\n version: ext.version,\n enabled:\n bundle.granularity === 'bundle'\n ? bundleEnabled\n : resolveEnabled(qualifiedExtensionId(bundle.id, ext.id)),\n }));\n const manifestSummary = bundle.extensions\n .map((ext) => `${ext.kind}:${qualifiedExtensionId(bundle.id, ext.id)}@${ext.version}`)\n .join(', ');\n return {\n id: bundle.id,\n granularity: bundle.granularity,\n enabled: bundleEnabled,\n extensions,\n manifestSummary,\n };\n });\n}\n\n// --- list -----------------------------------------------------------------\n\nexport class PluginsListCommand extends Command {\n static override paths = [['plugins', 'list']];\n static override usage = Command.Usage({\n category: 'Plugins',\n description: 'List discovered plugins and their load status.',\n details: 'Scans <scope>/.skill-map/plugins and ~/.skill-map/plugins (or --plugin-dir <path>). Built-in bundles (claude, core) are listed alongside user plugins.',\n });\n\n global = Option.Boolean('-g,--global', false);\n pluginDir = Option.String('--plugin-dir', { required: false });\n json = Option.Boolean('--json', false);\n\n async execute(): Promise<number> {\n const plugins = await loadAll({ global: this.global, pluginDir: this.pluginDir });\n const resolveEnabled = await buildResolver(this.global);\n const builtIns = builtInRows(resolveEnabled);\n\n if (this.json) {\n this.context.stdout.write(\n JSON.stringify({ builtIns, plugins }, omitModule, 2) + '\\n',\n );\n return ExitCode.Ok;\n }\n\n if (plugins.length === 0 && builtIns.length === 0) {\n this.context.stdout.write(PLUGINS_TEXTS.listEmpty);\n return ExitCode.Ok;\n }\n\n // Built-ins first; then user plugins.\n for (const bundle of builtIns) this.context.stdout.write(renderBuiltInBundleRow(bundle));\n for (const p of plugins) this.context.stdout.write(renderPluginRow(p));\n return ExitCode.Ok;\n }\n}\n\n/**\n * Render the multi-line block for one built-in bundle: header line plus\n * either a single inline kinds line (granularity=bundle) or one\n * indented status line per extension (granularity=extension).\n */\nfunction renderBuiltInBundleRow(bundle: IBuiltInBundleRow): string {\n const lines: string[] = [];\n lines.push(\n tx(PLUGINS_TEXTS.builtInBundleHeader, {\n status: bundle.enabled ? PLUGINS_TEXTS.rowStatusOk : PLUGINS_TEXTS.rowStatusOff,\n id: bundle.id,\n granularity: bundle.granularity,\n }),\n );\n if (bundle.granularity === 'bundle') {\n const kinds = bundle.extensions\n .map((e) => `${e.kind}:${qualifiedExtensionId(bundle.id, e.id)}`)\n .join(', ');\n lines.push(tx(PLUGINS_TEXTS.builtInBundleKindsLine, { kinds }));\n } else {\n for (const ext of bundle.extensions) {\n lines.push(\n tx(PLUGINS_TEXTS.builtInExtensionRow, {\n stat: ext.enabled ? PLUGINS_TEXTS.rowStatusOkPad : PLUGINS_TEXTS.rowStatusOffPad,\n kind: ext.kind,\n qualifiedId: qualifiedExtensionId(bundle.id, ext.id),\n version: ext.version,\n }),\n );\n }\n }\n return lines.join('\\n') + '\\n';\n}\n\n/** Render the single-line status row for one user plugin. */\nfunction renderPluginRow(p: IDiscoveredPlugin): string {\n // Defence in depth: every field that originates from the plugin's\n // manifest (`id`, `version`, `kind`, per-extension ids, `reason`) is\n // user-controlled string data. The id passes the loader's regex but\n // we still sanitize as a uniform policy. `path` is composed from\n // safe constants via `path.join` and stays bare.\n const kinds =\n p.extensions\n ?.map((e) => `${sanitizeForTerminal(e.kind)}:${sanitizeForTerminal(e.pluginId)}/${sanitizeForTerminal(e.id)}`)\n .join(', ') ?? '';\n const granularitySuffix = p.granularity\n ? tx(PLUGINS_TEXTS.pluginRowGranularitySuffix, { granularity: p.granularity })\n : '';\n const tail =\n p.status === 'enabled'\n ? tx(PLUGINS_TEXTS.pluginRowTailEnabled, { kinds })\n : tx(PLUGINS_TEXTS.pluginRowTailDisabled, {\n reason: sanitizeForTerminal(p.reason ?? ''),\n });\n return (\n tx(PLUGINS_TEXTS.pluginRow, {\n statusIcon: statusIcon(p.status).padEnd(6),\n id: sanitizeForTerminal(p.id),\n version: sanitizeForTerminal(p.manifest?.version ?? PLUGINS_TEXTS.detailVersionUnknown),\n granularitySuffix,\n tail,\n }) + '\\n'\n );\n}\n\n// --- show -----------------------------------------------------------------\n\nexport class PluginsShowCommand extends Command {\n static override paths = [['plugins', 'show']];\n static override usage = Command.Usage({\n category: 'Plugins',\n description: 'Show a single plugin\\'s manifest + loaded extensions.',\n });\n\n id = Option.String({ required: true });\n global = Option.Boolean('-g,--global', false);\n pluginDir = Option.String('--plugin-dir', { required: false });\n json = Option.Boolean('--json', false);\n\n async execute(): Promise<number> {\n const plugins = await loadAll({ global: this.global, pluginDir: this.pluginDir });\n const resolveEnabled = await buildResolver(this.global);\n const builtIns = builtInRows(resolveEnabled);\n const builtIn = builtIns.find((b) => b.id === this.id);\n const match = plugins.find((p) => p.id === this.id);\n\n if (!builtIn && !match) {\n this.context.stderr.write(tx(PLUGINS_TEXTS.pluginNotFound, { id: this.id }) + '\\n');\n return ExitCode.NotFound;\n }\n\n if (this.json) {\n const payload = builtIn ?? match;\n this.context.stdout.write(JSON.stringify(payload, omitModule, 2) + '\\n');\n return ExitCode.Ok;\n }\n\n const lines = builtIn\n ? renderBuiltInDetail(builtIn)\n : renderPluginDetail(match!);\n this.context.stdout.write(lines.join('\\n') + '\\n');\n return ExitCode.Ok;\n }\n}\n\n/** Detail rendering for one built-in bundle (header + extensions list). */\nfunction renderBuiltInDetail(builtIn: IBuiltInBundleRow): string[] {\n const lines = [\n tx(PLUGINS_TEXTS.detailIdRow, { id: builtIn.id }),\n PLUGINS_TEXTS.detailPathBuiltIn,\n tx(PLUGINS_TEXTS.detailStatusRow, {\n status: builtIn.enabled ? PLUGINS_TEXTS.detailStatusEnabled : PLUGINS_TEXTS.detailStatusDisabled,\n }),\n tx(PLUGINS_TEXTS.detailGranularityRow, { granularity: builtIn.granularity }),\n PLUGINS_TEXTS.detailExtensionsHeader,\n ];\n for (const ext of builtIn.extensions) {\n const tag =\n builtIn.granularity === 'extension'\n ? tx(PLUGINS_TEXTS.detailExtensionTag, {\n state: ext.enabled ? PLUGINS_TEXTS.detailExtensionTagOn : PLUGINS_TEXTS.detailExtensionTagOff,\n })\n : '';\n lines.push(\n tx(PLUGINS_TEXTS.detailExtensionRow, {\n kind: ext.kind,\n qualifiedId: qualifiedExtensionId(builtIn.id, ext.id),\n version: ext.version,\n tag,\n }),\n );\n }\n return lines;\n}\n\n// Optional manifest fields (`version`, `specCompat`, `granularity`,\n// `description`, `reason`) each fall back via `??` — every coalesce is\n// one cyclomatic branch, none is a real control-flow decision.\n// Defence in depth: every manifest-sourced field is sanitized before\n// rendering. `path` is composed from safe constants via `path.join`\n// and stays bare.\n// eslint-disable-next-line complexity\nfunction renderPluginDetail(match: IDiscoveredPlugin): string[] {\n const lines = [\n tx(PLUGINS_TEXTS.detailIdRow, { id: sanitizeForTerminal(match.id) }),\n tx(PLUGINS_TEXTS.detailPathRow, { path: match.path }),\n tx(PLUGINS_TEXTS.detailStatusRow, { status: match.status }),\n tx(PLUGINS_TEXTS.detailVersionRow, {\n version: sanitizeForTerminal(\n match.manifest?.version ?? PLUGINS_TEXTS.detailVersionUnknown,\n ),\n }),\n tx(PLUGINS_TEXTS.detailCompatRow, {\n compat: sanitizeForTerminal(\n match.manifest?.specCompat ?? PLUGINS_TEXTS.detailCompatUnknown,\n ),\n }),\n tx(PLUGINS_TEXTS.detailGranularityRow, {\n granularity: match.granularity ?? PLUGINS_TEXTS.detailGranularityUnknown,\n }),\n ];\n if (match.manifest?.description) {\n lines.push(\n tx(PLUGINS_TEXTS.detailSummaryRow, {\n description: sanitizeForTerminal(match.manifest.description),\n }),\n );\n }\n if (match.reason) {\n lines.push(\n tx(PLUGINS_TEXTS.detailReasonRow, { reason: sanitizeForTerminal(match.reason) }),\n );\n }\n if (match.extensions && match.extensions.length > 0) {\n lines.push(...renderExtensionsList(match.extensions));\n }\n return lines;\n}\n\n/** Indented `extensions:` block listing one bullet per loaded extension. */\nfunction renderExtensionsList(exts: ILoadedExtension[]): string[] {\n const lines: string[] = [PLUGINS_TEXTS.detailExtensionsHeader];\n for (const ext of exts) {\n lines.push(\n tx(PLUGINS_TEXTS.detailExtensionRow, {\n kind: sanitizeForTerminal(ext.kind),\n qualifiedId: `${sanitizeForTerminal(ext.pluginId)}/${sanitizeForTerminal(ext.id)}`,\n version: sanitizeForTerminal(ext.version),\n tag: '',\n }),\n );\n }\n return lines;\n}\n\n// --- applicableKinds doctor warnings (Spec § A.10) -----------------------\n\n/**\n * One unknown-kind warning. Produced when an Extractor declares\n * `applicableKinds` including a kind that no installed Provider (built-in\n * or user plugin) emits. The extractor itself stays `loaded` — the\n * Provider may arrive later — but `sm plugins doctor` surfaces the\n * mismatch so authors catch typos and missing-dependency cases early.\n */\ninterface IApplicableKindWarning {\n extractorQualifiedId: string;\n unknownKind: string;\n}\n\n/**\n * Pull the runtime instance an `ILoadedExtension` points at. The loader\n * stores the imported ESM namespace verbatim in `.module`; the\n * extension's runtime export lives at `module.default` (or, for a CJS\n * fallback, on the namespace itself). Returns `null` when the shape is\n * not recognisable — the caller treats that as \"no applicableKinds to\n * inspect\" and moves on.\n */\nfunction extensionInstance(ext: ILoadedExtension): Record<string, unknown> | null {\n const mod = ext.module;\n if (mod === null || typeof mod !== 'object') return null;\n const candidate = (mod as { default?: unknown }).default ?? mod;\n if (candidate === null || typeof candidate !== 'object') return null;\n return candidate as Record<string, unknown>;\n}\n\n/**\n * Collect the set of `node.kind` values every installed Provider\n * (built-in + user plugin) declares it can emit. The truth source is\n * `IProvider.kinds` — every kind the Provider emits MUST appear there\n * per `architecture.md` §`Provider`. The union of those keys is the\n * kernel's \"known kinds\" surface for unknown-kind detection.\n *\n * Phase 3 (spec 0.8.0): the source-of-truth migrated from a flat\n * `defaultRefreshAction` map to the `kinds` map (which subsumes both\n * the per-kind schema and the refresh action). The set of keys is the\n * same — only the field name changed.\n */\nfunction collectKnownKinds(plugins: IDiscoveredPlugin[]): Set<string> {\n const known = new Set<string>();\n forEachProviderInstance(plugins, ({ instance }) => {\n const map = instance['kinds'];\n if (map === null || typeof map !== 'object') return;\n for (const k of Object.keys(map)) known.add(k);\n });\n return known;\n}\n\n/**\n * Iterate every Provider instance reachable from this run — built-in\n * bundles first, then user plugins (enabled only). Centralises the\n * \"if (ext.kind !== 'provider') continue; cast/extract instance\"\n * guard so doctor-style helpers (collect known kinds, collect missing\n * exploration dirs, …) can stay focused on per-Provider logic.\n *\n * The `instance` field uses `Record<string, unknown>` so user-plugin\n * Providers (whose runtime shape is not type-checked) and built-in\n * Providers share the same callback signature.\n */\n// Two parallel iteration sources (built-in bundles + user plugins),\n// each with a kind/instance guard. Centralised here so doctor helpers\n// stay focused on per-Provider logic.\n// eslint-disable-next-line complexity\nfunction forEachProviderInstance(\n plugins: IDiscoveredPlugin[],\n callback: (entry: { id: string; pluginId: string; instance: Record<string, unknown> }) => void,\n): void {\n for (const bundle of builtInBundles) {\n for (const ext of bundle.extensions) {\n if (ext.kind !== 'provider') continue;\n const provider = ext as IProvider;\n callback({\n id: provider.id,\n pluginId: bundle.id,\n instance: provider as unknown as Record<string, unknown>,\n });\n }\n }\n for (const p of plugins) {\n if (p.status !== 'enabled' || !p.extensions) continue;\n for (const ext of p.extensions) {\n if (ext.kind !== 'provider') continue;\n const inst = extensionInstance(ext);\n if (!inst) continue;\n callback({ id: ext.id, pluginId: ext.pluginId, instance: inst });\n }\n }\n}\n\n/**\n * Walk every loaded Extractor (built-in + user plugin) and produce one\n * warning per unknown kind referenced via `applicableKinds`. An extractor\n * with no `applicableKinds` field is silent (default = applies to all\n * kinds). Iteration order is deterministic so the rendered doctor output\n * stays stable across runs.\n */\n// Two parallel iteration sources (built-in extractors + user plugin\n// extractors) with kind/applicableKinds guards. The shared inner loop\n// is `appendUnknownKindWarnings`.\n// eslint-disable-next-line complexity\nfunction collectApplicableKindWarnings(\n plugins: IDiscoveredPlugin[],\n knownKinds: Set<string>,\n): IApplicableKindWarning[] {\n const out: IApplicableKindWarning[] = [];\n\n // Built-in extractors (typed).\n for (const bundle of builtInBundles) {\n for (const ext of bundle.extensions) {\n if (ext.kind !== 'extractor') continue;\n const extractor = ext as IExtractor;\n if (!extractor.applicableKinds) continue;\n appendUnknownKindWarnings(\n out,\n qualifiedExtensionId(bundle.id, extractor.id),\n extractor.applicableKinds,\n knownKinds,\n );\n }\n }\n\n // User-plugin extractors (untyped — applicableKinds may be any value).\n for (const p of plugins) {\n if (p.status !== 'enabled' || !p.extensions) continue;\n for (const ext of p.extensions) {\n if (ext.kind !== 'extractor') continue;\n const inst = extensionInstance(ext);\n if (!inst) continue;\n const ak = inst['applicableKinds'];\n if (!Array.isArray(ak)) continue;\n appendUnknownKindWarnings(\n out,\n qualifiedExtensionId(ext.pluginId, ext.id),\n ak,\n knownKinds,\n );\n }\n }\n return out;\n}\n\n/**\n * Push one warning for every kind in `applicableKinds` that the\n * Provider catalog does not recognise. Tolerates `unknown[]` so the\n * user-plugin path (where the array shape is not type-checked) can\n * filter non-string entries silently.\n */\nfunction appendUnknownKindWarnings(\n out: IApplicableKindWarning[],\n extractorQualifiedId: string,\n applicableKinds: readonly unknown[],\n knownKinds: Set<string>,\n): void {\n for (const k of applicableKinds) {\n if (typeof k !== 'string') continue;\n if (!knownKinds.has(k)) out.push({ extractorQualifiedId, unknownKind: k });\n }\n}\n\n// --- explorationDir doctor warnings (Provider §) -------------------------\n\n/**\n * One missing-explorationDir warning. Produced when a Provider declares an\n * `explorationDir` that does not exist on the filesystem after `~`\n * expansion. Non-blocking — the user may legitimately have not installed\n * that platform yet — so the warning is informational and does NOT promote\n * the exit code.\n */\ninterface IProviderExplorationDirWarning {\n providerQualifiedId: string;\n explorationDir: string;\n resolvedPath: string;\n}\n\n/**\n * Resolve `~` and `~user` prefixes against the supplied home dir.\n * Mirrors the canonical shell convention so the doctor's existence check\n * matches what the Provider's `walk()` would actually traverse at scan\n * time. Returns the input verbatim when no `~` prefix is present.\n */\nfunction expandHome(p: string, homedir: string): string {\n if (p === '~') return homedir;\n if (p.startsWith('~/')) return join(homedir, p.slice(2));\n return p;\n}\n\n/**\n * Walk every loaded Provider (built-in + user plugin) and emit one warning\n * per declared `explorationDir` that does not exist on disk. The lookup\n * resolves `~` against the supplied home dir; relative paths fall back\n * to the cwd.\n */\nfunction collectExplorationDirWarnings(\n plugins: IDiscoveredPlugin[],\n homedir: string,\n): IProviderExplorationDirWarning[] {\n const out: IProviderExplorationDirWarning[] = [];\n forEachProviderInstance(plugins, ({ id, pluginId, instance }) => {\n const dir = instance['explorationDir'];\n if (typeof dir !== 'string' || dir.length === 0) return;\n const resolved = expandHome(dir, homedir);\n if (!existsSync(resolved)) {\n out.push({\n providerQualifiedId: qualifiedExtensionId(pluginId, id),\n explorationDir: dir,\n resolvedPath: resolved,\n });\n }\n });\n return out;\n}\n\n// --- doctor ---------------------------------------------------------------\n\nexport class PluginsDoctorCommand extends Command {\n static override paths = [['plugins', 'doctor']];\n static override usage = Command.Usage({\n category: 'Plugins',\n description: 'Run the full load pass and summarise by failure mode.',\n details: 'Exit code 0 when every plugin loads or is intentionally disabled; 1 when any plugin is in an error / incompat state.',\n });\n\n global = Option.Boolean('-g,--global', false);\n pluginDir = Option.String('--plugin-dir', { required: false });\n\n // Doctor verb: counts by status + applicableKinds warnings +\n // explorationDir warnings + bad-plugins issues, each with its own\n // gated render. Branching is intrinsic to the multi-section diagnostic\n // output; the per-section helpers (`collectKnownKinds`,\n // `collectApplicableKindWarnings`, `collectExplorationDirWarnings`)\n // already encapsulate the data gathering.\n // eslint-disable-next-line complexity\n async execute(): Promise<number> {\n const plugins = await loadAll({ global: this.global, pluginDir: this.pluginDir });\n const resolveEnabled = await buildResolver(this.global);\n const builtIns = builtInRows(resolveEnabled);\n const counts: Record<IDiscoveredPlugin['status'], number> = {\n enabled: 0,\n disabled: 0,\n 'incompatible-spec': 0,\n 'invalid-manifest': 0,\n 'load-error': 0,\n 'id-collision': 0,\n };\n // Built-ins contribute to enabled / disabled counts so the doctor\n // summary reflects the full surface, not just user plugins.\n for (const b of builtIns) {\n if (b.granularity === 'bundle') {\n counts[b.enabled ? 'enabled' : 'disabled']++;\n } else {\n for (const ext of b.extensions) {\n counts[ext.enabled ? 'enabled' : 'disabled']++;\n }\n }\n }\n for (const p of plugins) counts[p.status]++;\n\n const total = plugins.length + builtIns.reduce(\n (n, b) => n + (b.granularity === 'bundle' ? 1 : b.extensions.length),\n 0,\n );\n this.context.stdout.write(\n tx(PLUGINS_TEXTS.doctorDiscoveredHeader, {\n total,\n builtInCount: builtIns.length,\n userCount: plugins.length,\n }),\n );\n for (const status of Object.keys(counts) as Array<IDiscoveredPlugin['status']>) {\n this.context.stdout.write(\n tx(PLUGINS_TEXTS.doctorCountRow, {\n status: status.padEnd(18),\n count: counts[status],\n }),\n );\n }\n\n // Spec § A.10 — applicableKinds: surface unknown-kind warnings as\n // informational diagnostics. They do NOT promote the exit code (the\n // Provider that declares the kind may legitimately arrive later);\n // they only tell the author \"your extractor will never fire on the\n // kind you typed\".\n const knownKinds = collectKnownKinds(plugins);\n const applicableKindWarnings = collectApplicableKindWarnings(plugins, knownKinds);\n // Provider explorationDir validation. Non-blocking — the user may not\n // have installed that platform yet, so missing dir is informational.\n const explorationDirWarnings = collectExplorationDirWarnings(plugins, defaultRuntimeContext().homedir);\n if (applicableKindWarnings.length > 0 || explorationDirWarnings.length > 0) {\n this.context.stdout.write(PLUGINS_TEXTS.doctorWarningsHeader);\n for (const w of applicableKindWarnings) {\n this.context.stdout.write(\n tx(PLUGINS_TEXTS.doctorWarningLine, {\n message: tx(PLUGINS_TEXTS.doctorApplicableKindUnknown, {\n extractorId: w.extractorQualifiedId,\n unknownKind: w.unknownKind,\n }),\n }),\n );\n }\n for (const w of explorationDirWarnings) {\n this.context.stdout.write(\n tx(PLUGINS_TEXTS.doctorWarningLine, {\n message: tx(PLUGINS_TEXTS.doctorProviderExplorationDirMissing, {\n providerId: w.providerQualifiedId,\n explorationDir: w.explorationDir,\n resolvedPath: w.resolvedPath,\n }),\n }),\n );\n }\n }\n\n // Errors gate the exit code; `disabled` is intentional and never an issue.\n const bad = plugins.filter(\n (p) => p.status !== 'enabled' && p.status !== 'disabled',\n );\n if (bad.length > 0) {\n this.context.stdout.write(PLUGINS_TEXTS.doctorIssuesHeader);\n for (const p of bad) {\n this.context.stdout.write(\n tx(PLUGINS_TEXTS.doctorIssueLine, {\n status: p.status,\n id: p.id,\n reason: p.reason ?? '',\n }),\n );\n }\n return ExitCode.Issues;\n }\n return ExitCode.Ok;\n }\n}\n\n// --- enable / disable -----------------------------------------------------\n\ninterface IBundleSlim {\n id: string;\n granularity: TGranularity;\n extensionIds: string[];\n}\n\n/**\n * Build the canonical bundle catalogue: built-ins first, then any\n * loaded user plugins. Used by the toggle verbs to validate `<id>`\n * against the granularity declared on the owning bundle.\n *\n * Plugins whose manifest never validated (`invalid-manifest` /\n * `load-error` without a manifest) are still listed so the user can\n * disable a buggy plugin to silence its load error — but their\n * `granularity` falls back to `'bundle'` (the safe default that the\n * loader would inject if the manifest were repaired).\n */\nfunction bundleCatalogue(plugins: IDiscoveredPlugin[]): IBundleSlim[] {\n const out: IBundleSlim[] = [];\n for (const bundle of builtInBundles) {\n out.push({\n id: bundle.id,\n granularity: bundle.granularity,\n extensionIds: bundle.extensions.map((e) => e.id),\n });\n }\n for (const p of plugins) {\n out.push({\n id: p.id,\n granularity: p.granularity ?? 'bundle',\n extensionIds: p.extensions?.map((e) => e.id) ?? [],\n });\n }\n return out;\n}\n\ninterface IResolvedTarget {\n /**\n * The key written to `config_plugins.plugin_id`. For bundle granularity\n * this is the bundle id; for extension granularity it's the qualified\n * id `<bundle>/<ext>`.\n */\n key: string;\n}\n\n/**\n * Resolve a user-supplied `<id>` (either a plugin id or a qualified\n * extension id) against the catalogue. Returns either a usable\n * `key` to persist, or a directed error message that explains why the\n * id was rejected (granularity mismatch, unknown bundle, unknown\n * extension under a known bundle).\n */\n// eslint-disable-next-line complexity\nfunction resolveToggleTarget(\n id: string,\n catalogue: IBundleSlim[],\n verb: 'enable' | 'disable',\n): IResolvedTarget | { error: string } {\n if (id.includes('/')) {\n const [bundleId, extId, ...rest] = id.split('/');\n if (!bundleId || !extId || rest.length > 0) {\n return { error: tx(PLUGINS_TEXTS.qualifiedIdUnknownBundle, { bundleId: id }) };\n }\n const bundle = catalogue.find((b) => b.id === bundleId);\n if (!bundle) {\n return { error: tx(PLUGINS_TEXTS.qualifiedIdUnknownBundle, { bundleId }) };\n }\n if (bundle.granularity === 'bundle') {\n return {\n error: tx(PLUGINS_TEXTS.granularityBundleRejectsQualified, {\n bundleId,\n extId,\n verb,\n }),\n };\n }\n if (!bundle.extensionIds.includes(extId)) {\n return {\n error: tx(PLUGINS_TEXTS.qualifiedIdNotFound, {\n id,\n bundleId,\n extId,\n }),\n };\n }\n return { key: qualifiedExtensionId(bundleId, extId) };\n }\n\n const bundle = catalogue.find((b) => b.id === id);\n if (!bundle) {\n return { error: tx(PLUGINS_TEXTS.pluginNotFound, { id }) };\n }\n if (bundle.granularity === 'extension') {\n return {\n error: tx(PLUGINS_TEXTS.granularityExtensionRejectsBundleId, {\n bundleId: id,\n verb,\n }),\n };\n }\n return { key: bundle.id };\n}\n\nabstract class TogglePluginsBase extends Command {\n global = Option.Boolean('-g,--global', false);\n all = Option.Boolean('--all', false);\n id = Option.String({ required: false });\n\n // eslint-disable-next-line complexity\n protected async toggle(enabled: boolean): Promise<number> {\n const elapsed = startElapsed();\n const verb = enabled ? 'enable' : 'disable';\n if (this.all && this.id) {\n this.context.stderr.write(PLUGINS_TEXTS.toggleBothIdAndAll);\n emitDoneStderr(this.context.stderr, elapsed);\n return ExitCode.Error;\n }\n if (!this.all && !this.id) {\n this.context.stderr.write(PLUGINS_TEXTS.toggleNeitherIdNorAll);\n emitDoneStderr(this.context.stderr, elapsed);\n return ExitCode.Error;\n }\n\n // Resolve discovery so `<id>` is validated and `--all` knows the set.\n const plugins = await loadAll({\n global: this.global,\n pluginDir: undefined,\n });\n const catalogue = bundleCatalogue(plugins);\n\n let targets: string[];\n if (this.all) {\n // `--all` is a macro on bundle ids: every plugin / bundle the user\n // can see. We deliberately do NOT expand to qualified\n // <bundle>/<ext> keys — that would silently flip a granularity\n // policy. For granularity=extension bundles the user already\n // hits the directed error message (\"use bundle/<ext>\") if they\n // try the bundle id directly, so `--all` skips them here too\n // and the real \"disable every core extension\" intent is served\n // by `--no-built-ins` on `sm scan`.\n targets = catalogue\n .filter((b) => b.granularity === 'bundle')\n .map((b) => b.id);\n } else {\n const resolved = resolveToggleTarget(this.id!, catalogue, verb);\n if ('error' in resolved) {\n this.context.stderr.write(tx(PLUGINS_TEXTS.toggleResolveError, { error: resolved.error }));\n emitDoneStderr(this.context.stderr, elapsed);\n // Granularity errors and unknown ids are both user input\n // problems — exit 5 (NotFound) keeps the existing contract\n // for \"you asked me to act on something I cannot resolve\".\n return ExitCode.NotFound;\n }\n targets = [resolved.key];\n }\n\n const ctx = defaultRuntimeContext();\n const dbPath = resolveDbPath({ global: this.global, db: undefined, cwd: ctx.cwd, homedir: ctx.homedir });\n await withSqlite({ databasePath: dbPath, autoBackup: false }, async (adapter) => {\n for (const id of targets) {\n await adapter.pluginConfig.set(id, enabled);\n }\n });\n\n const verbPast = enabled ? 'enabled' : 'disabled';\n if (targets.length === 1) {\n this.context.stdout.write(tx(PLUGINS_TEXTS.toggleAppliedSingle, { verbPast, id: targets[0]! }));\n } else {\n this.context.stdout.write(\n tx(PLUGINS_TEXTS.toggleAppliedManyHeader, { verbPast, count: targets.length }),\n );\n for (const id of targets) {\n this.context.stdout.write(tx(PLUGINS_TEXTS.toggleAppliedManyRow, { id }));\n }\n }\n emitDoneStderr(this.context.stderr, elapsed);\n return ExitCode.Ok;\n }\n}\n\nexport class PluginsEnableCommand extends TogglePluginsBase {\n static override paths = [['plugins', 'enable']];\n static override usage = Command.Usage({\n category: 'Plugins',\n description: 'Enable a plugin (or --all). Persists in config_plugins.',\n details: `\n Writes a row to config_plugins with enabled=1. Takes precedence\n over the team-shared baseline at settings.json#/plugins/<id>/enabled.\n Use sm plugins disable to flip; sm config reset plugins.<id>.enabled\n drops the settings.json baseline.\n\n Granularity: a bundle-granularity plugin (default for user plugins,\n and the built-in 'claude' bundle) accepts only the bundle id. An\n extension-granularity plugin (the built-in 'core' bundle) accepts\n only qualified ids '<bundle>/<ext-id>'. Mismatches are rejected\n with directed guidance.\n `,\n });\n\n async execute(): Promise<number> {\n return this.toggle(true);\n }\n}\n\nexport class PluginsDisableCommand extends TogglePluginsBase {\n static override paths = [['plugins', 'disable']];\n static override usage = Command.Usage({\n category: 'Plugins',\n description: 'Disable a plugin (or --all). Persists in config_plugins; does not delete files.',\n details: `\n Writes a row to config_plugins with enabled=0. Discovery still\n surfaces the plugin in sm plugins list, but with status=disabled\n — its extensions are not imported and the kernel will not run\n them.\n\n Granularity: a bundle-granularity plugin (default for user plugins,\n and the built-in 'claude' bundle) accepts only the bundle id. An\n extension-granularity plugin (the built-in 'core' bundle) accepts\n only qualified ids '<bundle>/<ext-id>'. Mismatches are rejected\n with directed guidance.\n `,\n });\n\n async execute(): Promise<number> {\n return this.toggle(false);\n }\n}\n\n/* `port.pluginConfig.delete` is on the StoragePort surface, kept\n * available for `sm config reset` once that verb lands. */\n\n/**\n * JSON-serializer replacer: the ILoadedExtension.module field is a live\n * ESM namespace with circular references — omit it from output.\n *\n * We identify the namespace by its `[Symbol.toStringTag] === 'Module'`\n * marker (the standard tag Node sets on ESM module records), so a\n * plugin manifest that legitimately ships an unrelated `module` key\n * (e.g. a string property in `metadata`) is preserved. The earlier\n * implementation dropped EVERY `module` key in the tree, which silently\n * lost data on first sight.\n */\nfunction omitModule(key: string, value: unknown): unknown {\n if (key !== 'module') return value;\n if (value === null || typeof value !== 'object') return value;\n const tag = (value as { [Symbol.toStringTag]?: unknown })[Symbol.toStringTag];\n return tag === 'Module' ? undefined : value;\n}\n\nexport const PLUGIN_COMMANDS = [\n PluginsListCommand,\n PluginsShowCommand,\n PluginsDoctorCommand,\n PluginsEnableCommand,\n PluginsDisableCommand,\n];\n","/**\n * CLI strings emitted by `sm plugins` (`cli/commands/plugins.ts`).\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const PLUGINS_TEXTS = {\n // --- enable / disable error guidance --------------------------------\n // Spec § A.7 — granularity validation. The CLI rejects mismatched ids\n // up front (instead of silently writing a config_plugins row that the\n // runtime would later ignore) so the user learns the model immediately.\n granularityBundleRejectsQualified:\n \"'{{bundleId}}' has granularity=bundle. Use `sm plugins {{verb}} {{bundleId}}` to {{verb}} the whole bundle, \" +\n 'not `{{bundleId}}/{{extId}}` — individual extensions inside a bundle-granularity plugin cannot be toggled.',\n\n granularityExtensionRejectsBundleId:\n \"'{{bundleId}}' has granularity=extension. Use `sm plugins {{verb}} {{bundleId}}/<ext-id>` to {{verb}} a single \" +\n 'extension; the bundle id alone is not toggle-able. Run `sm plugins list` for the per-extension qualified ids.',\n\n pluginNotFound:\n 'Plugin not found: {{id}}. Run `sm plugins list` for discovered ids and the qualified extension ids.',\n\n qualifiedIdNotFound:\n \"Qualified extension id not found: {{id}}. The owning bundle '{{bundleId}}' does not declare an extension with id '{{extId}}'. \" +\n 'Run `sm plugins list` to see what each bundle ships.',\n\n qualifiedIdUnknownBundle:\n 'Qualified extension id references unknown bundle: {{bundleId}}. Run `sm plugins list` for known bundle ids.',\n\n // Spec § A.10 — `applicableKinds` filter on Extractors. When an extractor\n // declares a kind that no installed Provider emits, the load succeeds\n // (the Provider may arrive later) but `sm plugins doctor` surfaces a\n // non-blocking warning so the author sees the typo / missing dependency.\n // Exit code is NOT promoted by this warning.\n doctorApplicableKindUnknown:\n \"Extractor '{{extractorId}}' declares applicableKinds including '{{unknownKind}}', but no installed Provider declares that kind. \" +\n 'The extractor is loaded but will never fire on that kind.',\n\n // Provider explorationDir validation. Each Provider declares a filesystem\n // directory where its content lives (e.g. `~/.claude` for the Claude\n // Provider). `sm plugins doctor` checks the directory exists and surfaces\n // a non-blocking warning when missing — the user may legitimately not\n // have installed that platform yet, so the warning is informational.\n doctorProviderExplorationDirMissing:\n \"Provider '{{providerId}}' declares explorationDir '{{explorationDir}}', but the resolved path '{{resolvedPath}}' does not exist. \" +\n 'The Provider is loaded but will yield no nodes from that directory until it appears.',\n\n // --- list verb -------------------------------------------------------\n listEmpty: 'No plugins discovered.\\n',\n\n // --- doctor verb -----------------------------------------------------\n doctorDiscoveredHeader:\n 'Discovered {{total}} plugin(s) ({{builtInCount}} built-in bundles, {{userCount}} user):\\n',\n doctorCountRow: ' {{status}} {{count}}\\n',\n doctorWarningsHeader: '\\nWarnings:\\n',\n doctorWarningLine: ' [warn] {{message}}\\n',\n doctorIssuesHeader: '\\nIssues:\\n',\n doctorIssueLine: ' [{{status}}] {{id}} — {{reason}}\\n',\n\n // --- enable / disable -----------------------------------------------\n toggleBothIdAndAll: 'Pass either an <id> or --all, not both.\\n',\n toggleNeitherIdNorAll: 'Pass <id> or --all.\\n',\n toggleResolveError: '{{error}}\\n',\n toggleAppliedSingle: '{{verbPast}}: {{id}}\\n',\n toggleAppliedManyHeader: '{{verbPast}}: {{count}} plugin(s)\\n',\n toggleAppliedManyRow: ' - {{id}}\\n',\n\n // --- list / show renderers ------------------------------------------\n rowStatusOk: 'ok',\n rowStatusOff: 'off',\n rowStatusOkPad: 'ok ',\n rowStatusOffPad: 'off ',\n builtInBundleHeader: '{{status}} {{id}}@built-in (granularity={{granularity}})',\n builtInBundleKindsLine: ' {{kinds}}',\n builtInExtensionRow: ' {{stat}} {{kind}}:{{qualifiedId}}@{{version}}',\n pluginRow: '{{statusIcon}} {{id}}@{{version}}{{granularitySuffix}}{{tail}}',\n pluginRowGranularitySuffix: ' (granularity={{granularity}})',\n pluginRowTailEnabled: ' · {{kinds}}',\n pluginRowTailDisabled: ' · {{reason}}',\n detailIdRow: 'id: {{id}}',\n detailPathRow: 'path: {{path}}',\n detailPathBuiltIn: 'path: (built-in)',\n detailStatusRow: 'status: {{status}}',\n detailVersionRow: 'version: {{version}}',\n detailCompatRow: 'compat: {{compat}}',\n detailGranularityRow: 'granularity: {{granularity}}',\n detailGranularityUnknown: '(unknown — manifest invalid)',\n detailSummaryRow: 'summary: {{description}}',\n detailReasonRow: 'reason: {{reason}}',\n detailExtensionsHeader: 'extensions:',\n detailExtensionRow: ' - {{kind}}:{{qualifiedId}}@{{version}}{{tag}}',\n detailExtensionTag: ' [{{state}}]',\n detailExtensionTagOn: 'on',\n detailExtensionTagOff: 'off',\n detailStatusEnabled: 'enabled',\n detailStatusDisabled: 'disabled',\n detailVersionUnknown: '?',\n detailCompatUnknown: '?',\n} as const;\n","/**\n * `sm refresh <node.path>` and `sm refresh --stale` — kernel-side CLI\n * verbs for the universal enrichment layer (spec § A.8).\n *\n * Both verbs re-run extractors against either a single node or the set of\n * nodes whose probabilistic enrichment rows are flagged `stale = 1`,\n * persisting the fresh outputs back into `node_enrichments`. Deterministic\n * extractors run for real and persist; probabilistic extractors require\n * the job subsystem (Step 10) and are stubbed for now — they emit a\n * stderr advisory and skip without touching their stale rows.\n *\n * The verbs read the node's body off disk (the persisted scan is the\n * source of truth for `node.path` and the extractor manifest set, but the\n * extractor itself wants the live body). They do NOT trigger a full scan —\n * the rest of the graph stays untouched.\n *\n * Exit code: 0 on a clean stub (with a clear stderr advisory when\n * probabilistic extractors were skipped). Operational failures (DB\n * missing, node not found, plugin load error bubbling up) → exit 2 / 5\n * per spec/cli-contract.md §Exit codes.\n *\n * Stub caveats — until the job subsystem ships:\n *\n * - `--stale` only inspects probabilistic rows (those are the only ones\n * that can be stale; det rows regenerate via the A.9 fine-grained\n * scan cache and never carry the flag). The verb prints the count of\n * skipped probabilistic invocations on stderr so the user knows the\n * stale rows are still there.\n * - `<node.path>` re-runs deterministic extractors against the live\n * body and upserts their rows. Probabilistic extractors are skipped\n * with the same stderr note. Useful today as a \"force regenerate\n * this node's deterministic enrichments\" affordance, even though the\n * stale workflow it ultimately serves is partial.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\n\nimport { Command, Option } from 'clipanion';\n\nimport { listBuiltIns } from '../../built-in-plugins/built-ins.js';\nimport {\n runExtractorsForNode,\n type IEnrichmentRecord,\n type IExtractor,\n type IPersistedEnrichment,\n type Node,\n type ScanResult,\n} from '../../kernel/index.js';\nimport { InMemoryProgressEmitter } from '../../kernel/adapters/in-memory-progress.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { REFRESH_TEXTS } from '../i18n/refresh.texts.js';\nimport { defaultProjectDbPath } from '../util/db-path.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport { formatErrorMessage } from '../util/error-reporter.js';\nimport { assertContained } from '../util/path-guard.js';\nimport {\n composeScanExtensions,\n emptyPluginRuntime,\n loadPluginRuntime,\n} from '../util/plugin-runtime.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\nimport { tryWithSqlite, withSqlite } from '../util/with-sqlite.js';\n\n/**\n * `sm refresh [<node.path>] [--stale]`\n *\n * Mutex: `--stale` and the positional `<node.path>` are mutually\n * exclusive. Exactly one MUST be supplied.\n */\nexport class RefreshCommand extends Command {\n static override paths = [['refresh']];\n\n static override usage = Command.Usage({\n category: 'Scan',\n description:\n 'Refresh enrichment rows: granular (single node) or batch (every stale row).',\n details: `\n Re-runs Extractors against the node(s) and upserts their outputs into\n the universal enrichment layer (\\`node_enrichments\\`). Deterministic\n Extractors run for real and persist; probabilistic Extractors require\n the job subsystem (Step 10) and are stubbed for now — they emit a\n stderr advisory and skip without touching their stale rows.\n\n Layer separation: enrichments live separately from the author's\n frontmatter, which is immutable from any Extractor. Probabilistic\n enrichments track \\`body_hash_at_enrichment\\`; when the scan loop sees\n a body change, those rows are flagged \\`stale = 1\\` (NOT deleted, so\n the LLM cost is preserved) and surface here for refresh.\n\n Pass \\`--stale\\` to refresh every node carrying a stale row. Pass a\n positional \\`<node.path>\\` to refresh just that node. The two are\n mutually exclusive.\n `,\n examples: [\n ['Refresh a single node', '$0 refresh .claude/agents/architect.md'],\n ['Refresh every node with stale enrichments', '$0 refresh --stale'],\n ],\n });\n\n nodePath = Option.String({ name: 'node', required: false });\n stale = Option.Boolean('--stale', false, {\n description:\n 'Refresh every node whose probabilistic enrichment row is flagged stale=1.',\n });\n noPlugins = Option.Boolean('--no-plugins', false, {\n description: 'Skip drop-in plugin discovery; use only the built-in extractor set.',\n });\n\n // The remaining cyclomatic count comes from CLI ergonomics that don't\n // benefit from further extraction: argument-validation guards (2),\n // try/catch around extract (2) + persist (2), `instanceof Error` per\n // catch, plus the `if (probSkipCount > 0)` advisory. The inner work\n // already lives in `#resolveTargetNodes` and `#runDetExtractorsAcrossNodes`.\n // eslint-disable-next-line complexity\n async execute(): Promise<number> {\n // --- argument validation ------------------------------------------------\n if (this.stale && this.nodePath !== undefined) {\n this.context.stderr.write(REFRESH_TEXTS.nodeAndStaleMutex);\n return ExitCode.Error;\n }\n if (!this.stale && this.nodePath === undefined) {\n this.context.stderr.write(REFRESH_TEXTS.noTargetSpecified);\n return ExitCode.Error;\n }\n\n const ctx = defaultRuntimeContext();\n const dbPath = defaultProjectDbPath(ctx);\n\n // --- plugin runtime -----------------------------------------------------\n const pluginRuntime = this.noPlugins\n ? emptyPluginRuntime()\n : await loadPluginRuntime({ scope: 'project' });\n for (const warn of pluginRuntime.warnings) {\n this.context.stderr.write(`${warn}\\n`);\n }\n\n // We always want the built-in set + plugin set; refresh has no\n // `--no-built-ins` knob (refresh against an empty pipeline would\n // be a no-op, and the listBuiltIns import below keeps the registry\n // shape parity with `sm scan`).\n listBuiltIns(); // touch the built-in registry to surface load errors early.\n const composed = composeScanExtensions({ noBuiltIns: false, pluginRuntime });\n const allExtractors: IExtractor[] = composed?.extractors ?? [];\n\n // --- load DB-resident state --------------------------------------------\n const persisted = await tryWithSqlite(\n { databasePath: dbPath, autoBackup: false },\n async (adapter) => {\n const result = await adapter.scans.load();\n const enrichments = await adapter.scans.loadNodeEnrichments();\n return { result, enrichments };\n },\n );\n if (!persisted) {\n this.context.stderr.write(\n tx(REFRESH_TEXTS.nodeNotFound, { nodePath: this.nodePath ?? '<stale>' }),\n );\n return ExitCode.NotFound;\n }\n\n // --- decide target nodes -----------------------------------------------\n const targetResult = this.#resolveTargetNodes(persisted);\n if (!targetResult.ok) return targetResult.exitCode;\n const targetNodes = targetResult.nodes;\n\n // --- run det extractors per node, count prob skips ---------------------\n let extractResult;\n try {\n extractResult = await this.#runDetExtractorsAcrossNodes(targetNodes, allExtractors, ctx.cwd);\n } catch (err) {\n const message = formatErrorMessage(err);\n this.context.stderr.write(tx(REFRESH_TEXTS.refreshFailed, { message }));\n return ExitCode.Error;\n }\n const { freshDetEnrichments, probSkipCount, probSkipNodePaths } = extractResult;\n\n // --- persist fresh det enrichments -------------------------------------\n if (freshDetEnrichments.length > 0) {\n try {\n await withSqlite({ databasePath: dbPath, autoBackup: false }, async (adapter) => {\n await adapter.transaction(async (txStore) => {\n await txStore.enrichments.upsertMany(freshDetEnrichments);\n });\n });\n } catch (err) {\n const message = formatErrorMessage(err);\n this.context.stderr.write(tx(REFRESH_TEXTS.refreshFailed, { message }));\n return ExitCode.Error;\n }\n }\n this.context.stdout.write(\n tx(REFRESH_TEXTS.detPersisted, { detCount: freshDetEnrichments.length }),\n );\n\n // --- prob stub advisory ------------------------------------------------\n if (probSkipCount > 0) {\n this.context.stderr.write(\n tx(REFRESH_TEXTS.probStubSkipped, {\n count: probSkipCount,\n nodeCount: probSkipNodePaths.size,\n }),\n );\n }\n\n return ExitCode.Ok;\n }\n\n /**\n * Decide which nodes the verb should refresh based on `--stale` /\n * `<nodePath>`. Writes the per-target advisory to stdout (or the\n * not-found / nothing-to-do message). Returns either the target list\n * or the exit code the caller should use.\n */\n #resolveTargetNodes(\n persisted: { result: ScanResult; enrichments: IPersistedEnrichment[] },\n ): { ok: true; nodes: Node[] } | { ok: false; exitCode: number } {\n const nodesByPath = new Map<string, Node>();\n for (const node of persisted.result.nodes) nodesByPath.set(node.path, node);\n\n if (this.stale) {\n const staleEnrichments = persisted.enrichments.filter((e) => e.stale);\n if (staleEnrichments.length === 0) {\n // Terminal \"nothing to do\" message — the answer to the user's\n // request — stays on stdout.\n this.context.stdout.write(REFRESH_TEXTS.refreshingStaleNone);\n return { ok: false, exitCode: ExitCode.Ok };\n }\n const stalePaths = new Set(staleEnrichments.map((e) => e.nodePath));\n const nodes: Node[] = [];\n for (const path of stalePaths) {\n const node = nodesByPath.get(path);\n if (node) nodes.push(node);\n }\n // Mid-action banner — `info` channel, goes to stderr so a future\n // `--json` mode (or any pipe consumer) sees only the payload.\n this.context.stderr.write(\n tx(REFRESH_TEXTS.refreshingStale, {\n count: staleEnrichments.length,\n nodeCount: nodes.length,\n }),\n );\n return { ok: true, nodes };\n }\n\n const node = nodesByPath.get(this.nodePath!);\n if (!node) {\n this.context.stderr.write(\n tx(REFRESH_TEXTS.nodeNotFound, { nodePath: this.nodePath! }),\n );\n return { ok: false, exitCode: ExitCode.NotFound };\n }\n // Mid-action banner — stderr, same reasoning as above.\n this.context.stderr.write(\n tx(REFRESH_TEXTS.refreshingNode, { nodePath: node.path }),\n );\n return { ok: true, nodes: [node] };\n }\n\n /**\n * For each target node: read its body off disk, run every applicable\n * deterministic extractor, count probabilistic skips. Probabilistic\n * extractors are deferred to the job subsystem (Step 10); refresh\n * just reports the count so the user knows which extractors were\n * skipped and on which nodes.\n */\n async #runDetExtractorsAcrossNodes(\n targetNodes: Node[],\n allExtractors: IExtractor[],\n cwd: string,\n ): Promise<{\n freshDetEnrichments: IEnrichmentRecord[];\n probSkipCount: number;\n probSkipNodePaths: Set<string>;\n }> {\n const freshDetEnrichments: IEnrichmentRecord[] = [];\n let probSkipCount = 0;\n const probSkipNodePaths = new Set<string>();\n\n for (const node of targetNodes) {\n let body: string;\n try {\n // Defence in depth (audit M8): reject `node.path` rows that are\n // absolute or escape `cwd` BEFORE resolving + reading. A manually-\n // tampered DB row could otherwise route this read at any file\n // the user can open.\n assertContained(cwd, node.path);\n // Async read inside a sequential per-node loop. Today the loop\n // body still serializes (the extractor pass is awaited per\n // node), but routing the read through `fs/promises` lets the\n // event loop overlap any concurrent kernel work and keeps the\n // door open for a future parallel-by-node refresh without a\n // second sweep.\n const raw = await readFile(resolve(cwd, node.path), 'utf8');\n body = stripFrontmatterFence(raw);\n } catch (err) {\n this.context.stderr.write(\n tx(REFRESH_TEXTS.refreshFailed, {\n message: tx(REFRESH_TEXTS.readFailedDetail, {\n path: node.path,\n message: formatErrorMessage(err),\n }),\n }),\n );\n continue;\n }\n const fm = (node.frontmatter ?? {}) as Record<string, unknown>;\n const applicable = allExtractors.filter(\n (ex) => ex.applicableKinds === undefined || ex.applicableKinds.includes(node.kind),\n );\n for (const extractor of applicable) {\n if (extractor.mode === 'probabilistic') {\n probSkipCount += 1;\n probSkipNodePaths.add(node.path);\n continue;\n }\n const records = await runExtractorForEnrichment(extractor, node, body, fm);\n for (const record of records) freshDetEnrichments.push(record);\n }\n }\n\n return { freshDetEnrichments, probSkipCount, probSkipNodePaths };\n }\n}\n\n/**\n * Run a single Extractor against a node and return the enrichment records\n * it produced. Mirrors the orchestrator's per-(node, extractor) collection\n * step but is deliberately lighter — there is no link emission here, no\n * external pseudo-link partitioning, no scan-cache bookkeeping.\n *\n * Multiple `enrichNode` calls within the same `extract(ctx)` invocation\n * fold into a single record's `value` (last-write-wins per field), which\n * matches the orchestrator's contract.\n *\n * Exported for the test suite so it can drive a probe extractor directly\n * without bringing the whole CLI surface online.\n */\nexport async function runExtractorForEnrichment(\n extractor: IExtractor,\n node: Node,\n body: string,\n frontmatter: Record<string, unknown>,\n): Promise<IEnrichmentRecord[]> {\n // Delegate to the kernel's shared loop (audit item V4 — refresh used\n // to hand-duplicate the extract-and-fold dance). Refresh stays scoped\n // to the enrichment layer, so emitted links are discarded; the\n // emitter is a throwaway in-memory instance because refresh doesn't\n // expose progress events.\n const result = await runExtractorsForNode({\n extractors: [extractor],\n node,\n body,\n frontmatter,\n bodyHash: node.bodyHash,\n emitter: new InMemoryProgressEmitter(),\n });\n return result.enrichments;\n}\n\n\n/**\n * Strip a leading YAML frontmatter fence from `text`. Mirrors the\n * Provider's regex (`^---\\r?\\n[\\s\\S]*?\\r?\\n---\\r?\\n?`); if the close\n * fence is missing or the prefix is malformed, the helper returns the\n * original text unchanged — same fall-through as the Provider, where the\n * malformed-frontmatter extractor is responsible for surfacing the issue.\n */\nfunction stripFrontmatterFence(text: string): string {\n const match = text.match(/^---\\r?\\n[\\s\\S]*?\\r?\\n---\\r?\\n?/);\n if (!match) return text;\n return text.slice(match[0].length);\n}\n\n/** Aggregate export so `entry.ts` can register the refresh verb in one line. */\nexport const REFRESH_COMMANDS = [RefreshCommand];\n","/**\n * CLI strings emitted by `sm refresh` and `sm refresh --stale`\n * (`cli/commands/refresh.ts`).\n *\n * `sm refresh` is the granular companion to the universal enrichment\n * layer (spec § A.8). It re-runs Extractors against a single node (or\n * the set of nodes whose probabilistic enrichment rows are flagged\n * `stale`) so the kernel-curated overlay refreshes against the current\n * body. Pre-job-subsystem the prob path is stubbed; det extractors run\n * for real.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const REFRESH_TEXTS = {\n // --- argument validation --------------------------------------------------\n nodeAndStaleMutex:\n 'sm refresh: --stale cannot be combined with a positional <node.path>.\\n',\n\n noTargetSpecified:\n 'sm refresh: pass <node.path> for a single-node refresh, or --stale to ' +\n 'refresh every node with a stale enrichment row.\\n',\n\n // --- node lookup ----------------------------------------------------------\n nodeNotFound:\n 'sm refresh: node not found in the persisted scan: {{nodePath}}\\n' +\n 'Run `sm scan` first, then retry with the path as it appears in `sm list`.\\n',\n\n // --- happy path -----------------------------------------------------------\n refreshingNode: 'Refreshing enrichments for {{nodePath}}\\n',\n refreshingStale:\n 'Refreshing {{count}} stale enrichment row(s) across {{nodeCount}} node(s).\\n',\n\n refreshingStaleNone:\n 'sm refresh --stale: no stale enrichment rows in the DB. Nothing to do.\\n',\n\n // --- summary --------------------------------------------------------------\n detPersisted:\n 'Persisted {{detCount}} deterministic enrichment row(s).\\n',\n\n // --- prob stub ------------------------------------------------------------\n probStubSkipped:\n 'sm refresh: probabilistic refresh requires the job subsystem (Step 10). ' +\n 'Stub implementation: skipped {{count}} probabilistic extractor invocation(s) ' +\n 'across {{nodeCount}} node(s). Full probabilistic refresh lands when the job ' +\n 'subsystem ships.\\n',\n\n // --- failures -------------------------------------------------------------\n refreshFailed: 'sm refresh: {{message}}\\n',\n\n /**\n * Sub-detail composed inside `refreshFailed` when the failure is a\n * filesystem read on a specific node body. Catalogued separately so the\n * \"read failed for <path>: <err>\" copy lives in the i18n surface, not\n * in a TS string template.\n */\n readFailedDetail: 'read failed for {{path}}: {{message}}',\n} as const;\n","/**\n * Containment guards for filesystem paths the CLI dereferences from\n * persisted state (typically `node.path` rows from a SQLite snapshot).\n *\n * The threat model: a manually-tampered `.skill-map/skill-map.db` (or a\n * future plugin migration that writes raw rows) could land an absolute\n * path or a `../../`-laden relative path into `scan_nodes.path`. Verbs\n * that resolve the path against `cwd` and read the result (`sm refresh`,\n * future enrichment / export verbs) would then read files anywhere on\n * the disk.\n *\n * `assertContained` rejects both shapes before the read happens. It is\n * deliberately strict: relative paths only, no segment may escape the\n * supplied root after `resolve` collapses `..` segments. Internal\n * messages are English crude — they bubble up as `throw new Error(...)`,\n * not `tx(...)`, because they signal a tampered DB rather than a user\n * input problem.\n */\n\nimport { isAbsolute, resolve, sep } from 'node:path';\n\n/**\n * Throw when `rel` does not stay inside `cwd` after path resolution.\n * The caller is expected to wrap the throw into a verb-specific error\n * surface; the helper deliberately does not return a discriminated\n * union because the failure mode (tampered DB) is exceptional, not\n * routine.\n */\nexport function assertContained(cwd: string, rel: string): void {\n if (isAbsolute(rel)) {\n throw new Error(`node path is absolute, refusing to read: ${rel}`);\n }\n const abs = resolve(cwd, rel);\n if (abs !== cwd && !abs.startsWith(cwd + sep)) {\n throw new Error(`node path escapes repo root: ${rel}`);\n }\n}\n","import { Command, Option } from 'clipanion';\n\nimport { createKernel, runScan, runScanWithRenames } from '../../kernel/index.js';\nimport type {\n IEnrichmentRecord,\n IExtractorRunRecord,\n RenameOp,\n ScanResult,\n} from '../../kernel/index.js';\nimport { loadSchemaValidators } from '../../kernel/adapters/schema-validators.js';\nimport { listBuiltIns } from '../../built-in-plugins/built-ins.js';\nimport type { StoragePort } from '../../kernel/ports/storage.js';\nimport { loadConfig } from '../../kernel/config/loader.js';\nimport { buildIgnoreFilter, readIgnoreFileText } from '../../kernel/scan/ignore.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { SCAN_TEXTS } from '../i18n/scan.texts.js';\nimport { createCliProgressEmitter } from '../util/cli-progress-emitter.js';\nimport { defaultProjectDbPath } from '../util/db-path.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport { formatErrorMessage } from '../util/error-reporter.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\nimport {\n composeScanExtensions,\n emptyPluginRuntime,\n filterBuiltInManifests,\n loadPluginRuntime,\n type IPluginRuntimeBundle,\n} from '../util/plugin-runtime.js';\nimport { tryWithSqlite, withSqlite } from '../util/with-sqlite.js';\nimport { runWatchLoop } from './watch.js';\n\n/**\n * `sm scan [roots...] [--json] [--no-built-ins] [--no-plugins] [-n|--dry-run] [--changed]`\n *\n * Scans the given roots using the built-in extension set (claude Provider,\n * 4 extractors, 3 rules) plus any drop-in plugin extensions discovered\n * under `.skill-map/plugins/` and `~/.skill-map/plugins/` (Step 9.1).\n * The registry is populated with manifest rows so introspection\n * (`sm help`, `sm plugins list`) sees what's active; the orchestrator\n * consumes the callable instances separately.\n *\n * Result is persisted into `<cwd>/.skill-map/skill-map.db` (auto-migrated)\n * with replace-all semantics across `scan_nodes / scan_links / scan_issues`.\n *\n * - `--no-built-ins` skips both the pipeline and the persistence step\n * (kernel-empty-boot parity); cannot be combined with `--changed`.\n * - `--no-plugins` skips drop-in plugin discovery entirely. Only the\n * built-in set runs. Pairs with `--no-built-ins` for a fully empty\n * pipeline (e.g. for the `kernel-empty-boot` conformance contract).\n * Failed / incompatible plugins are logged to stderr and skipped;\n * the scan never aborts on a bad plugin.\n * - `-n` / `--dry-run` runs the scan in-memory and skips ALL DB writes.\n * Combined with `--changed` it still opens the DB read-side to load\n * the prior snapshot, then exits without writing.\n * - `--changed` performs an incremental scan against the persisted prior\n * snapshot. Reuses unchanged nodes (matched by path + bodyHash +\n * frontmatterHash) and reprocesses new / modified files only. If the\n * DB doesn't exist or the prior snapshot is empty, degrades to a full\n * scan and prints a one-liner to stderr.\n */\nexport class ScanCommand extends Command {\n static override paths = [['scan']];\n\n static override usage = Command.Usage({\n category: 'Scan',\n description: 'Scan roots for markdown nodes, run extractors and rules.',\n details: `\n Walks the given roots with the built-in claude Provider, runs the\n frontmatter / slash / at-directive / external-url-counter\n extractors per node, then the trigger-collision / broken-ref /\n superseded rules over the full graph. Emits a ScanResult\n conforming to scan-result.schema.json.\n\n The result is persisted into <cwd>/.skill-map/skill-map.db\n (replace-all over scan_nodes/links/issues). Pass --no-built-ins\n to skip both the pipeline and the persistence step (kernel-empty-boot\n parity).\n\n Pass -n / --dry-run to skip every DB operation (the result is\n computed in memory and emitted to stdout). Pass --changed to load\n the prior snapshot from the DB, reuse unchanged nodes, and only\n reprocess new / modified files.\n `,\n examples: [\n ['Scan the current directory', '$0 scan'],\n ['Scan multiple roots and print JSON', '$0 scan ./docs ./skills --json'],\n ['Empty-pipeline conformance', '$0 scan --no-built-ins --json'],\n ['Dry-run, no DB writes', '$0 scan -n --json'],\n ['Incremental scan against prior snapshot', '$0 scan --changed'],\n ['What would the next incremental scan persist?', '$0 scan --changed -n --json'],\n ],\n });\n\n roots = Option.Rest({ name: 'roots' });\n json = Option.Boolean('--json', false, {\n description: 'Emit a machine-readable ScanResult document on stdout.',\n });\n noBuiltIns = Option.Boolean('--no-built-ins', false, {\n description: 'Skip the built-in extension set. Yields a zero-filled ScanResult (kernel-empty-boot parity); skips DB persistence.',\n });\n noPlugins = Option.Boolean('--no-plugins', false, {\n description: 'Skip drop-in plugin discovery. Only the built-in set runs. Combine with --no-built-ins for a fully empty pipeline.',\n });\n noTokens = Option.Boolean('--no-tokens', false, {\n description: 'Skip per-node token counts (cl100k_base BPE). Leaves node.tokens undefined; spec-valid since the field is optional.',\n });\n dryRun = Option.Boolean('-n,--dry-run', false, {\n description: 'Run the scan in memory and skip every DB write. Combined with --changed, still opens the DB read-side to load the prior snapshot.',\n });\n changed = Option.Boolean('--changed', false, {\n description: 'Incremental scan: reuse unchanged nodes from the persisted prior snapshot. Degrades to a full scan if no prior snapshot exists.',\n });\n allowEmpty = Option.Boolean('--allow-empty', false, {\n description: 'Allow a zero-result scan to wipe an already-populated DB (replace-all replace by zero rows). Off by default to avoid the typo-trap where an invalid root silently clears your data.',\n });\n strict = Option.Boolean('--strict', false, {\n description: 'Promote frontmatter-validation findings from warn to error (exit code 1 on any violation). Overrides scan.strict from config when both are set.',\n });\n watch = Option.Boolean('--watch', false, {\n description: 'Long-running mode: watch the roots and trigger an incremental scan after each debounced batch of filesystem events. Alias of `sm watch`.',\n });\n\n // The biggest CLI orchestrator — watch alias dispatch + flag combo\n // checks + plugin runtime + config + ignore filter + prior-snapshot\n // load + persist branch (with stranded-orphan guard) + dry-run branch\n // + JSON / human render with strict self-validation. The core scan\n // pipeline lives in `runScan` / `runScanWithRenames`; the\n // `loadPrior` and `runScanWith` closures below encapsulate the DB +\n // option wiring. Splitting per branch would scatter Clipanion's\n // `this.<flag>` reads from the validations they shape.\n // eslint-disable-next-line complexity\n async execute(): Promise<number> {\n // --- watch alias -----------------------------------------------------\n // `--watch` is a thin alias for the `sm watch` verb. We delegate to\n // the shared loop so there is exactly one watcher implementation.\n // Combining `--watch` with one-shot-only flags is incoherent — the\n // watcher always persists incrementally over the prior snapshot.\n if (this.watch) {\n if (this.noBuiltIns || this.dryRun || this.changed || this.allowEmpty) {\n this.context.stderr.write(SCAN_TEXTS.watchCannotCombine);\n return ExitCode.Error;\n }\n const roots = this.roots.length > 0 ? this.roots : ['.'];\n return runWatchLoop({\n roots,\n json: this.json,\n noTokens: this.noTokens,\n strict: this.strict,\n noPlugins: this.noPlugins,\n context: this.context,\n });\n }\n\n // --- flag combinatorics -------------------------------------------------\n // `--no-built-ins` zero-fills the pipeline; combining it with\n // `--changed` (which loads a prior to merge against) is incoherent.\n if (this.changed && this.noBuiltIns) {\n this.context.stderr.write(SCAN_TEXTS.changedWithoutBuiltIns);\n return ExitCode.Error;\n }\n\n const kernel = createKernel();\n const roots = this.roots.length > 0 ? this.roots : ['.'];\n\n // --- plugin runtime --------------------------------------------------\n // Step 9.1 wires plugin discovery into the scan pipeline. Failed\n // plugins (`incompatible-spec` / `invalid-manifest` / `load-error`)\n // emit one stderr line each but never abort the scan — the kernel\n // keeps booting on a bad plugin. Disabled plugins are silently\n // skipped; their `sm plugins list` row already conveys intent.\n //\n // `--no-plugins` short-circuits discovery entirely (no DB / config\n // reads, no FS walk under `.skill-map/plugins/`). Pairs with\n // `--no-built-ins` for the kernel-empty-boot conformance posture.\n const pluginRuntime = this.noPlugins\n ? emptyPluginRuntime()\n : await loadPluginRuntime({ scope: 'project' });\n for (const warn of pluginRuntime.warnings) {\n this.context.stderr.write(`${warn}\\n`);\n }\n\n const extensions = composeScanExtensions({\n noBuiltIns: this.noBuiltIns,\n pluginRuntime,\n });\n if (!this.noBuiltIns) {\n // Granularity filter: a user-disabled built-in (whether bundle-\n // level `claude` or extension-level `core/<id>`) is silenced from\n // the registry too, so `sm help` / `sm plugins list` introspection\n // does not advertise it as active.\n const enabledBuiltIns = filterBuiltInManifests(listBuiltIns(), pluginRuntime.resolveEnabled);\n for (const manifest of enabledBuiltIns) kernel.registry.register(manifest);\n }\n for (const manifest of pluginRuntime.manifests) kernel.registry.register(manifest);\n\n const ctx = defaultRuntimeContext();\n const dbPath = defaultProjectDbPath(ctx);\n\n // --- config + ignore filter (no DB needed) -----------------------------\n // Loaded BEFORE we touch SQLite so a malformed config fails fast\n // without spinning up a connection.\n //\n // `--strict` (Step 6.7 + the .strict-config unification) propagates\n // to BOTH validation surfaces: the layered loader (so a bogus key\n // in settings.json fails the scan instead of being skipped with a\n // warning) and the per-node frontmatter validator (so any node\n // emitting a `frontmatter-invalid` issue trips exit 1).\n let cfg;\n try {\n cfg = loadConfig({ scope: 'project', strict: this.strict, ...ctx }).effective;\n } catch (err) {\n const message = formatErrorMessage(err);\n this.context.stderr.write(tx(SCAN_TEXTS.scanFailure, { message }));\n return ExitCode.Error;\n }\n const ignoreFileText = readIgnoreFileText(ctx.cwd);\n const ignoreFilterOpts: Parameters<typeof buildIgnoreFilter>[0] = {};\n if (cfg.ignore.length > 0) ignoreFilterOpts.configIgnore = cfg.ignore;\n if (ignoreFileText !== undefined) ignoreFilterOpts.ignoreFileText = ignoreFileText;\n const ignoreFilter = buildIgnoreFilter(ignoreFilterOpts);\n\n // Frontmatter strict: --strict on the CLI takes precedence; scan.strict\n // in config provides the team default. (Loader strict above is gated\n // strictly by --strict — config can't promote a loader warning to an\n // error transparently because the warning lives at config-load time.)\n const strict = this.strict || cfg.scan.strict === true;\n\n // --- prior snapshot semantics -----------------------------------------\n // Step 5.8 decoupled \"prior for rename detection\" from \"prior for\n // cache reuse\". The orchestrator uses `priorSnapshot` to fire the\n // rename heuristic (every scan that can detect deletes / additions),\n // and uses `enableCache` — independently — to decide whether to skip\n // extractors on hash-matching nodes (`--changed` only).\n //\n // When `--changed` is set but no prior is found, we warn so the user\n // gets feedback that the incremental flag had nothing to act on.\n // Without `--changed`, an empty / missing prior is silent (it's the\n // normal first-scan path).\n const loadPrior = async (\n adapter: StoragePort,\n ): Promise<ScanResult | null> => {\n if (this.noBuiltIns) return null;\n const loaded = await adapter.scans.load();\n if (loaded.nodes.length === 0) return null;\n // H6 — under `--strict`, validate the prior we just hydrated from\n // SQLite against `scan-result.schema.json` before letting the\n // orchestrator consume it. A DB that was corrupted manually,\n // mid-rollback, or by a downstream tool can hand us nodes with\n // null / wrong-typed `bodyHash` / `frontmatterHash`, which the\n // rename heuristic dereferences directly. Without `--strict` the\n // current best-effort behaviour stays — casual scans against a\n // partially-broken DB still produce something useful.\n if (strict) {\n const validators = loadSchemaValidators();\n const result = validators.validate('scan-result', loaded);\n if (!result.ok) {\n throw new Error(tx(SCAN_TEXTS.priorSchemaValidationFailed, { errors: result.errors }));\n }\n }\n return loaded;\n };\n\n // --- run scan, given a prior --------------------------------------------\n // Closure so the path that persists (single open) and the path that\n // doesn't (ephemeral read open + standalone scan) share one runScan\n // invocation. The optional `priorExtractorRuns` map drives the\n // Phase 4 / A.9 fine-grained Extractor cache; the CLI loads it from\n // `scan_extractor_runs` whenever the prior snapshot is hydrated.\n const runScanWith = async (\n prior: ScanResult | null,\n priorExtractorRuns?: Map<string, Map<string, string>>,\n ): Promise<{\n result: ScanResult;\n renameOps: RenameOp[];\n extractorRuns: IExtractorRunRecord[];\n enrichments: IEnrichmentRecord[];\n }> => {\n if (this.changed && prior === null) {\n this.context.stderr.write(SCAN_TEXTS.changedNoPriorWarning);\n }\n const runOptions: Parameters<typeof runScan>[1] = {\n roots,\n // `--global` for `sm scan` lands in Step 6 (config + onboarding).\n // The orchestrator already accepts the scope override; the CLI\n // surface defaults to `'project'` until the flag is wired.\n scope: 'project',\n tokenize: !this.noTokens,\n ignoreFilter,\n strict,\n emitter: createCliProgressEmitter(this.context.stderr),\n };\n if (extensions) runOptions.extensions = extensions;\n if (prior) {\n runOptions.priorSnapshot = prior;\n // Cache reuse is opt-in via `--changed`. With a prior loaded but\n // no `--changed`, the rename heuristic still fires but every\n // file re-walks through extractors deterministically.\n runOptions.enableCache = this.changed;\n }\n if (priorExtractorRuns) runOptions.priorExtractorRuns = priorExtractorRuns;\n return await runScanWithRenames(kernel, runOptions);\n };\n\n const willPersist = !this.noBuiltIns && !this.dryRun;\n let result: ScanResult;\n let renameOps: RenameOp[];\n let persistedTo: string | null = null;\n\n // Surface root-validation errors from the orchestrator as clean\n // operational failures (exit 2) rather than crash-trace dumps.\n // `runScan` validates each root exists as a directory up front;\n // those messages start with `runScan: root path ...`. The\n // persist-guard error path returns its own structured kind so\n // the caller can render the canonical \"refusing to wipe ...\"\n // line outside the DB scope.\n type IScanOutcome =\n | {\n kind: 'ok';\n result: ScanResult;\n renameOps: RenameOp[];\n extractorRuns: IExtractorRunRecord[];\n enrichments: IEnrichmentRecord[];\n }\n | { kind: 'scan-error'; message: string }\n | { kind: 'guard'; existing: number };\n\n let outcome: IScanOutcome;\n if (willPersist) {\n // SINGLE open: read prior + runScan + guard + persist all happen\n // inside one withSqlite. Saves one migration discovery + one\n // WAL setup vs. the old read-prior + persist double-open.\n try {\n outcome = await withSqlite({ databasePath: dbPath }, async (adapter) => {\n const prior = await loadPrior(adapter);\n // Phase 4 / A.9 — load the fine-grained Extractor cache only\n // when the prior snapshot is in play. Without a prior, the\n // orchestrator never hits the cache path so the runs map is\n // wasted I/O.\n const priorExtractorRuns =\n this.changed && prior ? await adapter.scans.loadExtractorRuns() : undefined;\n let scanned: {\n result: ScanResult;\n renameOps: RenameOp[];\n extractorRuns: IExtractorRunRecord[];\n enrichments: IEnrichmentRecord[];\n };\n try {\n scanned = await runScanWith(prior, priorExtractorRuns);\n } catch (err) {\n const message = formatErrorMessage(err);\n return { kind: 'scan-error', message };\n }\n // Defensive guard: refuse to wipe a populated DB with a\n // zero-result scan unless `--allow-empty` is passed. Belt-and-\n // braces with the orchestrator-level root validation: even if\n // a future code path or weird edge case yields a zero-filled\n // ScanResult, an existing populated snapshot survives. The\n // natural case of \"empty repo on first scan\" is not affected\n // (DB starts empty, scan returns 0 rows, persist proceeds).\n if (scanned.result.stats.nodesCount === 0 && !this.allowEmpty) {\n const counts = await adapter.scans.countRows();\n const existing = counts.nodes + counts.links + counts.issues;\n if (existing > 0) return { kind: 'guard', existing };\n }\n await adapter.scans.persist(scanned.result, {\n renameOps: scanned.renameOps,\n extractorRuns: scanned.extractorRuns,\n enrichments: scanned.enrichments,\n });\n return { kind: 'ok', ...scanned };\n });\n } catch (err) {\n // Open / migration / persist failures bubble out of withSqlite.\n const message = formatErrorMessage(err);\n this.context.stderr.write(tx(SCAN_TEXTS.scanFailure, { message }));\n return ExitCode.Error;\n }\n if (outcome.kind === 'scan-error') {\n this.context.stderr.write(tx(SCAN_TEXTS.scanFailure, { message: outcome.message }));\n return ExitCode.Error;\n }\n if (outcome.kind === 'guard') {\n this.context.stderr.write(tx(SCAN_TEXTS.guardWipeRefused, { existing: outcome.existing }));\n return ExitCode.Error;\n }\n result = outcome.result;\n renameOps = outcome.renameOps;\n persistedTo = dbPath;\n } else {\n // Non-persist path: ephemeral read-only open for the prior, then\n // runScan with the DB closed. We do NOT auto-create the DB here —\n // a `--dry-run` over a missing DB should not provision a scope.\n let prior: ScanResult | null;\n try {\n prior = this.noBuiltIns\n ? null\n : await tryWithSqlite(\n { databasePath: dbPath, autoBackup: false },\n loadPrior,\n );\n } catch (err) {\n // `loadPrior` throws under `--strict` if the DB-resident\n // scan-result fails schema validation. Surface it the same way\n // we surface a runScan failure.\n const message = formatErrorMessage(err);\n this.context.stderr.write(tx(SCAN_TEXTS.scanFailure, { message }));\n return ExitCode.Error;\n }\n try {\n const scanned = await runScanWith(prior);\n result = scanned.result;\n renameOps = scanned.renameOps;\n } catch (err) {\n const message = formatErrorMessage(err);\n this.context.stderr.write(tx(SCAN_TEXTS.scanFailure, { message }));\n return ExitCode.Error;\n }\n }\n\n // Exit code mirrors `sm check` (and spec/cli-contract.md §Exit codes):\n // 1 only when at least one issue is at `error` severity. Warns / infos\n // do not fail the verb. The exit code is independent of `--json`.\n const exitCode = result.issues.some((i) => i.severity === 'error') ? ExitCode.Issues : ExitCode.Ok;\n\n if (this.json) {\n // H4 — under `--strict`, self-validate the ScanResult against\n // `scan-result.schema.json` before emitting it. The\n // orchestrator's per-link / per-issue guards (`validateLink`,\n // `validateIssue`) only check shallow shape; a custom extractor\n // could still produce a Link that fails the full schema and\n // would silently slip into stdout. Without this gate, a\n // downstream `sm scan compare-with <dump>` that loads the dump\n // through its schema validator would fail with an error the\n // original scan never surfaced — the kind of drift `--strict`\n // exists to prevent.\n if (strict) {\n const validators = loadSchemaValidators();\n const validation = validators.validate('scan-result', result);\n if (!validation.ok) {\n this.context.stderr.write(tx(SCAN_TEXTS.jsonSelfValidationFailed, { errors: validation.errors }));\n return ExitCode.Error;\n }\n }\n this.context.stdout.write(JSON.stringify(result) + '\\n');\n return exitCode;\n }\n\n this.context.stdout.write(\n tx(SCAN_TEXTS.scannedSummary, {\n rootsCount: result.roots.length,\n durationMs: result.stats.durationMs,\n nodes: result.stats.nodesCount,\n links: result.stats.linksCount,\n issues: result.stats.issuesCount,\n }),\n );\n if (persistedTo) {\n this.context.stdout.write(tx(SCAN_TEXTS.persistedTo, { dbPath: persistedTo }));\n } else if (this.dryRun && !this.noBuiltIns) {\n this.context.stdout.write(\n tx(SCAN_TEXTS.wouldPersist, {\n nodes: result.stats.nodesCount,\n links: result.stats.linksCount,\n issues: result.stats.issuesCount,\n dbPath,\n }),\n );\n }\n return exitCode;\n }\n}\n\n","/**\n * CLI strings emitted by `sm scan` and the `sm scan compare-with`\n * sub-verb (`cli/commands/scan.ts`, `cli/commands/scan-compare.ts`).\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const SCAN_TEXTS = {\n // --- scan command ----------------------------------------------------\n watchCannotCombine:\n '--watch cannot be combined with --no-built-ins, --dry-run, --changed, or --allow-empty.\\n',\n\n changedWithoutBuiltIns:\n '--changed and --no-built-ins cannot be combined: --no-built-ins yields a zero-filled ScanResult, leaving nothing to merge against.\\n',\n\n changedNoPriorWarning: '--changed: no prior snapshot found; running full scan.\\n',\n\n scanFailure: 'sm scan: {{message}}\\n',\n\n guardWipeRefused:\n 'sm scan: refusing to wipe a populated DB ({{existing}} rows in scan_*) ' +\n 'with a zero-result scan. Pass --allow-empty to override. ' +\n 'If this is unexpected, double-check the root paths.\\n',\n\n jsonSelfValidationFailed:\n 'sm scan: internal — scan-result failed self-validation: {{errors}}\\n',\n\n scannedSummary:\n 'Scanned {{rootsCount}} root(s) in {{durationMs}}ms — ' +\n '{{nodes}} nodes, {{links}} links, {{issues}} issues.\\n',\n\n persistedTo: 'Persisted to {{dbPath}}\\n',\n\n wouldPersist:\n 'Would persist {{nodes}} nodes / {{links}} links / {{issues}} issues to {{dbPath}} (dry-run).\\n',\n\n priorSchemaValidationFailed:\n 'prior scan-result loaded from DB failed schema validation: {{errors}}. ' +\n 'Run `sm db backup` then re-scan without --strict to rebuild from disk.',\n\n // --- scan compare-with sub-verb --------------------------------------\n compareErrorPrefix: 'sm scan compare-with: {{message}}\\n',\n\n compareDumpNotFound: 'dump file not found: {{path}}',\n\n compareDumpReadFailed: 'could not read dump file {{path}}: {{message}}',\n\n compareDumpInvalidJson: 'dump file is not valid JSON: {{message}}',\n\n compareDumpSchemaMismatch: 'dump does not conform to scan-result.schema.json: {{errors}}',\n\n // --- scan compare-with delta render (human-readable output) ----------\n compareDeltaSummary:\n 'Delta vs {{comparedWith}}: ' +\n '{{nodesAdded}} nodes added, {{nodesRemoved}} removed, {{nodesChanged}} changed; ' +\n '{{linksAdded}} links added, {{linksRemoved}} removed; ' +\n '{{issuesAdded}} issues added, {{issuesRemoved}} removed.',\n\n compareDeltaNoDifferences: '(no differences)',\n\n compareDeltaNodesHeader: '## nodes',\n compareDeltaLinksHeader: '## links',\n compareDeltaIssuesHeader: '## issues',\n\n /** `+ <path> (<kind>)` — added node row. */\n compareDeltaNodeAdded: '+ {{path}} ({{kind}})',\n /** `- <path> (<kind>)` — removed node row. */\n compareDeltaNodeRemoved: '- {{path}} ({{kind}})',\n /** `~ <path> (<reason> changed)` — changed node row. */\n compareDeltaNodeChanged: '~ {{path}} ({{reason}} changed)',\n\n /** `+ <source> --<kind>--> <target>` — added link row. */\n compareDeltaLinkAdded: '+ {{source}} --{{kind}}--> {{target}}',\n /** `- <source> --<kind>--> <target>` — removed link row. */\n compareDeltaLinkRemoved: '- {{source}} --{{kind}}--> {{target}}',\n\n /** `+ [<severity>] <ruleId>: <message>` — added issue row. */\n compareDeltaIssueAdded: '+ [{{severity}}] {{ruleId}}: {{message}}',\n /** `- [<severity>] <ruleId>: <message>` — removed issue row. */\n compareDeltaIssueRemoved: '- [{{severity}}] {{ruleId}}: {{message}}',\n} as const;\n","/**\n * `sm watch [roots...]` — long-running incremental scan loop.\n *\n * Flow:\n *\n * 1. Load config + ignore filter once (same composition as `sm scan`).\n * 2. Run an initial incremental scan + persist, so the DB matches the\n * current filesystem before the watcher fires anything.\n * 3. Subscribe via `createChokidarWatcher` with `scan.watch.debounceMs`\n * from config.\n * 4. On each debounced batch, re-run the same scan+persist pipeline\n * and print one summary line (or one ScanResult ndjson record under\n * `--json`).\n * 5. SIGINT / SIGTERM closes the watcher and exits 0. Operational\n * errors during initial setup exit 2; per-batch errors are logged\n * and the loop keeps running (a transient FS error must not kill\n * a long-running watcher).\n *\n * `sm scan --watch` is an alias: `ScanCommand` detects the flag and\n * delegates here so we keep one implementation. The two surfaces share\n * the exit-code rule too — clean watcher shutdown is always 0,\n * regardless of per-batch issue severities.\n */\n\nimport { Command, Option } from 'clipanion';\n\nimport {\n createChokidarWatcher,\n createKernel,\n runScanWithRenames,\n} from '../../kernel/index.js';\nimport type {\n IEnrichmentRecord,\n IExtractorRunRecord,\n RenameOp,\n ScanResult,\n} from '../../kernel/index.js';\nimport { listBuiltIns } from '../../built-in-plugins/built-ins.js';\nimport { loadSchemaValidators } from '../../kernel/adapters/schema-validators.js';\nimport { loadConfig } from '../../kernel/config/loader.js';\nimport { buildIgnoreFilter, readIgnoreFileText } from '../../kernel/scan/ignore.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { WATCH_TEXTS } from '../i18n/watch.texts.js';\nimport { createCliProgressEmitter } from '../util/cli-progress-emitter.js';\nimport { defaultProjectDbPath } from '../util/db-path.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport { formatErrorMessage } from '../util/error-reporter.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\nimport {\n composeScanExtensions,\n emptyPluginRuntime,\n filterBuiltInManifests,\n loadPluginRuntime,\n} from '../util/plugin-runtime.js';\nimport { tryWithSqlite, withSqlite } from '../util/with-sqlite.js';\n\nexport interface IRunWatchOptions {\n roots: string[];\n json: boolean;\n noTokens: boolean;\n strict: boolean;\n /** Skip plugin discovery entirely. Step 9.1. */\n noPlugins?: boolean;\n context: {\n stdout: NodeJS.WritableStream;\n stderr: NodeJS.WritableStream;\n };\n /** Test hook: when set, the watcher closes after this many batches. */\n maxBatches?: number;\n}\n\n/**\n * Shared implementation behind `sm watch` and `sm scan --watch`.\n * Returns the final process exit code.\n */\n// Long-running watch loop: config + plugin runtime + initial scan +\n// debounced batch handler + signal handlers. Branching is intrinsic to\n// the loop's lifecycle (first-scan vs follow-up scan, JSON vs human\n// render, error recovery). The handler bodies use injected helpers.\n// eslint-disable-next-line complexity\nexport async function runWatchLoop(opts: IRunWatchOptions): Promise<number> {\n const { context } = opts;\n const runtimeCtx = defaultRuntimeContext();\n const { cwd } = runtimeCtx;\n\n let cfg;\n try {\n cfg = loadConfig({ scope: 'project', strict: opts.strict, ...runtimeCtx }).effective;\n } catch (err) {\n const message = formatErrorMessage(err);\n context.stderr.write(tx(WATCH_TEXTS.configLoadFailure, { message }));\n return ExitCode.Error;\n }\n\n const ignoreFileText = readIgnoreFileText(cwd);\n const ignoreFilterOpts: Parameters<typeof buildIgnoreFilter>[0] = {};\n if (cfg.ignore.length > 0) ignoreFilterOpts.configIgnore = cfg.ignore;\n if (ignoreFileText !== undefined) ignoreFilterOpts.ignoreFileText = ignoreFileText;\n const ignoreFilter = buildIgnoreFilter(ignoreFilterOpts);\n\n const strict = opts.strict || cfg.scan.strict === true;\n const debounceMs = cfg.scan.watch.debounceMs;\n const dbPath = defaultProjectDbPath(runtimeCtx);\n\n // Plugin discovery once at startup. Per-batch reuse avoids re-scanning\n // the plugins directory on every FS event; a hot reload of plugin code\n // requires restarting the watcher (Step 9.1; reload-on-change can be\n // a future polish if it shows up in real workflows).\n const pluginRuntime = opts.noPlugins\n ? emptyPluginRuntime()\n : await loadPluginRuntime({ scope: 'project' });\n for (const warn of pluginRuntime.warnings) {\n context.stderr.write(`${warn}\\n`);\n }\n\n // One scan pass with cache reuse + persist + render. Branching is\n // intrinsic to the watcher's per-batch lifecycle.\n // eslint-disable-next-line complexity\n const runOnePass = async (): Promise<void> => {\n const kernel = createKernel();\n const enabledBuiltIns = filterBuiltInManifests(listBuiltIns(), pluginRuntime.resolveEnabled);\n for (const manifest of enabledBuiltIns) kernel.registry.register(manifest);\n for (const manifest of pluginRuntime.manifests) kernel.registry.register(manifest);\n\n // Read prior snapshot AND prior `scan_extractor_runs` in a single\n // ephemeral open. Both feed the orchestrator's incremental path —\n // splitting them into two opens would re-run migration discovery\n // for nothing.\n const priorState = await tryWithSqlite(\n { databasePath: dbPath, autoBackup: false },\n async (reader) => {\n const loaded = await reader.scans.load();\n if (loaded.nodes.length === 0) return null;\n // H6 — under `--strict`, validate the prior against\n // `scan-result.schema.json` before handing it to the\n // orchestrator. The watcher's outer try/catch (initial scan)\n // and per-batch try/catch surface the throw with their usual\n // `sm watch: ... failed — ...` framing.\n if (strict) {\n const validators = loadSchemaValidators();\n const result = validators.validate('scan-result', loaded);\n if (!result.ok) {\n throw new Error(tx(WATCH_TEXTS.priorSchemaValidationFailed, { errors: result.errors }));\n }\n }\n const extractorRuns = await reader.scans.loadExtractorRuns();\n return { snapshot: loaded, extractorRuns };\n },\n );\n const priorSnapshot = priorState?.snapshot ?? null;\n const priorExtractorRuns = priorState?.extractorRuns;\n\n const composed = composeScanExtensions({ noBuiltIns: false, pluginRuntime });\n const runOptions: Parameters<typeof runScanWithRenames>[1] = {\n roots: opts.roots,\n scope: 'project',\n tokenize: !opts.noTokens,\n ignoreFilter,\n strict,\n emitter: createCliProgressEmitter(context.stderr),\n };\n if (composed) runOptions.extensions = composed;\n if (priorSnapshot) {\n runOptions.priorSnapshot = priorSnapshot;\n // The watcher always wants cache reuse — re-walking unchanged\n // files on every batch defeats the point of debouncing.\n runOptions.enableCache = true;\n }\n if (priorExtractorRuns) runOptions.priorExtractorRuns = priorExtractorRuns;\n\n let result: ScanResult;\n let renameOps: RenameOp[];\n let extractorRuns: IExtractorRunRecord[];\n let enrichments: IEnrichmentRecord[];\n try {\n const ran = await runScanWithRenames(kernel, runOptions);\n result = ran.result;\n renameOps = ran.renameOps;\n extractorRuns = ran.extractorRuns;\n enrichments = ran.enrichments;\n } catch (err) {\n const message = formatErrorMessage(err);\n context.stderr.write(tx(WATCH_TEXTS.scanFailed, { message }));\n return;\n }\n\n await withSqlite({ databasePath: dbPath }, (writer) =>\n writer.scans.persist(result, { renameOps, extractorRuns, enrichments }),\n );\n\n if (opts.json) {\n context.stdout.write(JSON.stringify(result) + '\\n');\n } else {\n context.stdout.write(\n tx(WATCH_TEXTS.scannedSummary, {\n nodes: result.stats.nodesCount,\n links: result.stats.linksCount,\n issues: result.stats.issuesCount,\n durationMs: result.stats.durationMs,\n }),\n );\n }\n };\n\n // 1. Initial scan so the DB matches current FS before we subscribe.\n if (!opts.json) {\n context.stderr.write(tx(WATCH_TEXTS.starting, { rootsCount: opts.roots.length, debounceMs }));\n }\n try {\n await runOnePass();\n } catch (err) {\n const message = formatErrorMessage(err);\n context.stderr.write(tx(WATCH_TEXTS.initialScanFailed, { message }));\n return ExitCode.Error;\n }\n\n // 2. Subscribe.\n let batchCount = 0;\n let stopRequested = false;\n let stopResolve: (() => void) | null = null;\n const stopped = new Promise<void>((r) => {\n stopResolve = r;\n });\n\n const watcher = createChokidarWatcher({\n roots: opts.roots,\n cwd,\n debounceMs,\n ignoreFilter,\n onBatch: async () => {\n if (stopRequested) return;\n batchCount++;\n try {\n await runOnePass();\n } catch (err) {\n const message = formatErrorMessage(err);\n context.stderr.write(tx(WATCH_TEXTS.batchFailed, { message }));\n }\n if (opts.maxBatches !== undefined && batchCount >= opts.maxBatches) {\n stopRequested = true;\n stopResolve?.();\n }\n },\n onError: (err) => {\n context.stderr.write(tx(WATCH_TEXTS.watcherError, { message: err.message }));\n },\n });\n\n // 3. Wire SIGINT / SIGTERM. Storing the handlers so we can clear them\n // on close — important for tests that spin a watcher up and down\n // multiple times in the same process.\n const onSignal = (): void => {\n if (stopRequested) return;\n stopRequested = true;\n stopResolve?.();\n };\n process.once('SIGINT', onSignal);\n process.once('SIGTERM', onSignal);\n\n await watcher.ready;\n if (!opts.json) {\n context.stderr.write(WATCH_TEXTS.ready);\n }\n\n await stopped;\n process.removeListener('SIGINT', onSignal);\n process.removeListener('SIGTERM', onSignal);\n await watcher.close();\n\n if (!opts.json) {\n context.stderr.write(tx(WATCH_TEXTS.stopped, { batchCount }));\n }\n return ExitCode.Ok;\n}\n\nexport class WatchCommand extends Command {\n static override paths = [['watch']];\n\n static override usage = Command.Usage({\n category: 'Scan',\n description: 'Watch roots and run an incremental scan after each debounced batch of filesystem events.',\n details: `\n Long-running version of 'sm scan --changed'. Subscribes to the\n given roots via chokidar, applies the same ignore chain\n (.skill-mapignore + config.ignore + bundled defaults), and\n triggers an incremental scan after each debounced batch.\n\n Default debounce is 300ms; configure via 'scan.watch.debounceMs'\n in .skill-map/settings.json. SIGINT / SIGTERM stop the watcher\n cleanly and exit 0.\n\n Under --json, every batch emits one ScanResult as ndjson on\n stdout. Without --json, every batch prints one summary line.\n\n 'sm scan --watch' is an alias and shares the same flag surface.\n `,\n examples: [\n ['Watch the current directory', '$0 watch'],\n ['Watch multiple roots', '$0 watch ./docs ./skills'],\n ['Stream ScanResult per batch as ndjson', '$0 watch --json'],\n ],\n });\n\n roots = Option.Rest({ name: 'roots' });\n json = Option.Boolean('--json', false, {\n description: 'Emit one ScanResult document per batch as ndjson on stdout.',\n });\n noTokens = Option.Boolean('--no-tokens', false, {\n description: 'Skip per-node token counts (cl100k_base BPE).',\n });\n strict = Option.Boolean('--strict', false, {\n description: 'Promote frontmatter-validation findings from warn to error inside each batch. Does not change the watcher exit code.',\n });\n noPlugins = Option.Boolean('--no-plugins', false, {\n description: 'Skip drop-in plugin discovery for the watcher session.',\n });\n\n async execute(): Promise<number> {\n const roots = this.roots.length > 0 ? this.roots : ['.'];\n return runWatchLoop({\n roots,\n json: this.json,\n noTokens: this.noTokens,\n strict: this.strict,\n noPlugins: this.noPlugins,\n context: this.context,\n });\n }\n}\n","/**\n * CLI strings emitted by `sm watch` (alias `sm scan --watch`) —\n * `cli/commands/watch.ts`.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const WATCH_TEXTS = {\n configLoadFailure: 'sm watch: {{message}}\\n',\n\n initialScanFailed: 'sm watch: initial scan failed — {{message}}\\n',\n\n batchFailed: 'sm watch: batch failed — {{message}}\\n',\n\n scanFailed: 'sm watch: scan failed — {{message}}\\n',\n\n watcherError: 'sm watch: watcher error — {{message}}\\n',\n\n starting: 'sm watch: starting on {{rootsCount}} root(s), debounce {{debounceMs}}ms\\n',\n\n ready: 'sm watch: ready. Press Ctrl+C to stop.\\n',\n\n stopped: 'sm watch: stopped after {{batchCount}} batch(es).\\n',\n\n scannedSummary:\n 'scanned {{nodes}} nodes / {{links}} links / {{issues}} issues in {{durationMs}}ms\\n',\n\n priorSchemaValidationFailed:\n 'prior scan-result loaded from DB failed schema validation: {{errors}}',\n} as const;\n","/**\n * `sm scan compare-with <dump> [roots...]` — read-only delta between a\n * fresh scan and a saved `ScanResult` JSON dump.\n *\n * Step 8.2 originally shipped this surface as `sm scan --compare-with\n * <dump>`. It got promoted to a proper sub-verb pre-1.0 (M1 review\n * finding) because the flag combinatorics were getting noisy: the\n * compare-with flow disables persistence, ignores `--changed`,\n * `--no-built-ins`, `--allow-empty`, conflicts with `--watch`. Every\n * one of those checks lived in `scan.ts`'s `execute()` as a runtime\n * guard against the wrong-flag-combo. As a verb the conflicts are\n * structural: a flag that does not belong to `scan compare-with` cannot\n * be passed in the first place.\n *\n * Flow:\n *\n * 1. Load the dump from `<dump>` and AJV-validate it against\n * `scan-result.schema.json`. A missing file, malformed JSON, or\n * a schema-violating dump → exit 2.\n * 2. Run a fresh scan in memory using the same wiring as `sm scan`\n * (built-ins, plugin runtime gated by `--no-plugins`, layered\n * config + ignore filter). The result is NOT persisted — this\n * verb is read-only.\n * 3. Compute the delta against the dump and emit it.\n *\n * Exit codes:\n * 0 empty delta — current state matches the dump.\n * 1 delta has at least one added / removed / changed item.\n * 2 operational error (dump load failure, scan failure, config\n * load failure).\n *\n * Typical use case: CI guard against drift. Freeze a baseline at merge\n * to main (`sm scan --json > .skill-map/baseline.json`); on every PR,\n * `sm scan compare-with .skill-map/baseline.json` — exit 1 trips the\n * build before the drift lands.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\n\nimport { Command, Option } from 'clipanion';\n\nimport { computeScanDelta, createKernel, isEmptyDelta, runScan } from '../../kernel/index.js';\nimport type { IScanDelta, ScanResult } from '../../kernel/index.js';\nimport { listBuiltIns } from '../../built-in-plugins/built-ins.js';\nimport { loadSchemaValidators } from '../../kernel/adapters/schema-validators.js';\nimport { loadConfig } from '../../kernel/config/loader.js';\nimport { buildIgnoreFilter, readIgnoreFileText } from '../../kernel/scan/ignore.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { sanitizeForTerminal } from '../../kernel/util/safe-text.js';\nimport { SCAN_TEXTS } from '../i18n/scan.texts.js';\nimport { createCliProgressEmitter } from '../util/cli-progress-emitter.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport { formatErrorMessage } from '../util/error-reporter.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\nimport {\n composeScanExtensions,\n emptyPluginRuntime,\n filterBuiltInManifests,\n loadPluginRuntime,\n} from '../util/plugin-runtime.js';\n\nexport class ScanCompareCommand extends Command {\n static override paths = [['scan', 'compare-with']];\n\n static override usage = Command.Usage({\n category: 'Scan',\n description:\n 'Run a fresh scan in memory and emit a delta against the saved ScanResult dump at <dump>. Read-only.',\n details: `\n Loads the JSON dump at <dump>, AJV-validates it against\n scan-result.schema.json, runs a fresh scan over [roots...]\n (default: current directory) using the same pipeline as 'sm scan'\n (built-ins + plugin runtime + layered config + ignore filter),\n and emits the delta between the dump and the fresh scan. The DB\n is NEVER touched — this verb is read-only.\n\n Exit 0 on empty delta (state matches the dump), exit 1 on any\n drift (added / removed / changed nodes, links, or issues), exit\n 2 on operational error (missing or malformed dump, schema\n violation, config / scan failure).\n\n Typical use case: CI guard. Freeze a baseline at merge to main:\n sm scan --json > .skill-map/baseline.json\n And on every PR, before the merge:\n sm scan compare-with .skill-map/baseline.json\n Any drift trips the build.\n `,\n examples: [\n ['Compare against a baseline', '$0 scan compare-with .skill-map/baseline.json'],\n ['Compare a specific subtree', '$0 scan compare-with baseline.json src/'],\n ['JSON output for tooling', '$0 scan compare-with baseline.json --json'],\n ],\n });\n\n dump = Option.String({ required: true });\n roots = Option.Rest({ name: 'roots' });\n json = Option.Boolean('--json', false, {\n description: 'Emit the IScanDelta document as JSON on stdout.',\n });\n noTokens = Option.Boolean('--no-tokens', false, {\n description: 'Skip per-node token counts during the fresh scan.',\n });\n strict = Option.Boolean('--strict', false, {\n description:\n 'Promote layered-config warnings and frontmatter-validation findings from warn to error.',\n });\n noPlugins = Option.Boolean('--no-plugins', false, {\n description: 'Skip drop-in plugin discovery.',\n });\n\n // Cyclomatic count comes from CLI ergonomics: 3 distinct try/catch\n // (dump load, config load, scan run) + flag-default handling + ternary\n // for the JSON branch. The pure pieces already live in\n // `loadAndValidateDump` and `computeScanDelta`.\n // eslint-disable-next-line complexity\n async execute(): Promise<number> {\n const ctx = defaultRuntimeContext();\n const roots = this.roots.length > 0 ? this.roots : ['.'];\n\n // 1. Load + validate the dump. Errors here are operational (exit 2)\n // — a missing file, malformed JSON, or a schema-violating dump\n // are all problems with the caller's input, not with the project\n // state.\n let prior: ScanResult;\n try {\n prior = loadAndValidateDump(this.dump);\n } catch (err) {\n const message = formatErrorMessage(err);\n this.context.stderr.write(tx(SCAN_TEXTS.compareErrorPrefix, { message }));\n return ExitCode.Error;\n }\n\n // 2. Run a fresh scan with the same wiring as the normal `sm scan`\n // code path. Skip persistence — this verb is read-only.\n const kernel = createKernel();\n const pluginRuntime = this.noPlugins\n ? emptyPluginRuntime()\n : await loadPluginRuntime({ scope: 'project' });\n for (const warn of pluginRuntime.warnings) this.context.stderr.write(`${warn}\\n`);\n const enabledBuiltIns = filterBuiltInManifests(listBuiltIns(), pluginRuntime.resolveEnabled);\n for (const manifest of enabledBuiltIns) kernel.registry.register(manifest);\n for (const manifest of pluginRuntime.manifests) kernel.registry.register(manifest);\n\n let cfg;\n try {\n const loaded = loadConfig({ scope: 'project', strict: this.strict, cwd: ctx.cwd, homedir: ctx.homedir });\n // Mirror `cli/commands/config.ts`: surface the layered loader's\n // accumulated warnings to stderr so the user sees malformed JSON /\n // unknown keys here too. Without this forward the verb silently\n // discarded them.\n for (const w of loaded.warnings) this.context.stderr.write(w + '\\n');\n cfg = loaded.effective;\n } catch (err) {\n const message = formatErrorMessage(err);\n this.context.stderr.write(tx(SCAN_TEXTS.compareErrorPrefix, { message }));\n return ExitCode.Error;\n }\n const ignoreFileText = readIgnoreFileText(ctx.cwd);\n const ignoreFilterOpts: Parameters<typeof buildIgnoreFilter>[0] = {};\n if (cfg.ignore.length > 0) ignoreFilterOpts.configIgnore = cfg.ignore;\n if (ignoreFileText !== undefined) ignoreFilterOpts.ignoreFileText = ignoreFileText;\n const ignoreFilter = buildIgnoreFilter(ignoreFilterOpts);\n const effectiveStrict = this.strict || cfg.scan.strict === true;\n\n const composedExtensions = composeScanExtensions({ noBuiltIns: false, pluginRuntime });\n let current: ScanResult;\n try {\n const compareRunOpts: Parameters<typeof runScan>[1] = {\n roots,\n scope: 'project',\n tokenize: !this.noTokens,\n ignoreFilter,\n strict: effectiveStrict,\n emitter: createCliProgressEmitter(this.context.stderr),\n };\n if (composedExtensions) compareRunOpts.extensions = composedExtensions;\n current = await runScan(kernel, compareRunOpts);\n } catch (err) {\n const message = formatErrorMessage(err);\n this.context.stderr.write(tx(SCAN_TEXTS.compareErrorPrefix, { message }));\n return ExitCode.Error;\n }\n\n // 3. Compute + render the delta. Exit 1 iff something diverged.\n const delta = computeScanDelta(prior, current, this.dump);\n const exitCode = isEmptyDelta(delta) ? ExitCode.Ok : ExitCode.Issues;\n\n if (this.json) {\n this.context.stdout.write(JSON.stringify(delta) + '\\n');\n return exitCode;\n }\n this.context.stdout.write(renderDeltaHuman(delta));\n return exitCode;\n }\n}\n\nfunction loadAndValidateDump(path: string): ScanResult {\n if (!existsSync(path)) {\n throw new Error(tx(SCAN_TEXTS.compareDumpNotFound, { path }));\n }\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch (err) {\n const message = formatErrorMessage(err);\n throw new Error(tx(SCAN_TEXTS.compareDumpReadFailed, { path, message }), { cause: err });\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n const message = formatErrorMessage(err);\n throw new Error(tx(SCAN_TEXTS.compareDumpInvalidJson, { message }), { cause: err });\n }\n const validators = loadSchemaValidators();\n const result = validators.validate<ScanResult>('scan-result', parsed);\n if (!result.ok) {\n throw new Error(tx(SCAN_TEXTS.compareDumpSchemaMismatch, { errors: result.errors }));\n }\n return result.data;\n}\n\nfunction renderDeltaHuman(delta: IScanDelta): string {\n const out: string[] = [];\n const totalAdded = delta.nodes.added.length + delta.links.added.length + delta.issues.added.length;\n const totalRemoved = delta.nodes.removed.length + delta.links.removed.length + delta.issues.removed.length;\n const totalChanged = delta.nodes.changed.length;\n\n out.push(\n tx(SCAN_TEXTS.compareDeltaSummary, {\n comparedWith: delta.comparedWith,\n nodesAdded: delta.nodes.added.length,\n nodesRemoved: delta.nodes.removed.length,\n nodesChanged: delta.nodes.changed.length,\n linksAdded: delta.links.added.length,\n linksRemoved: delta.links.removed.length,\n issuesAdded: delta.issues.added.length,\n issuesRemoved: delta.issues.removed.length,\n }),\n );\n\n if (totalAdded === 0 && totalRemoved === 0 && totalChanged === 0) {\n out.push('', SCAN_TEXTS.compareDeltaNoDifferences);\n return out.join('\\n') + '\\n';\n }\n\n out.push(...renderDeltaNodes(delta.nodes));\n out.push(...renderDeltaLinks(delta.links));\n out.push(...renderDeltaIssues(delta.issues));\n return out.join('\\n') + '\\n';\n}\n\nfunction renderDeltaNodes(nodes: IScanDelta['nodes']): string[] {\n if (nodes.added.length + nodes.removed.length + nodes.changed.length === 0) return [];\n const lines: string[] = ['', SCAN_TEXTS.compareDeltaNodesHeader];\n for (const n of nodes.added) {\n lines.push(tx(SCAN_TEXTS.compareDeltaNodeAdded, {\n path: sanitizeForTerminal(n.path),\n kind: sanitizeForTerminal(n.kind),\n }));\n }\n for (const n of nodes.removed) {\n lines.push(tx(SCAN_TEXTS.compareDeltaNodeRemoved, {\n path: sanitizeForTerminal(n.path),\n kind: sanitizeForTerminal(n.kind),\n }));\n }\n for (const c of nodes.changed) {\n lines.push(tx(SCAN_TEXTS.compareDeltaNodeChanged, {\n path: sanitizeForTerminal(c.after.path),\n reason: c.reason,\n }));\n }\n return lines;\n}\n\nfunction renderDeltaLinks(links: IScanDelta['links']): string[] {\n if (links.added.length + links.removed.length === 0) return [];\n const lines: string[] = ['', SCAN_TEXTS.compareDeltaLinksHeader];\n for (const l of links.added) {\n lines.push(tx(SCAN_TEXTS.compareDeltaLinkAdded, {\n source: sanitizeForTerminal(l.source),\n kind: sanitizeForTerminal(l.kind),\n target: sanitizeForTerminal(l.target),\n }));\n }\n for (const l of links.removed) {\n lines.push(tx(SCAN_TEXTS.compareDeltaLinkRemoved, {\n source: sanitizeForTerminal(l.source),\n kind: sanitizeForTerminal(l.kind),\n target: sanitizeForTerminal(l.target),\n }));\n }\n return lines;\n}\n\nfunction renderDeltaIssues(issues: IScanDelta['issues']): string[] {\n if (issues.added.length + issues.removed.length === 0) return [];\n const lines: string[] = ['', SCAN_TEXTS.compareDeltaIssuesHeader];\n for (const i of issues.added) {\n lines.push(tx(SCAN_TEXTS.compareDeltaIssueAdded, {\n severity: i.severity,\n ruleId: sanitizeForTerminal(i.ruleId),\n message: sanitizeForTerminal(i.message),\n }));\n }\n for (const i of issues.removed) {\n lines.push(tx(SCAN_TEXTS.compareDeltaIssueRemoved, {\n severity: i.severity,\n ruleId: sanitizeForTerminal(i.ruleId),\n message: sanitizeForTerminal(i.message),\n }));\n }\n return lines;\n}\n","/**\n * `sm show <node.path> [--json]`\n *\n * Detail view for a single node: weight (bytes/tokens triple-split),\n * frontmatter, links in/out, current issues. `--json` emits a detail\n * object with `node`, `linksOut`, `linksIn`, `issues`, plus the future\n * `findings` (Step 10) and `summary` (Step 11) slots reserved as\n * `[]` / `null` so consumers don't break when those land.\n *\n * Exit codes (per `spec/cli-contract.md` §Exit codes):\n * 0 ok\n * 2 bad flag\n * 5 node not found in scan_nodes (or the DB file is missing)\n */\n\nimport { Command, Option } from 'clipanion';\n\nimport type { Issue, Link, Node } from '../../kernel/types.js';\nimport { assertDbExists, resolveDbPath } from '../util/db-path.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport { withSqlite } from '../util/with-sqlite.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { sanitizeForTerminal } from '../../kernel/util/safe-text.js';\nimport { SHOW_TEXTS } from '../i18n/show.texts.js';\n\ninterface IShowDocument {\n node: Node;\n linksOut: Link[];\n linksIn: Link[];\n issues: Issue[];\n // TODO Step 10: populate from `state_findings` once the table lands.\n findings: never[];\n // TODO Step 11: populate from `state_summaries` once summarisers ship.\n summary: null;\n}\n\nexport class ShowCommand extends Command {\n static override paths = [['show']];\n static override usage = Command.Usage({\n category: 'Browse',\n description: 'Node detail: weight, frontmatter, links, issues, findings, summary.',\n details: `\n Loads a single node from the persisted snapshot, plus every link\n (in and out) and every current issue touching it. Findings and\n summaries are reserved slots and remain empty / null until the\n Step 10 / Step 11 features land.\n\n Run \\`sm scan\\` first to populate the DB.\n `,\n examples: [\n ['Show a single node', '$0 show .claude/agents/architect.md'],\n ['Machine-readable detail', '$0 show .claude/agents/architect.md --json'],\n ],\n });\n\n nodePath = Option.String({ required: true });\n global = Option.Boolean('-g,--global', false);\n db = Option.String('--db', { required: false });\n json = Option.Boolean('--json', false);\n\n async execute(): Promise<number> {\n const dbPath = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });\n if (!assertDbExists(dbPath, this.context.stderr)) return ExitCode.NotFound;\n\n return withSqlite({ databasePath: dbPath, autoBackup: false }, async (adapter) => {\n const bundle = await adapter.scans.findNode(this.nodePath);\n if (!bundle) {\n this.context.stderr.write(tx(SHOW_TEXTS.nodeNotFound, { nodePath: this.nodePath }));\n return ExitCode.NotFound;\n }\n\n const doc: IShowDocument = {\n node: bundle.node,\n linksOut: bundle.linksOut,\n linksIn: bundle.linksIn,\n issues: bundle.issues,\n findings: [],\n summary: null,\n };\n\n if (this.json) {\n this.context.stdout.write(JSON.stringify(doc) + '\\n');\n return ExitCode.Ok;\n }\n\n this.context.stdout.write(renderHuman(doc));\n return ExitCode.Ok;\n });\n }\n}\n\n// --- human renderer -------------------------------------------------------\n\n/**\n * Render one \"Links out\" / \"Links in\" section: aggregated count\n * header, `(none)` placeholder, or one line per grouped link with the\n * directional arrow. Used for both directions in `renderHuman`.\n */\nfunction renderLinksSection(\n label: string,\n links: Link[],\n projectField: 'target' | 'source',\n arrow: '→' | '←',\n): string[] {\n const aggregated = aggregateLinks(links, projectField);\n const lines: string[] = [\n '',\n tx(SHOW_TEXTS.sectionHeader, { label, count: links.length, unique: aggregated.length }),\n ];\n if (aggregated.length === 0) {\n lines.push(SHOW_TEXTS.placeholderNone);\n } else {\n for (const grp of aggregated) lines.push(formatGroupedLink(arrow, grp));\n }\n return lines;\n}\n\nfunction renderHuman(doc: IShowDocument): string {\n const { node, linksOut, linksIn, issues } = doc;\n const out: string[] = [];\n out.push(...renderNodeHeader(node));\n out.push('', SHOW_TEXTS.sectionFrontmatter);\n out.push(indent(JSON.stringify(node.frontmatter ?? {}, null, 2), 2));\n out.push(...renderLinksSection(SHOW_TEXTS.sectionLinksOut, linksOut, 'target', '→'));\n out.push(...renderLinksSection(SHOW_TEXTS.sectionLinksIn, linksIn, 'source', '←'));\n out.push(...renderIssuesSection(issues));\n // findings + summary intentionally omitted until the Step 10 / 11\n // features land — keeping an empty section header would mislead.\n return out.join('\\n') + '\\n';\n}\n\n/**\n * Header block: id line + each present optional field on its own row +\n * weight + token line + external refs counter. Optional fields are\n * gated individually so missing ones don't render as empty rows.\n */\nfunction renderNodeHeader(node: Node): string[] {\n const lines: string[] = [];\n lines.push(\n tx(SHOW_TEXTS.nodeIdentity, {\n path: sanitizeForTerminal(node.path),\n kind: sanitizeForTerminal(node.kind),\n provider: sanitizeForTerminal(node.provider),\n }),\n );\n if (node.title) lines.push(tx(SHOW_TEXTS.nodeFieldTitle, { value: sanitizeForTerminal(node.title) }));\n if (node.description) lines.push(tx(SHOW_TEXTS.nodeFieldDescription, { value: sanitizeForTerminal(node.description) }));\n if (node.stability) lines.push(tx(SHOW_TEXTS.nodeFieldStability, { value: sanitizeForTerminal(node.stability) }));\n if (node.version) lines.push(tx(SHOW_TEXTS.nodeFieldVersion, { value: sanitizeForTerminal(node.version) }));\n if (node.author) lines.push(tx(SHOW_TEXTS.nodeFieldAuthor, { value: sanitizeForTerminal(node.author) }));\n const b = node.bytes;\n lines.push(tx(SHOW_TEXTS.nodeWeight, { total: b.total, frontmatter: b.frontmatter, body: b.body }));\n if (node.tokens) {\n const t = node.tokens;\n lines.push(tx(SHOW_TEXTS.nodeTokens, { total: t.total, frontmatter: t.frontmatter, body: t.body }));\n }\n // Render even when 0 — \"External refs: 0\" is information, not noise.\n lines.push(tx(SHOW_TEXTS.nodeExternalRefs, { count: node.externalRefsCount }));\n return lines;\n}\n\n/** Issues block: header line + `(none)` placeholder or one bullet per issue. */\nfunction renderIssuesSection(issues: Issue[]): string[] {\n const lines: string[] = ['', tx(SHOW_TEXTS.issuesHeader, { count: issues.length })];\n if (issues.length === 0) {\n lines.push(SHOW_TEXTS.placeholderNone);\n } else {\n for (const issue of issues) {\n lines.push(\n tx(SHOW_TEXTS.issueRow, {\n severity: issue.severity,\n ruleId: sanitizeForTerminal(issue.ruleId),\n message: sanitizeForTerminal(issue.message),\n }),\n );\n }\n }\n return lines;\n}\n\nfunction indent(s: string, spaces: number): string {\n const pad = ' '.repeat(spaces);\n return s\n .split('\\n')\n .map((line) => (line.length > 0 ? pad + line : line))\n .join('\\n');\n}\n\ninterface IGroupedLink {\n /** The \"other end\" path: target for outgoing groups, source for incoming. */\n endpoint: string;\n kind: Link['kind'];\n /** Highest confidence across the group (rank: high > medium > low). */\n confidence: Link['confidence'];\n /** Union of all extractor ids that emitted any row in the group, sorted. */\n sources: string[];\n /** Original row count — informational, mirrors what `linksOut.length` showed before grouping. */\n rowCount: number;\n /** Trigger normalized form, when every row in the group agrees on it. `null` when the trigger is absent or differs. */\n normalizedTrigger: string | null;\n}\n\n/**\n * Group a flat link array by `(endpoint, kind, normalizedTrigger or null)`.\n * Used by the human renderer to collapse rows emitted by multiple\n * extractors for the same conceptual link into a single line. Storage\n * keeps the raw rows; `--json` emits them unchanged.\n *\n * `endpointSide` picks which end of the link is the \"other\" node:\n * `'target'` for outgoing links, `'source'` for incoming.\n */\n// eslint-disable-next-line complexity\nfunction aggregateLinks(links: Link[], endpointSide: 'target' | 'source'): IGroupedLink[] {\n const groups = new Map<string, IGroupedLink>();\n for (const link of links) {\n const endpoint = endpointSide === 'target' ? link.target : link.source;\n const trigger = link.trigger?.normalizedTrigger ?? null;\n // NUL separator — collision-free against any path (POSIX paths\n // cannot contain NUL) or trigger string. The null-trigger case\n // gets its own bucket key via the empty trailing component.\n const key = `${endpoint}\\x00${link.kind}\\x00${trigger ?? ''}`;\n const existing = groups.get(key);\n if (existing) {\n for (const src of link.sources) {\n if (!existing.sources.includes(src)) existing.sources.push(src);\n }\n if (rankConfidenceForGrouping(link.confidence) > rankConfidenceForGrouping(existing.confidence)) {\n existing.confidence = link.confidence;\n }\n existing.rowCount += 1;\n } else {\n groups.set(key, {\n endpoint,\n kind: link.kind,\n confidence: link.confidence,\n sources: [...link.sources],\n rowCount: 1,\n normalizedTrigger: trigger,\n });\n }\n }\n // Deterministic order: by endpoint, then kind. Sources inside each\n // group are sorted at the moment we render so additions during\n // grouping don't pay a sort per insert.\n for (const grp of groups.values()) grp.sources.sort();\n return [...groups.values()].sort((a, b) => {\n if (a.endpoint !== b.endpoint) return a.endpoint.localeCompare(b.endpoint);\n return a.kind.localeCompare(b.kind);\n });\n}\n\nfunction formatGroupedLink(arrow: '→' | '←', grp: IGroupedLink): string {\n const dup = grp.rowCount > 1\n ? tx(SHOW_TEXTS.groupedLinkDup, { count: grp.rowCount })\n : '';\n const sources = grp.sources.length > 0\n ? tx(SHOW_TEXTS.groupedLinkSources, {\n values: grp.sources.map(sanitizeForTerminal).join(', '),\n })\n : '';\n return tx(SHOW_TEXTS.groupedLinkHead, {\n kind: sanitizeForTerminal(grp.kind),\n confidence: grp.confidence,\n arrow,\n endpoint: sanitizeForTerminal(grp.endpoint),\n dup,\n sources,\n });\n}\n\nconst CONFIDENCE_RANK: Record<Link['confidence'], number> = {\n high: 2,\n medium: 1,\n low: 0,\n};\n\nfunction rankConfidenceForGrouping(c: Link['confidence']): number {\n return CONFIDENCE_RANK[c];\n}\n","/**\n * Strings emitted by `cli/commands/show.ts`.\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const SHOW_TEXTS = {\n nodeNotFound: 'Node not found: {{nodePath}}\\n',\n\n // --- renderHuman labels ------------------------------------------------\n sectionFrontmatter: 'Frontmatter:',\n sectionLinksOut: 'Links out',\n sectionLinksIn: 'Links in',\n sectionIssues: 'Issues',\n placeholderNone: ' (none)',\n sectionHeader: '{{label}} ({{count}}, {{unique}} unique):',\n issuesHeader: 'Issues ({{count}}):',\n issueRow: ' - [{{severity}}] {{ruleId}}: {{message}}',\n\n // --- formatGroupedLink ------------------------------------------------\n /**\n * Bullet line for one grouped link in the in/out lists. `{{kind}}` and\n * `{{endpoint}}` are pre-sanitized by the caller; `{{dup}}` is the\n * `(×N)` count when the row collapses multiple identical edges, empty\n * otherwise; `{{sources}}` is the trailing ` sources: a, b` segment\n * (empty when the link has no sources).\n */\n groupedLinkHead:\n ' - [{{kind}}/{{confidence}}] {{arrow}} {{endpoint}}{{dup}}{{sources}}',\n groupedLinkDup: ' (×{{count}})',\n groupedLinkSources: ' sources: {{values}}',\n\n // --- renderNodeHeader labels ------------------------------------------\n nodeIdentity: '{{path}} [{{kind}}] (provider: {{provider}})',\n nodeFieldTitle: 'title: {{value}}',\n nodeFieldDescription: 'description: {{value}}',\n nodeFieldStability: 'stability: {{value}}',\n nodeFieldVersion: 'version: {{value}}',\n nodeFieldAuthor: 'author: {{value}}',\n nodeWeight: 'Weight: bytes {{total}} total / {{frontmatter}} frontmatter / {{body}} body',\n nodeTokens: ' tokens {{total}} total / {{frontmatter}} frontmatter / {{body}} body',\n nodeExternalRefs: 'External refs: {{count}}',\n} as const;\n","/**\n * Clipanion stubs for every verb from `spec/cli-contract.md` that has no\n * real implementation yet. Each stub:\n *\n * 1. Registers the same paths as the final command will (so `sm help` sees\n * the full surface today, and the CI drift check against\n * `context/cli-reference.md` works).\n * 2. Advertises its future home via the `category` / `description` /\n * `details` in the Usage block — this is what the Step 1c\n * introspection layer serialises to json / md. Every stub\n * description carries a `(planned)` suffix appended via\n * `planned()`, so `sm --help` users can tell at a glance which\n * verbs work today and which are reserved for future shipment.\n * 3. On execute, writes a one-liner to stderr (`<verb>: not yet\n * implemented (planned).`) and exits with code 2 (error / unhandled)\n * per spec/cli-contract.md §Exit codes.\n *\n * Why no Step number in user-facing strings: roadmap step numbers shift\n * (a Step 9 plan can be split into 9.1 / 9.2 / 9.3 mid-flight), and\n * stale promises in `--help` are a worse UX than no promise at all.\n * The `// Step N` comments scattered in this file ARE preserved as\n * dev hints; they're for whoever is reading the source, not for end\n * users.\n *\n * When a later Step replaces a stub, the replacement class takes over\n * the same paths and this file loses the entry. The ordering here\n * mirrors the contract's section order so a grep → stub mapping is\n * easy.\n */\n\nimport { Command, Option } from 'clipanion';\n\nimport { ExitCode, type TExitCode } from '../util/exit-codes.js';\nimport { tx } from '../../kernel/util/tx.js';\nimport { STUBS_TEXTS } from '../i18n/stubs.texts.js';\n\n/**\n * Tag a description as belonging to a planned-but-unimplemented verb.\n * Currently appends `(planned)` so the help output disambiguates\n * stubs from real verbs without committing to a release date.\n */\nfunction planned(description: string): string {\n return `${description} (planned)`;\n}\n\nfunction notImplemented(cmd: Command, verb: string): TExitCode {\n cmd.context.stderr.write(tx(STUBS_TEXTS.notImplemented, { verb }));\n return ExitCode.Error;\n}\n\n// ---------------------------------------------------------------------------\n// Setup & state\n// ---------------------------------------------------------------------------\n//\n// `sm init` left this file at Step 6.5; it lives in src/cli/commands/init.ts\n// now. `sm doctor` remains a stub until Step 3 (or whenever doctor lands).\n\nexport class DoctorCommand extends Command {\n static override paths = [['doctor']];\n static override usage = Command.Usage({\n category: 'Setup',\n description: planned('Diagnostic report: DB integrity, pending migrations, orphan rows, plugin status, runner availability.'),\n });\n\n async execute(): Promise<number> {\n // Step 3 territory.\n return notImplemented(this, 'doctor');\n }\n}\n\n// ---------------------------------------------------------------------------\n// Config — moved to ./config.ts at Step 6.3\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Browse\n// ---------------------------------------------------------------------------\n//\n// `sm list`, `sm show`, `sm check` left this file in Step 4.5; they live\n// in src/cli/commands/{list,show,check}.ts now. The remaining Browse\n// stubs (findings / graph / export / orphans*) ship in later Steps.\n\nexport class FindingsCommand extends Command {\n static override paths = [['findings']];\n static override usage = Command.Usage({\n category: 'Browse',\n description: planned('Probabilistic findings: injection, stale summaries, low confidence.'),\n });\n kind = Option.String('--kind', { required: false });\n since = Option.String('--since', { required: false });\n threshold = Option.String('--threshold', { required: false });\n json = Option.Boolean('--json', false);\n\n async execute(): Promise<number> {\n // Step 10 territory.\n return notImplemented(this, 'findings');\n }\n}\n\n// GraphCommand moved to ./graph.ts at Step 8.1.\n// ExportCommand moved to ./export.ts at Step 8.3.\n\n// orphans / orphans reconcile / orphans undo-rename — moved to ./orphans.ts\n// at Step 5.6\n\n// ---------------------------------------------------------------------------\n// Actions\n// ---------------------------------------------------------------------------\n\nexport class ActionsListCommand extends Command {\n static override paths = [['actions', 'list']];\n static override usage = Command.Usage({\n category: 'Jobs',\n description: planned('Registered action types (manifest view).'),\n });\n\n async execute(): Promise<number> {\n // Step 9 territory.\n return notImplemented(this, 'actions list');\n }\n}\n\nexport class ActionsShowCommand extends Command {\n static override paths = [['actions', 'show']];\n static override usage = Command.Usage({\n category: 'Jobs',\n description: planned('Full action manifest, including preconditions and expected duration.'),\n });\n id = Option.String({ required: true });\n\n async execute(): Promise<number> {\n // Step 9 territory.\n return notImplemented(this, 'actions show');\n }\n}\n\n// ---------------------------------------------------------------------------\n// Jobs\n// ---------------------------------------------------------------------------\n\nexport class JobSubmitCommand extends Command {\n static override paths = [['job', 'submit']];\n static override usage = Command.Usage({\n category: 'Jobs',\n description: planned('Enqueue a single job or fan out to every matching node (--all).'),\n });\n action = Option.String({ required: true });\n node = Option.String('-n', { required: false });\n all = Option.Boolean('--all', false);\n run = Option.Boolean('--run', false);\n force = Option.Boolean('--force', false);\n ttl = Option.String('--ttl', { required: false });\n priority = Option.String('--priority', { required: false });\n\n async execute(): Promise<number> {\n // Step 9 territory.\n return notImplemented(this, 'job submit');\n }\n}\n\nexport class JobListCommand extends Command {\n static override paths = [['job', 'list']];\n static override usage = Command.Usage({ category: 'Jobs', description: planned('List jobs.') });\n status = Option.String('--status', { required: false });\n action = Option.String('--action', { required: false });\n node = Option.String('--node', { required: false });\n\n async execute(): Promise<number> {\n // Step 9 territory.\n return notImplemented(this, 'job list');\n }\n}\n\nexport class JobShowCommand extends Command {\n static override paths = [['job', 'show']];\n static override usage = Command.Usage({ category: 'Jobs', description: planned('Job detail: state, claim time, TTL, runner, content hash.') });\n id = Option.String({ required: true });\n\n async execute(): Promise<number> {\n // Step 9 territory.\n return notImplemented(this, 'job show');\n }\n}\n\nexport class JobPreviewCommand extends Command {\n static override paths = [['job', 'preview']];\n static override usage = Command.Usage({ category: 'Jobs', description: planned('Render the job MD file without executing.') });\n id = Option.String({ required: true });\n\n async execute(): Promise<number> {\n // Step 9 territory.\n return notImplemented(this, 'job preview');\n }\n}\n\nexport class JobClaimCommand extends Command {\n static override paths = [['job', 'claim']];\n static override usage = Command.Usage({\n category: 'Jobs',\n description: planned('Atomic primitive: return next queued job id, mark it running.'),\n });\n filter = Option.String('--filter', { required: false });\n\n async execute(): Promise<number> {\n // Step 9 territory.\n return notImplemented(this, 'job claim');\n }\n}\n\nexport class JobRunCommand extends Command {\n static override paths = [['job', 'run']];\n static override usage = Command.Usage({\n category: 'Jobs',\n description: planned('Full CLI-runner loop: claim + spawn + record.'),\n });\n all = Option.Boolean('--all', false);\n max = Option.String('--max', { required: false });\n\n async execute(): Promise<number> {\n // Step 9 territory.\n return notImplemented(this, 'job run');\n }\n}\n\nexport class JobStatusCommand extends Command {\n static override paths = [['job', 'status']];\n static override usage = Command.Usage({\n category: 'Jobs',\n description: planned('Counts (per status) or single-job status.'),\n });\n id = Option.String({ required: false });\n\n async execute(): Promise<number> {\n // Step 9 territory.\n return notImplemented(this, 'job status');\n }\n}\n\nexport class JobCancelCommand extends Command {\n static override paths = [['job', 'cancel']];\n static override usage = Command.Usage({\n category: 'Jobs',\n description: planned('Force a running job to failed with reason user-cancelled.'),\n });\n id = Option.String({ required: false });\n all = Option.Boolean('--all', false);\n\n async execute(): Promise<number> {\n // Step 9 territory.\n return notImplemented(this, 'job cancel');\n }\n}\n\n// JobPruneCommand moved to ./jobs.ts (lands real in Step 7.3).\n\n// ---------------------------------------------------------------------------\n// Record (callback)\n// ---------------------------------------------------------------------------\n\nexport class RecordCommand extends Command {\n static override paths = [['record']];\n static override usage = Command.Usage({\n category: 'Jobs',\n description: planned('Close a running job with success or failure. Nonce is the sole credential.'),\n });\n id = Option.String('--id', { required: true });\n nonce = Option.String('--nonce', { required: true });\n status = Option.String('--status', { required: true });\n report = Option.String('--report', { required: false });\n tokensIn = Option.String('--tokens-in', { required: false });\n tokensOut = Option.String('--tokens-out', { required: false });\n durationMs = Option.String('--duration-ms', { required: false });\n model = Option.String('--model', { required: false });\n error = Option.String('--error', { required: false });\n\n async execute(): Promise<number> {\n // Step 9 territory.\n return notImplemented(this, 'record');\n }\n}\n\n// ---------------------------------------------------------------------------\n// History — moved to ./history.ts at Step 5.3 / 5.4\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Plugins — enable/disable moved to ./plugins.ts at Step 6.6\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Server\n// ---------------------------------------------------------------------------\n\nexport class ServeCommand extends Command {\n static override paths = [['serve']];\n static override usage = Command.Usage({\n category: 'Setup',\n description: planned('Start Hono + WebSocket for the Web UI. Single-port mandate: SPA + REST + WS under one listener.'),\n });\n port = Option.String('--port', { required: false });\n host = Option.String('--host', { required: false });\n noOpen = Option.Boolean('--no-open', false);\n\n async execute(): Promise<number> {\n // Step 12 territory.\n return notImplemented(this, 'serve');\n }\n}\n\n// ---------------------------------------------------------------------------\n// Aggregate export\n// ---------------------------------------------------------------------------\n\nexport const STUB_COMMANDS = [\n DoctorCommand,\n FindingsCommand,\n ActionsListCommand,\n ActionsShowCommand,\n JobSubmitCommand,\n JobListCommand,\n JobShowCommand,\n JobPreviewCommand,\n JobClaimCommand,\n JobRunCommand,\n JobStatusCommand,\n JobCancelCommand,\n RecordCommand,\n ServeCommand,\n];\n","/**\n * Strings emitted by `cli/commands/stubs.ts` (placeholder verbs that\n * aren't implemented yet).\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const STUBS_TEXTS = {\n notImplemented: '{{verb}}: not yet implemented (planned).\\n',\n} as const;\n","import { Command, Option } from 'clipanion';\n\nimport { tx } from '../../kernel/util/tx.js';\nimport { VERSION_TEXTS } from '../i18n/version.texts.js';\nimport { resolveDbPath } from '../util/db-path.js';\nimport { defaultRuntimeContext } from '../util/runtime-context.js';\nimport { ExitCode } from '../util/exit-codes.js';\nimport { VERSION } from '../version.js';\nimport { tryWithSqlite } from '../util/with-sqlite.js';\n\n/**\n * `sm version` — multi-line version matrix.\n *\n * Shape is defined in `spec/cli-contract.md`:\n *\n * sm <cli version>\n * kernel <kernel version>\n * spec <spec version implemented>\n * runtime Node v<n>.<n>.<n>\n * db-schema <applied migration version | —>\n *\n * `runtime` is rendered in human mode but absent from `--json` —\n * `cli-contract.md` § `sm version` lists exactly four JSON fields\n * (`{ sm, kernel, spec, dbSchema }`); the runtime line is\n * informational only and stays out of the machine surface to keep the\n * spec contract literal. Promoting it would require a spec PR + a\n * changeset.\n *\n * The Clipanion built-in `--version` flag remains for the single-line form.\n *\n * `db-schema` resolution:\n * - When the project DB file exists, the command opens it through\n * `StoragePort.migrations.currentSchemaVersion()` (which reads\n * `PRAGMA user_version`; the migrations runner keeps that pragma in\n * sync with the latest applied kernel migration).\n * - When the DB is absent, the field stays `—` (no scope provisioned\n * yet — typically pre-`sm init`).\n * - Any read failure is silenced into `—` rather than turned into an\n * error: `sm version` is informational and MUST NOT crash on a bad\n * DB file.\n */\nexport class VersionCommand extends Command {\n static override paths = [['version']];\n\n static override usage = Command.Usage({\n category: 'Introspection',\n description: 'Print the CLI / kernel / spec / runtime / db-schema version matrix.',\n });\n\n json = Option.Boolean('--json', false);\n\n async execute(): Promise<number> {\n const runtime = `Node ${process.version}`;\n const kernelVersion = VERSION;\n const specVersion = await resolveSpecVersion();\n const dbSchema = await resolveDbSchemaVersion();\n\n if (this.json) {\n // Spec § `sm version`: exactly `{ sm, kernel, spec, dbSchema }`.\n // `dbSchema` keeps the human-rendered `—` sentinel for \"no DB\n // yet\" so consumers branch on the literal once instead of having\n // to remember a separate JSON-only convention.\n const payload = {\n sm: VERSION,\n kernel: kernelVersion,\n spec: specVersion,\n dbSchema,\n };\n this.context.stdout.write(JSON.stringify(payload) + '\\n');\n return ExitCode.Ok;\n }\n\n const lines: Array<[string, string]> = [\n ['sm', VERSION],\n ['kernel', kernelVersion],\n ['spec', specVersion],\n ['runtime', runtime],\n ['db-schema', dbSchema],\n ];\n\n const pad = Math.max(...lines.map(([k]) => k.length)) + 2;\n for (const [k, v] of lines) {\n this.context.stdout.write(tx(VERSION_TEXTS.matrixRow, { key: k.padEnd(pad), value: v }));\n }\n return ExitCode.Ok;\n }\n}\n\nasync function resolveSpecVersion(): Promise<string> {\n try {\n const mod = await import('@skill-map/spec', { with: { type: 'json' } });\n const version = (mod as { default?: { specPackageVersion?: string } }).default\n ?.specPackageVersion;\n return version ?? 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\n/**\n * Resolve the project DB schema version through `StoragePort`.\n *\n * Failure modes (return `—` for all):\n * - DB file does not exist (no `sm init` yet — `tryWithSqlite`\n * short-circuits to `null` before opening the adapter, so no\n * `.skill-map/` directory is provisioned for an informational\n * read).\n * - DB file exists but cannot be opened (corrupt / permissions).\n * - PRAGMA returns null / non-numeric (engine quirk; never observed).\n */\nasync function resolveDbSchemaVersion(): Promise<string> {\n const dbPath = resolveDbPath({ global: false, db: undefined, ...defaultRuntimeContext() });\n try {\n const v = await tryWithSqlite({ databasePath: dbPath, autoBackup: false }, async (port) =>\n port.migrations.currentSchemaVersion(),\n );\n if (v === null || v === undefined) return '—';\n return String(v);\n } catch {\n return '—';\n }\n}\n","/**\n * CLI strings emitted by `sm version` (`cli/commands/version.ts`).\n *\n * Convention: flat string templates with `{{name}}` placeholders. The\n * `tx` helper at `kernel/util/tx.ts` does the interpolation.\n */\n\nexport const VERSION_TEXTS = {\n // One row of the human-mode version matrix. `key` is left-padded by the\n // command itself so the column width matches the widest label dynamically.\n matrixRow: '{{key}}{{value}}\\n',\n} as const;\n"],"mappings":"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","names":["Cli","resolve","join","existsSync","resolve","resolve","existsSync","join","ID","ID","ID","link","ID","link","ID","byPath","link","ID","ID","link","ID","link","readFileSync","dirname","resolve","resolve","readFileSync","require","dirname","ID","link","createRequire","existsSync","readFileSync","join","relative","resolve","Ajv2020","addFormatsModule","addFormats","addFormatsModule","existsSync","join","resolve","readFileSync","relative","Ajv2020","require","createRequire","existsSync","readFileSync","join","join","existsSync","readFileSync","msg","existsSync","mkdirSync","dirname","resolve","DatabaseSync","sql","sql","resolve","resolve","existsSync","readFileSync","readdirSync","dirname","join","resolve","DatabaseSync","fileURLToPath","dirname","fileURLToPath","resolve","existsSync","readdirSync","join","sql","readFileSync","DatabaseSync","existsSync","readFileSync","readdirSync","join","sql","FILE_RE","join","existsSync","readdirSync","sql","readFileSync","link","sql","sql","link","resolve","mkdirSync","dirname","DatabaseSync","path","sql","existsSync","resolve","existsSync","mkdirSync","readFileSync","dirname","join","Command","Option","join","deleteAtPath","existsSync","readFileSync","mkdirSync","dirname","Command","Option","existsSync","readFileSync","dirname","resolve","fileURLToPath","Command","Option","existsSync","readdirSync","readFileSync","isAbsolute","join","relative","resolve","readFileSync","join","exitCode","readdirSync","isAbsolute","resolve","relative","existsSync","existsSync","readdirSync","statSync","dirname","resolve","createRequire","fileURLToPath","resolveSpecRoot","require","dirname","fileURLToPath","resolve","existsSync","Command","Option","readFileSync","spawnSync","stat","dirname","join","resolve","DatabaseSync","Command","Option","stat","Command","Option","resolve","join","dirname","DatabaseSync","spawnSync","args","exitCode","Command","Option","link","KIND_ORDER","Command","Option","link","pickTitle","Command","Option","Command","Option","readFileSync","createRequire","resolve","Command","Option","Command","Option","renderMarkdown","createRequire","resolve","readFileSync","truncate","args","cli","mkdir","readFile","stat","join","Command","Option","existsSync","statSync","yaml","existsSync","statSync","link","args","pickMetadata","yaml","byPath","resolve","relative","sep","byPath","link","pathExists","stat","Command","Option","join","mkdir","homedir","readFile","Command","Option","Command","Option","sep","Command","Option","readdirSync","statSync","join","resolve","stat","Command","Option","Command","Option","Command","Option","renderTable","formatRow","sep","Command","Option","Command","Option","tx","issue","existsSync","join","resolve","Command","Option","resolveSearchPaths","homedir","resolve","Command","Option","builtIns","join","existsSync","bundle","readFile","resolve","Command","Option","isAbsolute","resolve","sep","assertContained","Command","Option","node","assertContained","readFile","resolve","Command","Option","Command","Option","result","extractorRuns","Command","Option","Command","Option","roots","result","exitCode","existsSync","readFileSync","Command","Option","Command","Option","exitCode","existsSync","readFileSync","Command","Option","Command","Option","renderHuman","link","Command","Option","Command","Option","Command","Option","Command","Option","resolveSpecVersion","Cli"]}
|