@skill-map/cli 0.63.0 → 0.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // cli/entry.ts
2
2
 
3
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="09459eb1-3d6f-565d-9552-4819baae6f1a")}catch(e){}}();
4
- import { existsSync as existsSync33 } from "fs";
3
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="43bb2367-1e14-5df2-9221-a5e7a877a666")}catch(e){}}();
4
+ import { existsSync as existsSync34 } from "fs";
5
5
  import { Builtins, Cli as Cli2 } from "clipanion";
6
6
 
7
7
  // kernel/adapters/in-memory-progress.ts
@@ -250,7 +250,7 @@ function bucketByKind(kind, instance, bag) {
250
250
  // package.json
251
251
  var package_default = {
252
252
  name: "@skill-map/cli",
253
- version: "0.63.0",
253
+ version: "0.64.0",
254
254
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
255
255
  license: "MIT",
256
256
  type: "module",
@@ -4560,6 +4560,25 @@ var ENTRY_TEXTS = {
4560
4560
  */
4561
4561
  bareNoProject: "{{glyph}} No skill-map project found in {{cwd}}.\n {{hint}}\n",
4562
4562
  bareNoProjectHint: "Run `sm init` to bootstrap one, or `sm --help` to see all commands.",
4563
+ /**
4564
+ * Hint variant for a bare `sm` in an EMPTY cwd when the interactive
4565
+ * menu cannot run (non-TTY stdin) or the operator gave no valid pick.
4566
+ * Points at the two getting-started verbs instead of `sm init` (a
4567
+ * brand-new user in an empty folder wants to try the tool, not
4568
+ * bootstrap an empty project).
4569
+ */
4570
+ bareEmptyHint: "Run `sm tutorial` for a guided walkthrough, or `sm example` to drop a project to explore.",
4571
+ /**
4572
+ * Getting-started menu shown on bare `sm` in an empty folder on an
4573
+ * interactive terminal. Header uses a yellow `?` glyph; two numbered
4574
+ * options dispatch to `sm tutorial` / `sm example`; the input line
4575
+ * accepts a number, a verb name, or an empty answer (which takes the
4576
+ * default, option 1, the tutorial).
4577
+ */
4578
+ emptyMenuHeader: "{{glyph}} This folder is empty. How would you like to start?",
4579
+ emptyMenuOptionTutorial: " 1) Run the guided tutorial (sm tutorial) (default)",
4580
+ emptyMenuOptionExample: " 2) Copy an example project to try (sm example)",
4581
+ emptyMenuInput: " Enter the number [default 1]: ",
4563
4582
  parseErrorHeadline: "sm: {{message}}",
4564
4583
  parseErrorUnknownOption: "unknown option '{{name}}'",
4565
4584
  parseErrorUnknownOptionForVerb: "{{verb}}: unknown option '{{name}}'",
@@ -4792,6 +4811,64 @@ function requireDbOrExit(path, stderr) {
4792
4811
  return ExitCode.NotFound;
4793
4812
  }
4794
4813
 
4814
+ // cli/util/empty-cwd.ts
4815
+ import { readdirSync } from "fs";
4816
+ function isDirEmpty(dir) {
4817
+ return readdirSync(dir).length === 0;
4818
+ }
4819
+ function listCwdEntries(dir) {
4820
+ const entries = readdirSync(dir).sort();
4821
+ const shown = entries.slice(0, 5);
4822
+ const more = entries.length > shown.length ? ", ..." : "";
4823
+ return shown.join(", ") + more;
4824
+ }
4825
+ function displayCwd(cwd) {
4826
+ const segments = cwd.split("/").filter((s) => s.length > 0);
4827
+ if (segments.length === 0) return "./";
4828
+ return `./${segments[segments.length - 1]}/`;
4829
+ }
4830
+
4831
+ // cli/util/empty-folder-prompt.ts
4832
+ import { createInterface } from "readline";
4833
+ async function decideBareNoArgs(state, prompt) {
4834
+ if (state.hasDb) return { kind: "route", argv: ["serve"] };
4835
+ if (state.isTty && state.isEmptyDir) {
4836
+ const choice = await prompt();
4837
+ if (choice !== null) return { kind: "route", argv: [choice] };
4838
+ }
4839
+ return { kind: "hint" };
4840
+ }
4841
+ function classifyEmptyFolderAnswer(trimmed) {
4842
+ if (trimmed === "") return "tutorial";
4843
+ if (trimmed === "1") return "tutorial";
4844
+ if (trimmed === "2") return "example";
4845
+ const lower = trimmed.toLowerCase();
4846
+ if (lower === "tutorial") return "tutorial";
4847
+ if (lower === "example") return "example";
4848
+ return null;
4849
+ }
4850
+ async function promptEmptyFolderChoice(stdin, stderr, ansi) {
4851
+ const menu = [
4852
+ tx(ENTRY_TEXTS.emptyMenuHeader, { glyph: ansi.yellow("?") }),
4853
+ ENTRY_TEXTS.emptyMenuOptionTutorial,
4854
+ ENTRY_TEXTS.emptyMenuOptionExample
4855
+ ].join("\n");
4856
+ stderr.write(menu + "\n");
4857
+ const rl = createInterface({ input: stdin, output: stderr });
4858
+ try {
4859
+ for (let attempt = 0; attempt < 5; attempt += 1) {
4860
+ const answer = await new Promise(
4861
+ (resolveP) => rl.question(ENTRY_TEXTS.emptyMenuInput, resolveP)
4862
+ );
4863
+ const choice = classifyEmptyFolderAnswer(answer.trim());
4864
+ if (choice !== null) return choice;
4865
+ }
4866
+ return null;
4867
+ } finally {
4868
+ rl.close();
4869
+ }
4870
+ }
4871
+
4795
4872
  // cli/util/edit-distance.ts
4796
4873
  function editDistance(a, b, max) {
4797
4874
  if (a === b) return 0;
@@ -4967,7 +5044,7 @@ function defaultRuntimeContext() {
4967
5044
  }
4968
5045
 
4969
5046
  // cli/telemetry/first-run-prompt.ts
4970
- import { createInterface } from "readline/promises";
5047
+ import { createInterface as createInterface2 } from "readline/promises";
4971
5048
 
4972
5049
  // cli/i18n/telemetry.texts.ts
4973
5050
  var TELEMETRY_PROMPT_TEXTS = {
@@ -5270,7 +5347,7 @@ async function runConsentPrompt(stdin, stdout, nowMs) {
5270
5347
  const rendered = renderConsent(
5271
5348
  ansiFor({ isTTY: stdout.isTTY === true, noColorFlag: false })
5272
5349
  );
5273
- const rl = createInterface({ input: stdin, output: stdout });
5350
+ const rl = createInterface2({ input: stdin, output: stdout });
5274
5351
  try {
5275
5352
  const consented = await readConsentDecision(rl, stdout, rendered);
5276
5353
  writeUserSettings({
@@ -6000,13 +6077,13 @@ var CONSENT_TEXTS = {
6000
6077
  };
6001
6078
 
6002
6079
  // cli/util/confirm.ts
6003
- import { createInterface as createInterface2 } from "readline";
6080
+ import { createInterface as createInterface3 } from "readline";
6004
6081
  var YES_PATTERN = new RegExp(UTIL_TEXTS.confirmYesPatternSource, "i");
6005
6082
  var NO_PATTERN = new RegExp(UTIL_TEXTS.confirmNoPatternSource, "i");
6006
6083
  async function confirm(question, streams, opts) {
6007
6084
  const defaultAnswer = opts?.defaultAnswer ?? "no";
6008
6085
  const suffix = defaultAnswer === "yes" ? UTIL_TEXTS.confirmPromptSuffixDefaultYes : UTIL_TEXTS.confirmPromptSuffix;
6009
- const rl = createInterface2({ input: streams.stdin, output: streams.stderr });
6086
+ const rl = createInterface3({ input: streams.stdin, output: streams.stderr });
6010
6087
  try {
6011
6088
  const answer = await new Promise(
6012
6089
  (resolveP) => rl.question(`${question}${suffix}`, resolveP)
@@ -6360,7 +6437,7 @@ var AsyncMutex = class {
6360
6437
  this.#locked = true;
6361
6438
  return;
6362
6439
  }
6363
- await new Promise((resolve43) => this.#waiters.push(resolve43));
6440
+ await new Promise((resolve44) => this.#waiters.push(resolve44));
6364
6441
  this.#locked = true;
6365
6442
  }
6366
6443
  unlock() {
@@ -6807,7 +6884,7 @@ async function selectReferencedJobFilePaths(db) {
6807
6884
  }
6808
6885
 
6809
6886
  // kernel/adapters/sqlite/migrations.ts
6810
- import { copyFileSync, existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync8, readdirSync } from "fs";
6887
+ import { copyFileSync, existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync8, readdirSync as readdirSync2 } from "fs";
6811
6888
  import { dirname as dirname7, join as join5, resolve as resolve10 } from "path";
6812
6889
  import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
6813
6890
  import { fileURLToPath } from "url";
@@ -6829,7 +6906,7 @@ function defaultMigrationsDir() {
6829
6906
  }
6830
6907
  function discoverMigrations(dir = defaultMigrationsDir()) {
6831
6908
  if (!existsSync8(dir)) return [];
6832
- const files = readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).filter((name) => FILE_RE.test(name)).sort();
6909
+ const files = readdirSync2(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).filter((name) => FILE_RE.test(name)).sort();
6833
6910
  const out = [];
6834
6911
  for (const name of files) {
6835
6912
  const match = FILE_RE.exec(name);
@@ -6944,7 +7021,7 @@ function writeBackup(dbPath, destPath) {
6944
7021
  }
6945
7022
 
6946
7023
  // kernel/adapters/sqlite/plugin-migrations.ts
6947
- import { existsSync as existsSync9, readFileSync as readFileSync9, readdirSync as readdirSync2 } from "fs";
7024
+ import { existsSync as existsSync9, readFileSync as readFileSync9, readdirSync as readdirSync3 } from "fs";
6948
7025
  import { join as join6 } from "path";
6949
7026
 
6950
7027
  // kernel/adapters/sqlite/plugin-migrations-validator.ts
@@ -7264,7 +7341,7 @@ function resolvePluginMigrationsDir(plugin) {
7264
7341
  function discoverPluginMigrations(plugin) {
7265
7342
  const dir = resolvePluginMigrationsDir(plugin);
7266
7343
  if (!dir) return [];
7267
- const files = readdirSync2(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).filter((name) => FILE_RE2.test(name)).sort();
7344
+ const files = readdirSync3(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).filter((name) => FILE_RE2.test(name)).sort();
7268
7345
  const out = [];
7269
7346
  for (const name of files) {
7270
7347
  const match = FILE_RE2.exec(name);
@@ -9863,7 +9940,7 @@ var PLUGIN_LOADER_TEXTS = {
9863
9940
 
9864
9941
  // kernel/adapters/plugin-loader/index.ts
9865
9942
  import { createRequire as createRequire5 } from "module";
9866
- import { existsSync as existsSync13, readFileSync as readFileSync13, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
9943
+ import { existsSync as existsSync13, readFileSync as readFileSync13, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
9867
9944
  import { join as join8, resolve as resolve16 } from "path";
9868
9945
  import { pathToFileURL } from "url";
9869
9946
  import semver from "semver";
@@ -10206,9 +10283,9 @@ function providerKindFailure(opts, status, fileName, errDescription) {
10206
10283
  }
10207
10284
  };
10208
10285
  }
10209
- function isDirectorySafe(path, statSync12) {
10286
+ function isDirectorySafe(path, statSync13) {
10210
10287
  try {
10211
- return statSync12(path).isDirectory();
10288
+ return statSync13(path).isDirectory();
10212
10289
  } catch {
10213
10290
  return false;
10214
10291
  }
@@ -10318,7 +10395,7 @@ var PluginLoader = class {
10318
10395
  const out = [];
10319
10396
  for (const root of this.#options.searchPaths) {
10320
10397
  if (!existsSync13(root)) continue;
10321
- for (const entry of readdirSync4(root, { withFileTypes: true })) {
10398
+ for (const entry of readdirSync5(root, { withFileTypes: true })) {
10322
10399
  if (!entry.isDirectory()) continue;
10323
10400
  const candidate = join8(root, entry.name);
10324
10401
  if (existsSync13(join8(candidate, "plugin.json"))) {
@@ -10689,7 +10766,7 @@ function collectKindEntries(pluginPath, kindDir, out) {
10689
10766
  if (!existsSync13(kindAbs)) return;
10690
10767
  let entries;
10691
10768
  try {
10692
- entries = readdirSync4(kindAbs);
10769
+ entries = readdirSync5(kindAbs);
10693
10770
  } catch {
10694
10771
  return;
10695
10772
  }
@@ -12593,7 +12670,7 @@ import { Command as Command5, Option as Option5 } from "clipanion";
12593
12670
 
12594
12671
  // conformance/index.ts
12595
12672
  import { spawnSync as spawnSync2 } from "child_process";
12596
- import { cpSync, existsSync as existsSync17, mkdtempSync, readdirSync as readdirSync5, readFileSync as readFileSync15, rmSync, statSync as statSync3 } from "fs";
12673
+ import { cpSync, existsSync as existsSync17, mkdtempSync, readdirSync as readdirSync6, readFileSync as readFileSync15, rmSync, statSync as statSync3 } from "fs";
12597
12674
  import { tmpdir } from "os";
12598
12675
  import { isAbsolute as isAbsolute6, join as join11, relative as relative3, resolve as resolve20 } from "path";
12599
12676
 
@@ -12749,7 +12826,7 @@ function runPriorScansSetup(c, options, scope, fixturesRoot, setupEnv) {
12749
12826
  }
12750
12827
  function replaceFixture(scope, fixturesRoot, fixture) {
12751
12828
  assertContained2(fixturesRoot, fixture, "fixture");
12752
- for (const entry of readdirSync5(scope)) {
12829
+ for (const entry of readdirSync6(scope)) {
12753
12830
  if (entry === KERNEL_SKILL_MAP_DIR) continue;
12754
12831
  rmSync(join11(scope, entry), { recursive: true, force: true });
12755
12832
  }
@@ -12986,7 +13063,7 @@ var CONFORMANCE_TEXTS = {
12986
13063
  };
12987
13064
 
12988
13065
  // cli/util/conformance-scopes.ts
12989
- import { existsSync as existsSync18, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
13066
+ import { existsSync as existsSync18, readdirSync as readdirSync7, statSync as statSync4 } from "fs";
12990
13067
  import { dirname as dirname11, resolve as resolve21 } from "path";
12991
13068
  import { createRequire as createRequire6 } from "module";
12992
13069
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -13027,7 +13104,7 @@ function collectProviderScopes(specRoot) {
13027
13104
  }
13028
13105
  const pluginsRoot = resolve21(workspaceRoot, "plugins");
13029
13106
  if (!existsSync18(pluginsRoot)) return out;
13030
- for (const pluginEntry of readdirSync6(pluginsRoot)) {
13107
+ for (const pluginEntry of readdirSync7(pluginsRoot)) {
13031
13108
  const pluginDir = resolve21(pluginsRoot, pluginEntry);
13032
13109
  if (!isDir(pluginDir)) continue;
13033
13110
  const providersRoot = resolve21(pluginDir, "providers");
@@ -13044,7 +13121,7 @@ function isDir(path) {
13044
13121
  }
13045
13122
  }
13046
13123
  function collectPluginProviderScopes(providersRoot, specRoot, out) {
13047
- for (const entry of readdirSync6(providersRoot)) {
13124
+ for (const entry of readdirSync7(providersRoot)) {
13048
13125
  const providerDir = resolve21(providersRoot, entry);
13049
13126
  if (!isDir(providerDir)) continue;
13050
13127
  const conformanceDir = resolve21(providerDir, "conformance");
@@ -13090,7 +13167,7 @@ function selectConformanceScopes(scope) {
13090
13167
  }
13091
13168
  function listCaseFiles(scope) {
13092
13169
  if (!existsSync18(scope.casesDir)) return [];
13093
- return readdirSync6(scope.casesDir).filter((entry) => entry.endsWith(".json")).sort().map((entry) => resolve21(scope.casesDir, entry));
13170
+ return readdirSync7(scope.casesDir).filter((entry) => entry.endsWith(".json")).sort().map((entry) => resolve21(scope.casesDir, entry));
13094
13171
  }
13095
13172
 
13096
13173
  // cli/commands/conformance.ts
@@ -14219,123 +14296,443 @@ var DB_COMMANDS = [
14219
14296
  DbMigrateCommand
14220
14297
  ];
14221
14298
 
14222
- // cli/commands/export.ts
14299
+ // cli/commands/example.ts
14300
+ import { cpSync as cpSync2, existsSync as existsSync21, statSync as statSync5 } from "fs";
14301
+ import { dirname as dirname16, relative as relative5, resolve as resolve26 } from "path";
14302
+ import { fileURLToPath as fileURLToPath5 } from "url";
14223
14303
  import { Command as Command13, Option as Option12 } from "clipanion";
14224
14304
 
14225
- // kernel/scan/query.ts
14226
- var HAS_VALUES = /* @__PURE__ */ new Set(["issues"]);
14227
- var ExportQueryError = class extends Error {
14228
- constructor(message) {
14229
- super(message);
14230
- this.name = "ExportQueryError";
14231
- }
14305
+ // cli/i18n/example.texts.ts
14306
+ var EXAMPLE_TEXTS = {
14307
+ // Success, written to stdout after the example project is created in
14308
+ // the cwd. The project ships unscanned (no `.skill-map/`), so the
14309
+ // next-steps block points the user at `sm scan` (provisions the
14310
+ // project and builds the graph) then `sm serve` (opens the map).
14311
+ written: " {{glyph}} Example project created in {{cwd}}\n\n A small wired portfolio is ready to explore: a handbook\n (AGENTS.md) that mentions a content-editor agent and invokes a\n publish command, a check-links skill the command calls, and the\n docs they reference. Next:\n\n {{scanGlyph}} sm scan build the graph from these files\n {{serveGlyph}} sm serve open the interactive map in the browser\n",
14312
+ writtenScanGlyph: "1)",
14313
+ writtenServeGlyph: "2)",
14314
+ // Refusal, the cwd is not empty and `--force` was not set. Goes to
14315
+ // stderr, exit code 2 (operational error per spec § Exit codes). The
14316
+ // example writes a self-contained project into the cwd, so it needs an
14317
+ // empty directory; the hint spells the two ways forward. Mirrors the
14318
+ // error shape: glyph + headline + dim hint.
14319
+ notEmpty: "{{glyph}} sm example: the current directory is not empty (found {{entries}})\n {{hint}}\n",
14320
+ notEmptyHint: "sm example writes a self-contained project; run it in a fresh empty directory, or pass `--force` to use this one anyway (colliding files are overwritten).",
14321
+ // Unexpected positional argument. The verb takes none. Goes to
14322
+ // stderr, exit code 2. Mirrors the error shape: glyph + headline +
14323
+ // dim hint.
14324
+ unexpectedArg: "{{glyph}} sm example: unexpected argument '{{arg}}'\n {{hint}}\n",
14325
+ unexpectedArgHint: "sm example takes no positional argument. Run `sm example` in an empty directory.",
14326
+ // I/O failure on write or on reading the bundled example payload.
14327
+ writeFailed: "{{glyph}} sm example: failed to write the example project: {{message}}\n",
14328
+ sourceMissing: "{{glyph}} sm example: could not read the bundled example payload from the install.\n {{hint}}\n",
14329
+ sourceMissingHint: "Reinstall @skill-map/cli or report the bug."
14232
14330
  };
14233
- function parseExportQuery(raw) {
14234
- const trimmed = raw.trim();
14235
- const out = { raw: trimmed };
14236
- if (trimmed.length === 0) return out;
14237
- const seen = /* @__PURE__ */ new Set();
14238
- for (const token of trimmed.split(/\s+/)) {
14239
- const eq = token.indexOf("=");
14240
- if (eq <= 0 || eq === token.length - 1) {
14241
- throw new ExportQueryError(
14242
- tx(QUERY_TEXTS.exportQueryInvalidToken, { token })
14243
- );
14244
- }
14245
- const key = token.slice(0, eq).toLowerCase();
14246
- const valuePart = token.slice(eq + 1);
14247
- if (seen.has(key)) {
14248
- throw new ExportQueryError(
14249
- tx(QUERY_TEXTS.exportQueryDuplicateKey, { key })
14250
- );
14251
- }
14252
- seen.add(key);
14253
- const values = valuePart.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
14254
- if (values.length === 0) {
14255
- throw new ExportQueryError(tx(QUERY_TEXTS.exportQueryEmptyValues, { key }));
14256
- }
14257
- switch (key) {
14258
- case "kind":
14259
- out.kinds = parseKindValues(values);
14260
- break;
14261
- case "has":
14262
- if (parseHasValues(values)) out.hasIssues = true;
14263
- break;
14264
- case "path":
14265
- out.pathGlobs = values;
14266
- break;
14267
- default:
14268
- throw new ExportQueryError(
14269
- tx(QUERY_TEXTS.exportQueryUnknownKey, { key })
14270
- );
14271
- }
14331
+
14332
+ // cli/util/serve-banner.ts
14333
+ import { relative as relative4, isAbsolute as isAbsolute7 } from "path";
14334
+ var ESC2 = {
14335
+ reset: "\x1B[0m",
14336
+ bold: "\x1B[1m",
14337
+ dim: "\x1B[2m",
14338
+ underline: "\x1B[4m",
14339
+ /** 256-color violet (xterm 141). */
14340
+ violet: "\x1B[38;5;141m",
14341
+ /** 256-color green (xterm 42). */
14342
+ green: "\x1B[38;5;42m",
14343
+ /** 256-color yellow (xterm 214), matches `cli/util/ansi.ts:yellow`. */
14344
+ yellow: "\x1B[38;5;214m"
14345
+ };
14346
+ var LOGO_LINES = [
14347
+ " ____ _ _ _ _ __ __ ",
14348
+ " / ___|| | _(_) | | | \\/ | __ _ _ __ ",
14349
+ " \\___ \\| |/ / | | | | |\\/| |/ _` | '_ \\ ",
14350
+ " ___) | <| | | | | | | | (_| | |_) |",
14351
+ " |____/|_|\\_\\_|_|_| |_| |_|\\__,_| .__/ ",
14352
+ " |_| "
14353
+ ];
14354
+ var LOGO_WIDTH = 40;
14355
+ function renderBanner(input) {
14356
+ const url = `http://${input.host}:${input.port}`;
14357
+ const dbDisplay = formatDbPath(input.dbPath, input.cwd);
14358
+ const browserLine = input.openBrowser ? "Opening browser\u2026 Press Ctrl+C to stop." : `Visit ${url}/ in your browser. Press Ctrl+C to stop.`;
14359
+ if (!input.isTTY) {
14360
+ return renderFlat({
14361
+ host: input.host,
14362
+ port: input.port,
14363
+ dbPath: input.dbPath,
14364
+ openBrowser: input.openBrowser,
14365
+ dev: input.dev === true
14366
+ });
14272
14367
  }
14273
- return out;
14368
+ return renderFiglet({
14369
+ version: input.version,
14370
+ url,
14371
+ dbDisplay,
14372
+ pathDisplay: formatCwdPath(input.cwd),
14373
+ browserLine,
14374
+ colorEnabled: input.colorEnabled,
14375
+ referencePaths: input.referencePaths ?? [],
14376
+ dev: input.dev === true
14377
+ });
14274
14378
  }
14275
- function parseKindValues(values) {
14276
- for (const v of values) {
14277
- if (v.length === 0) {
14278
- throw new ExportQueryError(QUERY_TEXTS.exportQueryEmptyKind);
14279
- }
14280
- }
14281
- return values;
14379
+ function resolveColorEnabled(opts) {
14380
+ if (opts.noColorFlag) return false;
14381
+ const noColor = opts.env["NO_COLOR"];
14382
+ if (noColor !== void 0 && noColor !== "") return false;
14383
+ const forceColor = opts.env["FORCE_COLOR"];
14384
+ if (forceColor !== void 0 && forceColor !== "") return true;
14385
+ return opts.isTTY;
14282
14386
  }
14283
- function parseHasValues(values) {
14284
- for (const v of values) {
14285
- if (!HAS_VALUES.has(v)) {
14286
- throw new ExportQueryError(
14287
- tx(QUERY_TEXTS.exportQueryUnsupportedHas, {
14288
- value: v,
14289
- allowed: [...HAS_VALUES].join(", ")
14290
- })
14291
- );
14292
- }
14387
+ function renderFlat(input) {
14388
+ const safeHost = sanitizeForTerminal(input.host);
14389
+ const safeDb = sanitizeForTerminal(input.dbPath);
14390
+ const url = `http://${safeHost}:${input.port}`;
14391
+ const devSuffix = input.dev ? " [dev]" : "";
14392
+ const linesOut = [];
14393
+ linesOut.push(`sm serve${devSuffix}: listening on ${url} (db=${safeDb})`);
14394
+ if (input.openBrowser) {
14395
+ linesOut.push(`sm serve: opening ${url}/ in your browser. Press Ctrl+C to stop.`);
14396
+ } else {
14397
+ linesOut.push(`sm serve: visit ${url}/ in your browser. Press Ctrl+C to stop.`);
14293
14398
  }
14294
- return values.includes("issues");
14399
+ return linesOut.join("\n") + "\n";
14295
14400
  }
14296
- function applyExportQuery(scan, query) {
14297
- const nodesWithIssues = query.hasIssues ? collectNodesWithIssues(scan.issues) : null;
14298
- const compiledGlobs = query.pathGlobs ? query.pathGlobs.map(compileGlob) : null;
14299
- const filteredNodes = scan.nodes.filter((node) => {
14300
- if (query.kinds && !query.kinds.includes(node.kind)) return false;
14301
- if (nodesWithIssues && !nodesWithIssues.has(node.path)) return false;
14302
- if (compiledGlobs && !compiledGlobs.some((re) => re.test(node.path))) return false;
14303
- return true;
14304
- });
14305
- const survivingPaths = new Set(filteredNodes.map((n) => n.path));
14306
- const filteredLinks = scan.links.filter(
14307
- (link) => survivingPaths.has(link.source) && survivingPaths.has(link.target)
14308
- );
14309
- const filteredIssues = scan.issues.filter(
14310
- (issue) => issue.nodeIds.some((id) => survivingPaths.has(id))
14311
- );
14312
- return {
14313
- query,
14314
- nodes: filteredNodes,
14315
- links: filteredLinks,
14316
- issues: filteredIssues
14317
- };
14401
+ function renderLogoBlock(input) {
14402
+ const { dimOpen, dimClose, violetOpen, violetClose } = resolveAnsi(input.colorEnabled);
14403
+ const logoLines = LOGO_LINES.map((line) => `${violetOpen}${line}${violetClose}`);
14404
+ const versionText = `v${input.version}`;
14405
+ const versionPad = Math.max(0, LOGO_WIDTH - versionText.length);
14406
+ const versionLine = `${" ".repeat(versionPad)}${dimOpen}${versionText}${dimClose}`;
14407
+ return `${logoLines.join("\n")}
14408
+
14409
+ ${versionLine}
14410
+
14411
+ `;
14318
14412
  }
14319
- function collectNodesWithIssues(issues) {
14320
- const out = /* @__PURE__ */ new Set();
14321
- for (const issue of issues) {
14322
- for (const nodeId of issue.nodeIds) out.add(nodeId);
14413
+ function renderFiglet(input) {
14414
+ const {
14415
+ dimOpen,
14416
+ dimClose,
14417
+ greenUnderline,
14418
+ greenUnderlineClose,
14419
+ violetOpen,
14420
+ violetClose,
14421
+ yellowOpen,
14422
+ yellowClose
14423
+ } = resolveAnsi(input.colorEnabled);
14424
+ const logoLines = LOGO_LINES.map((line) => `${violetOpen}${line}${violetClose}`);
14425
+ const versionText = `v${input.version}`;
14426
+ const devText = "[dev]";
14427
+ const versionWidth = input.dev ? devText.length : versionText.length;
14428
+ const versionPad = Math.max(0, LOGO_WIDTH - versionWidth);
14429
+ const versionLine = input.dev ? `${" ".repeat(versionPad)}${yellowOpen}${devText}${yellowClose}` : `${" ".repeat(versionPad)}${dimOpen}${versionText}${dimClose}`;
14430
+ const lines = [];
14431
+ lines.push(...logoLines);
14432
+ lines.push("");
14433
+ lines.push(versionLine);
14434
+ lines.push("");
14435
+ lines.push(` ${dimOpen}Server${dimClose} ${greenUnderline}${input.url}${greenUnderlineClose}`);
14436
+ lines.push(` ${dimOpen}Path${dimClose} ${input.pathDisplay}`);
14437
+ lines.push(` ${dimOpen}DB${dimClose} ${input.dbDisplay}`);
14438
+ lines.push(...renderListRows("Refs", input.referencePaths, dimOpen, dimClose));
14439
+ lines.push("");
14440
+ lines.push(` ${dimOpen}${input.browserLine}${dimClose}`);
14441
+ lines.push("");
14442
+ return lines.join("\n") + "\n";
14443
+ }
14444
+ function renderListRows(label, values, dimOpen, dimClose) {
14445
+ if (values.length === 0) return [];
14446
+ const out = [];
14447
+ const labelPad = " ".repeat(Math.max(1, 9 - label.length));
14448
+ const continuationPad = " ".repeat(11);
14449
+ out.push(` ${dimOpen}${label}${dimClose}${labelPad}${sanitizeForTerminal(values[0])}`);
14450
+ for (let i = 1; i < values.length; i += 1) {
14451
+ out.push(`${continuationPad}${sanitizeForTerminal(values[i])}`);
14323
14452
  }
14324
14453
  return out;
14325
14454
  }
14326
- function compileGlob(pattern) {
14327
- const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
14328
- const withDouble = escaped.replace(/\*\*/g, "\0DOUBLESTAR\0");
14329
- const withSingle = withDouble.replace(/\*/g, "[^/]*");
14330
- const final = withSingle.replace(/DOUBLESTAR/g, ".*");
14331
- return new RegExp(`^${final}$`);
14332
- }
14333
-
14334
- // cli/i18n/export.texts.ts
14335
- var EXPORT_TEXTS = {
14336
- /**
14337
- * Generic §3.1 single-line error wrapper. The caller passes the
14338
- * already-rendered glyph (red `✕`) and the inner error message. No
14455
+ var EMPTY_ANSI = {
14456
+ dimOpen: "",
14457
+ dimClose: "",
14458
+ greenUnderline: "",
14459
+ greenUnderlineClose: "",
14460
+ violetOpen: "",
14461
+ violetClose: "",
14462
+ yellowOpen: "",
14463
+ yellowClose: ""
14464
+ };
14465
+ var ENABLED_ANSI = {
14466
+ dimOpen: ESC2.dim,
14467
+ dimClose: ESC2.reset,
14468
+ greenUnderline: `${ESC2.green}${ESC2.underline}`,
14469
+ greenUnderlineClose: ESC2.reset,
14470
+ violetOpen: ESC2.violet,
14471
+ violetClose: ESC2.reset,
14472
+ yellowOpen: ESC2.yellow,
14473
+ yellowClose: ESC2.reset
14474
+ };
14475
+ function resolveAnsi(colorEnabled) {
14476
+ return colorEnabled ? ENABLED_ANSI : EMPTY_ANSI;
14477
+ }
14478
+ function formatDbPath(dbPath, cwd) {
14479
+ const safe = sanitizeForTerminal(dbPath);
14480
+ if (!isAbsolute7(safe)) return safe;
14481
+ const rel = relative4(cwd, safe);
14482
+ if (rel === "" || rel.startsWith("..") || isAbsolute7(rel)) {
14483
+ return safe;
14484
+ }
14485
+ return rel;
14486
+ }
14487
+ function formatCwdPath(cwd) {
14488
+ return sanitizeForTerminal(cwd);
14489
+ }
14490
+
14491
+ // cli/commands/example.ts
14492
+ var EXAMPLE_SOURCE_DIR = "fixtures/demo-scope";
14493
+ var ExampleCommand = class extends SmCommand {
14494
+ static paths = [["example"]];
14495
+ static usage = Command13.Usage({
14496
+ category: "Setup",
14497
+ description: "Materialize a ready-to-explore example project (the demo harness) in the current directory.",
14498
+ details: `
14499
+ Writes a small wired portfolio into <cwd>: a handbook (AGENTS.md)
14500
+ that mentions a content-editor agent and invokes a publish command,
14501
+ a check-links skill the command calls, and the docs they reference.
14502
+ It is the same harness the public demo renders.
14503
+
14504
+ Ships unscanned (no .skill-map/): run \`sm scan\` then \`sm serve\`
14505
+ to build and open the graph. Requires an empty directory; refuses a
14506
+ non-empty cwd (exit 2) unless --force. Takes no positional argument.
14507
+ `,
14508
+ examples: [
14509
+ ["Create the example project in the cwd", "$0 example"],
14510
+ ["Create it even if the directory is not empty", "$0 example --force"]
14511
+ ]
14512
+ });
14513
+ // The verb takes no positional argument. Accept one so a stray
14514
+ // `sm example foo` lands on a friendly usage error (guarded in
14515
+ // `run()`) instead of clipanion's generic "extraneous argument".
14516
+ legacyPositional = Option12.String({ required: false });
14517
+ force = Option12.Boolean("--force", false, {
14518
+ description: "Overwrite colliding files in a non-empty directory without prompting."
14519
+ });
14520
+ async run() {
14521
+ const ctx = defaultRuntimeContext();
14522
+ const stderr = this.context.stderr;
14523
+ const stderrAnsi = this.ansiFor("stderr");
14524
+ const errGlyph = stderrAnsi.red("\u2715");
14525
+ if (this.legacyPositional !== void 0) {
14526
+ this.printer.error(
14527
+ tx(EXAMPLE_TEXTS.unexpectedArg, {
14528
+ glyph: errGlyph,
14529
+ arg: this.legacyPositional,
14530
+ hint: stderrAnsi.dim(EXAMPLE_TEXTS.unexpectedArgHint)
14531
+ })
14532
+ );
14533
+ return ExitCode.Error;
14534
+ }
14535
+ if (!this.force && !isDirEmpty(ctx.cwd)) {
14536
+ this.printer.error(
14537
+ tx(EXAMPLE_TEXTS.notEmpty, {
14538
+ glyph: errGlyph,
14539
+ entries: listCwdEntries(ctx.cwd),
14540
+ hint: stderrAnsi.dim(EXAMPLE_TEXTS.notEmptyHint)
14541
+ })
14542
+ );
14543
+ return ExitCode.Error;
14544
+ }
14545
+ let sourceDir;
14546
+ try {
14547
+ sourceDir = resolveExampleSourceDir();
14548
+ } catch {
14549
+ this.printer.error(
14550
+ tx(EXAMPLE_TEXTS.sourceMissing, {
14551
+ glyph: errGlyph,
14552
+ hint: stderrAnsi.dim(EXAMPLE_TEXTS.sourceMissingHint)
14553
+ })
14554
+ );
14555
+ return ExitCode.Error;
14556
+ }
14557
+ try {
14558
+ cpSync2(sourceDir, ctx.cwd, {
14559
+ recursive: true,
14560
+ // Never copy the example's own scan state: the project must ship
14561
+ // unscanned so the user's first `sm scan` provisions it fresh.
14562
+ filter: (src) => isExamplePayloadEntry(sourceDir, src)
14563
+ });
14564
+ } catch (err) {
14565
+ this.printer.error(
14566
+ tx(EXAMPLE_TEXTS.writeFailed, {
14567
+ glyph: errGlyph,
14568
+ message: formatErrorMessage(err)
14569
+ })
14570
+ );
14571
+ return ExitCode.Error;
14572
+ }
14573
+ const colorEnabled = resolveColorEnabled({
14574
+ isTTY: stderr.isTTY === true,
14575
+ noColorFlag: this.noColor,
14576
+ env: process.env
14577
+ });
14578
+ this.printer.info(renderLogoBlock({ version: VERSION, colorEnabled }));
14579
+ const ansi = this.ansiFor("stdout");
14580
+ this.printer.data(
14581
+ tx(EXAMPLE_TEXTS.written, {
14582
+ glyph: ansi.green("\u2713"),
14583
+ cwd: ansi.dim(displayCwd(ctx.cwd)),
14584
+ scanGlyph: ansi.dim(EXAMPLE_TEXTS.writtenScanGlyph),
14585
+ serveGlyph: ansi.dim(EXAMPLE_TEXTS.writtenServeGlyph)
14586
+ })
14587
+ );
14588
+ return ExitCode.Ok;
14589
+ }
14590
+ };
14591
+ var cachedSourceDir;
14592
+ function resolveExampleSourceDir() {
14593
+ if (cachedSourceDir !== void 0) return cachedSourceDir;
14594
+ const here = dirname16(fileURLToPath5(import.meta.url));
14595
+ const candidates = [
14596
+ // dev: src/cli/commands/ → repo-root fixtures/demo-scope/
14597
+ resolve26(here, "../../..", EXAMPLE_SOURCE_DIR),
14598
+ // bundled: dist/cli.js → dist/cli/example (sibling)
14599
+ resolve26(here, "cli/example"),
14600
+ // bundled fallback: any-depth → cli/example
14601
+ resolve26(here, "../cli/example")
14602
+ ];
14603
+ for (const candidate of candidates) {
14604
+ if (existsSync21(candidate) && statSync5(candidate).isDirectory()) {
14605
+ cachedSourceDir = candidate;
14606
+ return candidate;
14607
+ }
14608
+ }
14609
+ throw new Error(
14610
+ `example payload directory not found in any candidate location (last tried: ${candidates[candidates.length - 1]})`
14611
+ );
14612
+ }
14613
+ function isExamplePayloadEntry(sourceRoot, src) {
14614
+ const rel = relative5(sourceRoot, src);
14615
+ if (rel === "") return true;
14616
+ return rel.split(/[\\/]/)[0] !== ".skill-map";
14617
+ }
14618
+
14619
+ // cli/commands/export.ts
14620
+ import { Command as Command14, Option as Option13 } from "clipanion";
14621
+
14622
+ // kernel/scan/query.ts
14623
+ var HAS_VALUES = /* @__PURE__ */ new Set(["issues"]);
14624
+ var ExportQueryError = class extends Error {
14625
+ constructor(message) {
14626
+ super(message);
14627
+ this.name = "ExportQueryError";
14628
+ }
14629
+ };
14630
+ function parseExportQuery(raw) {
14631
+ const trimmed = raw.trim();
14632
+ const out = { raw: trimmed };
14633
+ if (trimmed.length === 0) return out;
14634
+ const seen = /* @__PURE__ */ new Set();
14635
+ for (const token of trimmed.split(/\s+/)) {
14636
+ const eq = token.indexOf("=");
14637
+ if (eq <= 0 || eq === token.length - 1) {
14638
+ throw new ExportQueryError(
14639
+ tx(QUERY_TEXTS.exportQueryInvalidToken, { token })
14640
+ );
14641
+ }
14642
+ const key = token.slice(0, eq).toLowerCase();
14643
+ const valuePart = token.slice(eq + 1);
14644
+ if (seen.has(key)) {
14645
+ throw new ExportQueryError(
14646
+ tx(QUERY_TEXTS.exportQueryDuplicateKey, { key })
14647
+ );
14648
+ }
14649
+ seen.add(key);
14650
+ const values = valuePart.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
14651
+ if (values.length === 0) {
14652
+ throw new ExportQueryError(tx(QUERY_TEXTS.exportQueryEmptyValues, { key }));
14653
+ }
14654
+ switch (key) {
14655
+ case "kind":
14656
+ out.kinds = parseKindValues(values);
14657
+ break;
14658
+ case "has":
14659
+ if (parseHasValues(values)) out.hasIssues = true;
14660
+ break;
14661
+ case "path":
14662
+ out.pathGlobs = values;
14663
+ break;
14664
+ default:
14665
+ throw new ExportQueryError(
14666
+ tx(QUERY_TEXTS.exportQueryUnknownKey, { key })
14667
+ );
14668
+ }
14669
+ }
14670
+ return out;
14671
+ }
14672
+ function parseKindValues(values) {
14673
+ for (const v of values) {
14674
+ if (v.length === 0) {
14675
+ throw new ExportQueryError(QUERY_TEXTS.exportQueryEmptyKind);
14676
+ }
14677
+ }
14678
+ return values;
14679
+ }
14680
+ function parseHasValues(values) {
14681
+ for (const v of values) {
14682
+ if (!HAS_VALUES.has(v)) {
14683
+ throw new ExportQueryError(
14684
+ tx(QUERY_TEXTS.exportQueryUnsupportedHas, {
14685
+ value: v,
14686
+ allowed: [...HAS_VALUES].join(", ")
14687
+ })
14688
+ );
14689
+ }
14690
+ }
14691
+ return values.includes("issues");
14692
+ }
14693
+ function applyExportQuery(scan, query) {
14694
+ const nodesWithIssues = query.hasIssues ? collectNodesWithIssues(scan.issues) : null;
14695
+ const compiledGlobs = query.pathGlobs ? query.pathGlobs.map(compileGlob) : null;
14696
+ const filteredNodes = scan.nodes.filter((node) => {
14697
+ if (query.kinds && !query.kinds.includes(node.kind)) return false;
14698
+ if (nodesWithIssues && !nodesWithIssues.has(node.path)) return false;
14699
+ if (compiledGlobs && !compiledGlobs.some((re) => re.test(node.path))) return false;
14700
+ return true;
14701
+ });
14702
+ const survivingPaths = new Set(filteredNodes.map((n) => n.path));
14703
+ const filteredLinks = scan.links.filter(
14704
+ (link) => survivingPaths.has(link.source) && survivingPaths.has(link.target)
14705
+ );
14706
+ const filteredIssues = scan.issues.filter(
14707
+ (issue) => issue.nodeIds.some((id) => survivingPaths.has(id))
14708
+ );
14709
+ return {
14710
+ query,
14711
+ nodes: filteredNodes,
14712
+ links: filteredLinks,
14713
+ issues: filteredIssues
14714
+ };
14715
+ }
14716
+ function collectNodesWithIssues(issues) {
14717
+ const out = /* @__PURE__ */ new Set();
14718
+ for (const issue of issues) {
14719
+ for (const nodeId of issue.nodeIds) out.add(nodeId);
14720
+ }
14721
+ return out;
14722
+ }
14723
+ function compileGlob(pattern) {
14724
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
14725
+ const withDouble = escaped.replace(/\*\*/g, "\0DOUBLESTAR\0");
14726
+ const withSingle = withDouble.replace(/\*/g, "[^/]*");
14727
+ const final = withSingle.replace(/DOUBLESTAR/g, ".*");
14728
+ return new RegExp(`^${final}$`);
14729
+ }
14730
+
14731
+ // cli/i18n/export.texts.ts
14732
+ var EXPORT_TEXTS = {
14733
+ /**
14734
+ * Generic §3.1 single-line error wrapper. The caller passes the
14735
+ * already-rendered glyph (red `✕`) and the inner error message. No
14339
14736
  * hint here because the inner message itself varies across call
14340
14737
  * sites (config load, file write, malformed query). Specific errors
14341
14738
  * with actionable next steps use their own §3.1b blocks below.
@@ -14399,7 +14796,7 @@ var DEFERRED_FORMATS = {
14399
14796
  };
14400
14797
  var ExportCommand = class extends SmCommand {
14401
14798
  static paths = [["export"]];
14402
- static usage = Command13.Usage({
14799
+ static usage = Command14.Usage({
14403
14800
  category: "Browse",
14404
14801
  description: "Filtered export. Query syntax is implementation-defined pre-1.0.",
14405
14802
  details: `
@@ -14424,8 +14821,8 @@ var ExportCommand = class extends SmCommand {
14424
14821
  ["Files under a path glob", '$0 export "path=.claude/commands/**" --format json']
14425
14822
  ]
14426
14823
  });
14427
- query = Option12.String({ required: false });
14428
- format = Option12.String("--format", { required: false });
14824
+ query = Option13.String({ required: false });
14825
+ format = Option13.String("--format", { required: false });
14429
14826
  async run() {
14430
14827
  const stderrAnsi = this.ansiFor("stderr");
14431
14828
  const errGlyph = stderrAnsi.red("\u2715");
@@ -14649,7 +15046,7 @@ function pickTitle2(node) {
14649
15046
  }
14650
15047
 
14651
15048
  // cli/commands/graph.ts
14652
- import { Command as Command14, Option as Option13 } from "clipanion";
15049
+ import { Command as Command15, Option as Option14 } from "clipanion";
14653
15050
 
14654
15051
  // cli/i18n/graph.texts.ts
14655
15052
  var GRAPH_TEXTS = {
@@ -14667,7 +15064,7 @@ var GRAPH_TEXTS = {
14667
15064
  var DEFAULT_FORMAT = "ascii";
14668
15065
  var GraphCommand = class extends SmCommand {
14669
15066
  static paths = [["graph"]];
14670
- static usage = Command14.Usage({
15067
+ static usage = Command15.Usage({
14671
15068
  category: "Browse",
14672
15069
  description: "Render the full graph via the named formatter.",
14673
15070
  details: `
@@ -14684,10 +15081,10 @@ var GraphCommand = class extends SmCommand {
14684
15081
  ["Use a non-default DB file", "$0 graph --db /path/to/skill-map.db"]
14685
15082
  ]
14686
15083
  });
14687
- format = Option13.String("--format", DEFAULT_FORMAT, {
15084
+ format = Option14.String("--format", DEFAULT_FORMAT, {
14688
15085
  description: `Formatter format. Must match the \`formatId\` field of a registered formatter. Default: ${DEFAULT_FORMAT}.`
14689
15086
  });
14690
- noPlugins = Option13.Boolean("--no-plugins", false, {
15087
+ noPlugins = Option14.Boolean("--no-plugins", false, {
14691
15088
  description: "Skip drop-in plugin discovery. Only built-in formatters participate."
14692
15089
  });
14693
15090
  async run() {
@@ -14738,8 +15135,8 @@ var GraphCommand = class extends SmCommand {
14738
15135
  // cli/commands/help.ts
14739
15136
  import { readFileSync as readFileSync17 } from "fs";
14740
15137
  import { createRequire as createRequire7 } from "module";
14741
- import { resolve as resolve26 } from "path";
14742
- import { Command as Command15, Option as Option14 } from "clipanion";
15138
+ import { resolve as resolve27 } from "path";
15139
+ import { Command as Command16, Option as Option15 } from "clipanion";
14743
15140
 
14744
15141
  // cli/i18n/help.texts.ts
14745
15142
  var HELP_TEXTS = {
@@ -14882,9 +15279,9 @@ var HELP_GROUPS = {
14882
15279
  };
14883
15280
 
14884
15281
  // cli/commands/help.ts
14885
- var HelpCommand = class extends Command15 {
15282
+ var HelpCommand = class extends Command16 {
14886
15283
  static paths = [["help"]];
14887
- static usage = Command15.Usage({
15284
+ static usage = Command16.Usage({
14888
15285
  category: "Introspection",
14889
15286
  description: "Self-describing introspection. --format human|md|json.",
14890
15287
  details: `
@@ -14897,13 +15294,13 @@ var HelpCommand = class extends Command15 {
14897
15294
  json : structured surface dump per spec/cli-contract.md.
14898
15295
  `
14899
15296
  });
14900
- verbParts = Option14.Rest({ required: 0 });
14901
- format = Option14.String("--format", "human");
15297
+ verbParts = Option15.Rest({ required: 0 });
15298
+ format = Option15.String("--format", "human");
14902
15299
  // `HelpCommand` extends Clipanion's `Command` directly (see block
14903
15300
  // comment), so it does not inherit `SmCommand`'s `--no-color`. Declared
14904
15301
  // here because the human overview now emits a coloured tutorial glyph;
14905
15302
  // `ansiFor` still layers `NO_COLOR` / `FORCE_COLOR` / TTY on top.
14906
- noColor = Option14.Boolean("--no-color", false, {
15303
+ noColor = Option15.Boolean("--no-color", false, {
14907
15304
  description: "Disable ANSI color codes."
14908
15305
  });
14909
15306
  async execute() {
@@ -15052,7 +15449,7 @@ function resolveSpecVersion() {
15052
15449
  try {
15053
15450
  const req = createRequire7(import.meta.url);
15054
15451
  const indexPath = req.resolve("@skill-map/spec/index.json");
15055
- const pkgPath = resolve26(indexPath, "..", "package.json");
15452
+ const pkgPath = resolve27(indexPath, "..", "package.json");
15056
15453
  const pkg = JSON.parse(readFileSync17(pkgPath, "utf8"));
15057
15454
  return pkg.version;
15058
15455
  } catch {
@@ -15295,12 +15692,12 @@ function renderCompactOverview(verbs, ansi) {
15295
15692
  lines.push(HELP_TEXTS.compactFooter);
15296
15693
  return lines.join("\n") + "\n";
15297
15694
  }
15298
- var RootHelpCommand = class extends Command15 {
15695
+ var RootHelpCommand = class extends Command16 {
15299
15696
  static paths = [["-h"], ["--help"]];
15300
15697
  // Mirrors `HelpCommand.noColor`: this command extends Clipanion's
15301
15698
  // `Command` directly, so the flag is declared here to gate the
15302
15699
  // coloured tutorial glyph in the overview.
15303
- noColor = Option14.Boolean("--no-color", false, {
15700
+ noColor = Option15.Boolean("--no-color", false, {
15304
15701
  description: "Disable ANSI color codes."
15305
15702
  });
15306
15703
  async execute() {
@@ -15360,8 +15757,8 @@ function registeredVerbPaths(cli2) {
15360
15757
 
15361
15758
  // cli/commands/hooks.ts
15362
15759
  import { chmod as chmod2, mkdir as mkdir3, readFile as readFile2, stat as stat2, writeFile } from "fs/promises";
15363
- import { dirname as dirname16, resolve as resolve27 } from "path";
15364
- import { Command as Command16, Option as Option15 } from "clipanion";
15760
+ import { dirname as dirname17, resolve as resolve28 } from "path";
15761
+ import { Command as Command17, Option as Option16 } from "clipanion";
15365
15762
 
15366
15763
  // cli/i18n/hooks.texts.ts
15367
15764
  var HOOKS_TEXTS = {
@@ -15408,7 +15805,7 @@ var SKILL_MAP_BLOCK = [
15408
15805
  var FRESH_HOOK_BODY = HOOK_SHEBANG + SKILL_MAP_BLOCK;
15409
15806
  var HooksInstallCommand = class extends SmCommand {
15410
15807
  static paths = [["hooks", "install"]];
15411
- static usage = Command16.Usage({
15808
+ static usage = Command17.Usage({
15412
15809
  category: "Actions",
15413
15810
  description: "Install a git pre-commit hook that auto-bumps staged sidecar drift.",
15414
15811
  details: `
@@ -15431,8 +15828,8 @@ var HooksInstallCommand = class extends SmCommand {
15431
15828
  ["Preview what would be written", "$0 hooks install pre-commit-bump --dry-run"]
15432
15829
  ]
15433
15830
  });
15434
- flavour = Option15.String({ required: true });
15435
- dryRun = Option15.Boolean("-n,--dry-run", false);
15831
+ flavour = Option16.String({ required: true });
15832
+ dryRun = Option16.Boolean("-n,--dry-run", false);
15436
15833
  // The remaining cyclomatic count is from CLI ergonomics, flavour
15437
15834
  // guard, repo lookup, marker detection, dry-run / json / chained /
15438
15835
  // fresh branches each contributing a guard. Inner work already lives
@@ -15463,8 +15860,8 @@ var HooksInstallCommand = class extends SmCommand {
15463
15860
  );
15464
15861
  return ExitCode.NotFound;
15465
15862
  }
15466
- const hooksDir = resolve27(repoRoot, ".git", "hooks");
15467
- const hookPath = resolve27(hooksDir, "pre-commit");
15863
+ const hooksDir = resolve28(repoRoot, ".git", "hooks");
15864
+ const hookPath = resolve28(hooksDir, "pre-commit");
15468
15865
  const existing = await pathExists(hookPath) ? await readFile2(hookPath, "utf8") : null;
15469
15866
  const planned2 = computePlannedHookContent(existing);
15470
15867
  if (planned2.kind === "already-installed") {
@@ -15522,8 +15919,8 @@ var HooksInstallCommand = class extends SmCommand {
15522
15919
  async function findGitRepoRoot(cwd) {
15523
15920
  let current = cwd;
15524
15921
  while (true) {
15525
- if (await pathExists(resolve27(current, ".git"))) return current;
15526
- const parent = dirname16(current);
15922
+ if (await pathExists(resolve28(current, ".git"))) return current;
15923
+ const parent = dirname17(current);
15527
15924
  if (parent === current) return null;
15528
15925
  current = parent;
15529
15926
  }
@@ -15545,11 +15942,11 @@ var HOOKS_COMMANDS = [HooksInstallCommand];
15545
15942
  // cli/commands/init.ts
15546
15943
  import { mkdir as mkdir4, readFile as readFile3, unlink, writeFile as writeFile2 } from "fs/promises";
15547
15944
  import { join as join16 } from "path";
15548
- import { Command as Command17, Option as Option16 } from "clipanion";
15945
+ import { Command as Command18, Option as Option17 } from "clipanion";
15549
15946
 
15550
15947
  // kernel/orchestrator/index.ts
15551
- import { existsSync as existsSync23, statSync as statSync6 } from "fs";
15552
- import { isAbsolute as isAbsolute9, resolve as resolve30 } from "path";
15948
+ import { existsSync as existsSync24, statSync as statSync7 } from "fs";
15949
+ import { isAbsolute as isAbsolute10, resolve as resolve31 } from "path";
15553
15950
  import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
15554
15951
 
15555
15952
  // kernel/i18n/orchestrator.texts.ts
@@ -16494,7 +16891,7 @@ function collectBrokenLinks(links, nodes, ctx) {
16494
16891
  return broken;
16495
16892
  }
16496
16893
  function applyResolution(link, indexes, ctx) {
16497
- const resolution = resolve28(link, indexes, ctx);
16894
+ const resolution = resolve29(link, indexes, ctx);
16498
16895
  if (resolution === "none") return;
16499
16896
  link.resolvedTarget = resolution;
16500
16897
  }
@@ -16509,7 +16906,7 @@ function buildIndexes(nodes, ctx) {
16509
16906
  }
16510
16907
  return { byPath: byPath3, byName, nodeByPath };
16511
16908
  }
16512
- function resolve28(link, indexes, ctx) {
16909
+ function resolve29(link, indexes, ctx) {
16513
16910
  if (indexes.byPath.has(link.target)) return link.target;
16514
16911
  return resolveByName(link, indexes, ctx);
16515
16912
  }
@@ -16898,7 +17295,7 @@ function detectRenamesAndOrphans(prior, current, issues, silenced) {
16898
17295
  }
16899
17296
 
16900
17297
  // kernel/orchestrator/walk.ts
16901
- import { isAbsolute as isAbsolute8, resolve as resolve29 } from "path";
17298
+ import { isAbsolute as isAbsolute9, resolve as resolve30 } from "path";
16902
17299
 
16903
17300
  // kernel/sidecar/drift.ts
16904
17301
  function computeDriftStatus(args2) {
@@ -16911,8 +17308,8 @@ function computeDriftStatus(args2) {
16911
17308
  }
16912
17309
 
16913
17310
  // kernel/sidecar/discover-orphans.ts
16914
- import { existsSync as existsSync21, readdirSync as readdirSync7, statSync as statSync5 } from "fs";
16915
- import { join as join13, relative as relative4, sep as sep4 } from "path";
17311
+ import { existsSync as existsSync22, readdirSync as readdirSync8, statSync as statSync6 } from "fs";
17312
+ import { join as join13, relative as relative6, sep as sep4 } from "path";
16916
17313
  function discoverOrphanSidecars(roots, shouldSkip) {
16917
17314
  const out = [];
16918
17315
  for (const root of roots) {
@@ -16923,13 +17320,13 @@ function discoverOrphanSidecars(roots, shouldSkip) {
16923
17320
  function walk2(root, current, shouldSkip, out) {
16924
17321
  let entries;
16925
17322
  try {
16926
- entries = readdirSync7(current, { withFileTypes: true, encoding: "utf8" });
17323
+ entries = readdirSync8(current, { withFileTypes: true, encoding: "utf8" });
16927
17324
  } catch {
16928
17325
  return;
16929
17326
  }
16930
17327
  for (const entry of entries) {
16931
17328
  const full = join13(current, entry.name);
16932
- const rel = relative4(root, full).split(sep4).join("/");
17329
+ const rel = relative6(root, full).split(sep4).join("/");
16933
17330
  if (shouldSkip(rel)) continue;
16934
17331
  if (entry.isSymbolicLink()) continue;
16935
17332
  if (entry.isDirectory()) {
@@ -16939,13 +17336,13 @@ function walk2(root, current, shouldSkip, out) {
16939
17336
  if (!entry.isFile()) continue;
16940
17337
  if (!entry.name.endsWith(".sm")) continue;
16941
17338
  const expectedMd = `${full.slice(0, -".sm".length)}.md`;
16942
- if (existsSync21(expectedMd) && safeIsFile(expectedMd)) continue;
17339
+ if (existsSync22(expectedMd) && safeIsFile(expectedMd)) continue;
16943
17340
  out.push({ sidecarPath: full, relativePath: rel, expectedMdPath: expectedMd });
16944
17341
  }
16945
17342
  }
16946
17343
  function safeIsFile(path) {
16947
17344
  try {
16948
- return statSync5(path).isFile();
17345
+ return statSync6(path).isFile();
16949
17346
  } catch {
16950
17347
  return false;
16951
17348
  }
@@ -16953,8 +17350,8 @@ function safeIsFile(path) {
16953
17350
 
16954
17351
  // kernel/orchestrator/node-build.ts
16955
17352
  import { createHash as createHash2 } from "crypto";
16956
- import { existsSync as existsSync22 } from "fs";
16957
- import { isAbsolute as isAbsolute7, resolve as resolvePath } from "path";
17353
+ import { existsSync as existsSync23 } from "fs";
17354
+ import { isAbsolute as isAbsolute8, resolve as resolvePath } from "path";
16958
17355
  import "js-tiktoken/lite";
16959
17356
  import yaml4 from "js-yaml";
16960
17357
 
@@ -17117,12 +17514,12 @@ function resolveSidecarOverlay(relativePath2, nodePathForIssue, roots, liveBodyH
17117
17514
  };
17118
17515
  }
17119
17516
  function resolveAbsoluteMdPath(relativePath2, roots) {
17120
- if (isAbsolute7(relativePath2)) {
17121
- return existsSync22(relativePath2) ? relativePath2 : null;
17517
+ if (isAbsolute8(relativePath2)) {
17518
+ return existsSync23(relativePath2) ? relativePath2 : null;
17122
17519
  }
17123
17520
  for (const root of roots) {
17124
17521
  const candidate = resolvePath(root, relativePath2);
17125
- if (existsSync22(candidate)) return candidate;
17522
+ if (existsSync23(candidate)) return candidate;
17126
17523
  }
17127
17524
  return null;
17128
17525
  }
@@ -17344,8 +17741,8 @@ function expandSidecarPaths(paths, priorNodesByPath) {
17344
17741
  }
17345
17742
  function toAbsolute(relPath, roots) {
17346
17743
  const root = roots[0] ?? ".";
17347
- const absRoot = isAbsolute8(root) ? root : resolve29(root);
17348
- return resolve29(absRoot, relPath);
17744
+ const absRoot = isAbsolute9(root) ? root : resolve30(root);
17745
+ return resolve30(absRoot, relPath);
17349
17746
  }
17350
17747
  function resolveEffectiveCaps(opts) {
17351
17748
  return {
@@ -17935,7 +18332,7 @@ function validateRoots(roots) {
17935
18332
  throw new Error(ORCHESTRATOR_TEXTS.runScanRootEmptyArray);
17936
18333
  }
17937
18334
  for (const root of roots) {
17938
- if (!existsSync23(root) || !statSync6(root).isDirectory()) {
18335
+ if (!existsSync24(root) || !statSync7(root).isDirectory()) {
17939
18336
  throw new Error(tx(ORCHESTRATOR_TEXTS.runScanRootMissing, { root }));
17940
18337
  }
17941
18338
  }
@@ -17943,8 +18340,8 @@ function validateRoots(roots) {
17943
18340
  function resolveActiveProviderOption(optionValue, roots, providers) {
17944
18341
  if (optionValue !== void 0) return optionValue;
17945
18342
  for (const root of roots) {
17946
- const absRoot = isAbsolute9(root) ? root : resolve30(root);
17947
- if (!existsSync23(absRoot)) continue;
18343
+ const absRoot = isAbsolute10(root) ? root : resolve31(root);
18344
+ if (!existsSync24(absRoot)) continue;
17948
18345
  const detected = detectProvidersFromFilesystem(absRoot, providers)[0] ?? null;
17949
18346
  if (detected !== null) return detected;
17950
18347
  }
@@ -17952,10 +18349,10 @@ function resolveActiveProviderOption(optionValue, roots, providers) {
17952
18349
  }
17953
18350
 
17954
18351
  // kernel/scan/watcher.ts
17955
- import { resolve as resolve31, relative as relative5, sep as sep5 } from "path";
18352
+ import { resolve as resolve32, relative as relative7, sep as sep5 } from "path";
17956
18353
  import chokidar from "chokidar";
17957
18354
  function createChokidarWatcher(opts) {
17958
- const absRoots = opts.roots.map((r) => resolve31(opts.cwd, r));
18355
+ const absRoots = opts.roots.map((r) => resolve32(opts.cwd, r));
17959
18356
  const ignoreFilterOpt = opts.ignoreFilter;
17960
18357
  const getFilter = ignoreFilterOpt === void 0 ? void 0 : typeof ignoreFilterOpt === "function" ? ignoreFilterOpt : () => ignoreFilterOpt;
17961
18358
  const ignored = getFilter ? (path) => {
@@ -18048,7 +18445,7 @@ function createChokidarWatcher(opts) {
18048
18445
  }
18049
18446
  function relativePathFromRoots2(absolute, absRoots) {
18050
18447
  for (const root of absRoots) {
18051
- const rel = relative5(root, absolute);
18448
+ const rel = relative7(root, absolute);
18052
18449
  if (rel === "" || rel === ".") return "";
18053
18450
  if (!rel.startsWith("..") && !rel.startsWith(`..${sep5}`)) {
18054
18451
  return rel.split(sep5).join("/");
@@ -18459,9 +18856,9 @@ function resolveScanRoots(inputs) {
18459
18856
  }
18460
18857
 
18461
18858
  // core/runtime/reference-paths-walker.ts
18462
- import { readdirSync as readdirSync8, statSync as statSync7 } from "fs";
18859
+ import { readdirSync as readdirSync9, statSync as statSync8 } from "fs";
18463
18860
  import { homedir as osHomedir2 } from "os";
18464
- import { isAbsolute as isAbsolute10, join as join14, resolve as resolve32 } from "path";
18861
+ import { isAbsolute as isAbsolute11, join as join14, resolve as resolve33 } from "path";
18465
18862
  var REFERENCE_WALK_MAX_FILES = 5e4;
18466
18863
  var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
18467
18864
  "node_modules",
@@ -18469,10 +18866,10 @@ var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
18469
18866
  SKILL_MAP_DIR
18470
18867
  ]);
18471
18868
  function resolveScanPath(raw, cwd) {
18472
- if (raw.startsWith("~/")) return resolve32(join14(osHomedir2(), raw.slice(2)));
18473
- if (raw === "~") return resolve32(osHomedir2());
18474
- if (isAbsolute10(raw)) return resolve32(raw);
18475
- return resolve32(cwd, raw);
18869
+ if (raw.startsWith("~/")) return resolve33(join14(osHomedir2(), raw.slice(2)));
18870
+ if (raw === "~") return resolve33(osHomedir2());
18871
+ if (isAbsolute11(raw)) return resolve33(raw);
18872
+ return resolve33(cwd, raw);
18476
18873
  }
18477
18874
  function walkReferencePaths(rawRoots, cwd) {
18478
18875
  const paths = /* @__PURE__ */ new Set();
@@ -18494,7 +18891,7 @@ function walkInto(dir, out) {
18494
18891
  if (out.size >= REFERENCE_WALK_MAX_FILES) return true;
18495
18892
  let entries;
18496
18893
  try {
18497
- entries = readdirSync8(dir, { withFileTypes: true });
18894
+ entries = readdirSync9(dir, { withFileTypes: true });
18498
18895
  } catch {
18499
18896
  return false;
18500
18897
  }
@@ -18513,15 +18910,15 @@ function walkInto(dir, out) {
18513
18910
  }
18514
18911
  function safeStat(path) {
18515
18912
  try {
18516
- return statSync7(path);
18913
+ return statSync8(path);
18517
18914
  } catch {
18518
18915
  return null;
18519
18916
  }
18520
18917
  }
18521
18918
 
18522
18919
  // core/runtime/active-provider-bootstrap.ts
18523
- import { createInterface as createInterface3 } from "readline";
18524
- import { isAbsolute as isAbsolute11, join as join15 } from "path";
18920
+ import { createInterface as createInterface4 } from "readline";
18921
+ import { isAbsolute as isAbsolute12, join as join15 } from "path";
18525
18922
  async function bootstrapActiveProvider(opts) {
18526
18923
  const fromCwd = resolveActiveProvider(opts.cwd, opts.providers);
18527
18924
  if (fromCwd.source === "config") {
@@ -18572,7 +18969,7 @@ function aggregateDetected(cwd, effectiveRoots, cwdDetected, providers) {
18572
18969
  out.push(id);
18573
18970
  }
18574
18971
  for (const root of effectiveRoots) {
18575
- const absRoot = isAbsolute11(root) ? root : join15(cwd, root);
18972
+ const absRoot = isAbsolute12(root) ? root : join15(cwd, root);
18576
18973
  const r = resolveActiveProvider(absRoot, providers);
18577
18974
  for (const id of r.detected) {
18578
18975
  if (seen.has(id)) continue;
@@ -18672,7 +19069,7 @@ async function promptForLens(detected, stdin, stderr, warnGlyph) {
18672
19069
  );
18673
19070
  }
18674
19071
  stderr.write(lines.join("\n") + "\n");
18675
- const rl = createInterface3({ input: stdin, output: stderr });
19072
+ const rl = createInterface4({ input: stdin, output: stderr });
18676
19073
  try {
18677
19074
  const answer = await new Promise(
18678
19075
  (resolveP) => rl.question(SCAN_RUNNER_TEXTS.activeProviderPromptInput, resolveP)
@@ -18690,8 +19087,8 @@ async function promptForLens(detected, stdin, stderr, warnGlyph) {
18690
19087
  }
18691
19088
 
18692
19089
  // core/sqlite/db-drift-reset.ts
18693
- import { existsSync as existsSync24 } from "fs";
18694
- import { createInterface as createInterface4 } from "readline";
19090
+ import { existsSync as existsSync25 } from "fs";
19091
+ import { createInterface as createInterface5 } from "readline";
18695
19092
  import { DatabaseSync as DatabaseSync8 } from "node:sqlite";
18696
19093
 
18697
19094
  // core/sqlite/i18n/db-drift.texts.ts
@@ -18742,7 +19139,7 @@ function detectDriftReason(dbPath, currentVersion) {
18742
19139
  return classifyFingerprint(dbPath).kind === "drift" ? "schema" : null;
18743
19140
  }
18744
19141
  function readScannedByVersion(dbPath) {
18745
- if (dbPath === ":memory:" || !existsSync24(dbPath)) return null;
19142
+ if (dbPath === ":memory:" || !existsSync25(dbPath)) return null;
18746
19143
  let raw = null;
18747
19144
  try {
18748
19145
  raw = new DatabaseSync8(dbPath, { readOnly: true });
@@ -18775,7 +19172,7 @@ async function askDriftReset(dbVersion, reason, policy) {
18775
19172
  hint: dim(DB_DRIFT_TEXTS.driftPromptHint)
18776
19173
  })
18777
19174
  );
18778
- const rl = createInterface4({ input: policy.stdin, output: policy.stderr });
19175
+ const rl = createInterface5({ input: policy.stdin, output: policy.stderr });
18779
19176
  try {
18780
19177
  const answer = await new Promise(
18781
19178
  (resolveP) => rl.question(DB_DRIFT_TEXTS.driftPromptQuestion, resolveP)
@@ -19134,7 +19531,7 @@ var INIT_TEXTS = {
19134
19531
  // cli/commands/init.ts
19135
19532
  var InitCommand = class extends SmCommand {
19136
19533
  static paths = [["init"]];
19137
- static usage = Command17.Usage({
19534
+ static usage = Command18.Usage({
19138
19535
  category: "Setup",
19139
19536
  description: "Bootstrap the current project: scaffold .skill-map/, provision DB, run first scan.",
19140
19537
  details: `
@@ -19153,16 +19550,16 @@ var InitCommand = class extends SmCommand {
19153
19550
  ["Preview what would be created", "$0 init --dry-run"]
19154
19551
  ]
19155
19552
  });
19156
- noScan = Option16.Boolean("--no-scan", false, {
19553
+ noScan = Option17.Boolean("--no-scan", false, {
19157
19554
  description: "Skip the first scan after scaffolding."
19158
19555
  });
19159
- force = Option16.Boolean("--force", false, {
19556
+ force = Option17.Boolean("--force", false, {
19160
19557
  description: "Overwrite an existing settings.json / settings.local.json / .skillmapignore."
19161
19558
  });
19162
- strict = Option16.Boolean("--strict", false, {
19559
+ strict = Option17.Boolean("--strict", false, {
19163
19560
  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."
19164
19561
  });
19165
- dryRun = Option16.Boolean("-n,--dry-run", false, {
19562
+ dryRun = Option17.Boolean("-n,--dry-run", false, {
19166
19563
  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."
19167
19564
  });
19168
19565
  // CLI orchestrator: paths setup + dry-run branch (delegated to
@@ -19415,7 +19812,7 @@ async function ensureGitignoreEntries(scopeRoot, entries) {
19415
19812
  }
19416
19813
 
19417
19814
  // cli/commands/history.ts
19418
- import { Command as Command18, Option as Option17 } from "clipanion";
19815
+ import { Command as Command19, Option as Option18 } from "clipanion";
19419
19816
 
19420
19817
  // cli/i18n/history.texts.ts
19421
19818
  var HISTORY_TEXTS = {
@@ -19533,7 +19930,7 @@ function parseStatuses(input, stderr, ansi) {
19533
19930
  }
19534
19931
  var HistoryCommand = class extends SmCommand {
19535
19932
  static paths = [["history"]];
19536
- static usage = Command18.Usage({
19933
+ static usage = Command19.Usage({
19537
19934
  category: "History",
19538
19935
  description: "Filter execution records. --json emits an array conforming to execution-record.schema.json.",
19539
19936
  details: `
@@ -19553,12 +19950,12 @@ var HistoryCommand = class extends SmCommand {
19553
19950
  ["Machine-readable, scoped to one node", "$0 history -n skills/foo.md --json"]
19554
19951
  ]
19555
19952
  });
19556
- node = Option17.String("-n", { required: false });
19557
- action = Option17.String("--action", { required: false });
19558
- status = Option17.String("--status", { required: false });
19559
- since = Option17.String("--since", { required: false });
19560
- until = Option17.String("--until", { required: false });
19561
- limit = Option17.String("--limit", { required: false });
19953
+ node = Option18.String("-n", { required: false });
19954
+ action = Option18.String("--action", { required: false });
19955
+ status = Option18.String("--status", { required: false });
19956
+ since = Option18.String("--since", { required: false });
19957
+ until = Option18.String("--until", { required: false });
19958
+ limit = Option18.String("--limit", { required: false });
19562
19959
  // CLI list verb: many optional filter flags (`--node`, `--action`,
19563
19960
  // `--status`, `--since`, `--until`, `--limit`, `--json`, `--quiet`)
19564
19961
  // each adding a guarded mutation to the filter or render path. Each
@@ -19609,7 +20006,7 @@ var HistoryCommand = class extends SmCommand {
19609
20006
  };
19610
20007
  var HistoryStatsCommand = class extends SmCommand {
19611
20008
  static paths = [["history", "stats"]];
19612
- static usage = Command18.Usage({
20009
+ static usage = Command19.Usage({
19613
20010
  category: "History",
19614
20011
  description: "Aggregate counts, tokens, periods, top nodes, and error rates over state_executions. --json conforms to history-stats.schema.json.",
19615
20012
  details: `
@@ -19627,10 +20024,10 @@ var HistoryStatsCommand = class extends SmCommand {
19627
20024
  ["Top 5 nodes, JSON", "$0 history stats --top 5 --json"]
19628
20025
  ]
19629
20026
  });
19630
- since = Option17.String("--since", { required: false });
19631
- until = Option17.String("--until", { required: false });
19632
- period = Option17.String("--period", { required: false });
19633
- top = Option17.String("--top", { required: false });
20027
+ since = Option18.String("--since", { required: false });
20028
+ until = Option18.String("--until", { required: false });
20029
+ period = Option18.String("--period", { required: false });
20030
+ top = Option18.String("--top", { required: false });
19634
20031
  // CLI stats verb: range parsing + window flags + period flag + JSON
19635
20032
  // branch + per-period iteration. Each branch is a single-purpose
19636
20033
  // gate; the data work lives in `aggregateHistoryStats`.
@@ -19931,20 +20328,20 @@ function trimMs(iso) {
19931
20328
 
19932
20329
  // cli/commands/jobs.ts
19933
20330
  import { unlink as unlink2 } from "fs/promises";
19934
- import { relative as relative6 } from "path";
19935
- import { Command as Command19, Option as Option18 } from "clipanion";
20331
+ import { relative as relative8 } from "path";
20332
+ import { Command as Command20, Option as Option19 } from "clipanion";
19936
20333
 
19937
20334
  // kernel/jobs/orphan-files.ts
19938
- import { readdirSync as readdirSync9, statSync as statSync8 } from "fs";
19939
- import { join as join17, resolve as resolve33 } from "path";
20335
+ import { readdirSync as readdirSync10, statSync as statSync9 } from "fs";
20336
+ import { join as join17, resolve as resolve34 } from "path";
19940
20337
  function findOrphanJobFiles(jobsDir, referencedPaths) {
19941
20338
  let entries;
19942
20339
  try {
19943
- const stat3 = statSync8(jobsDir);
20340
+ const stat3 = statSync9(jobsDir);
19944
20341
  if (!stat3.isDirectory()) {
19945
20342
  return { orphanFilePaths: [], referencedCount: referencedPaths.size };
19946
20343
  }
19947
- entries = readdirSync9(jobsDir, { withFileTypes: true });
20344
+ entries = readdirSync10(jobsDir, { withFileTypes: true });
19948
20345
  } catch {
19949
20346
  return { orphanFilePaths: [], referencedCount: referencedPaths.size };
19950
20347
  }
@@ -19954,7 +20351,7 @@ function findOrphanJobFiles(jobsDir, referencedPaths) {
19954
20351
  if (!entry.isFile()) continue;
19955
20352
  const name = entry.name;
19956
20353
  if (!name.endsWith(".md")) continue;
19957
- const abs = resolve33(join17(jobsDir, name));
20354
+ const abs = resolve34(join17(jobsDir, name));
19958
20355
  if (!referencedPaths.has(abs)) orphans.push(abs);
19959
20356
  }
19960
20357
  orphans.sort();
@@ -19983,7 +20380,7 @@ var JOBS_TEXTS = {
19983
20380
  // cli/commands/jobs.ts
19984
20381
  var JobPruneCommand = class extends SmCommand {
19985
20382
  static paths = [["job", "prune"]];
19986
- static usage = Command19.Usage({
20383
+ static usage = Command20.Usage({
19987
20384
  category: "Jobs",
19988
20385
  description: "Retention GC for completed / failed jobs (per config policy). --orphan-files removes MD files with no DB row.",
19989
20386
  details: `
@@ -20010,10 +20407,10 @@ var JobPruneCommand = class extends SmCommand {
20010
20407
  ["Preview without touching the DB", "$0 job prune --dry-run --json"]
20011
20408
  ]
20012
20409
  });
20013
- orphanFiles = Option18.Boolean("--orphan-files", false, {
20410
+ orphanFiles = Option19.Boolean("--orphan-files", false, {
20014
20411
  description: "Also remove MD files in .skill-map/jobs/ that have no matching state_jobs row."
20015
20412
  });
20016
- dryRun = Option18.Boolean("-n,--dry-run", false, {
20413
+ dryRun = Option19.Boolean("-n,--dry-run", false, {
20017
20414
  description: "Report what would be pruned without touching the DB or filesystem."
20018
20415
  });
20019
20416
  async run() {
@@ -20096,7 +20493,7 @@ var JobPruneCommand = class extends SmCommand {
20096
20493
  let removed = 0;
20097
20494
  for (const p of paths) {
20098
20495
  try {
20099
- assertContained(jobsDir, relative6(jobsDir, p));
20496
+ assertContained(jobsDir, relative8(jobsDir, p));
20100
20497
  } catch {
20101
20498
  continue;
20102
20499
  }
@@ -20151,7 +20548,7 @@ function formatPolicy(seconds) {
20151
20548
  }
20152
20549
 
20153
20550
  // cli/commands/list.ts
20154
- import { Command as Command20, Option as Option19 } from "clipanion";
20551
+ import { Command as Command21, Option as Option20 } from "clipanion";
20155
20552
 
20156
20553
  // cli/i18n/list.texts.ts
20157
20554
  var LIST_TEXTS = {
@@ -20186,7 +20583,7 @@ var SORT_BY = {
20186
20583
  var PATH_COL_MAX_WIDTH = 60;
20187
20584
  var ListCommand = class extends SmCommand {
20188
20585
  static paths = [["list"]];
20189
- static usage = Command20.Usage({
20586
+ static usage = Command21.Usage({
20190
20587
  category: "Browse",
20191
20588
  description: "Tabular listing of nodes. --json emits an array conforming to node.schema.json.",
20192
20589
  details: `
@@ -20210,11 +20607,11 @@ var ListCommand = class extends SmCommand {
20210
20607
  ["Filter by tag", "$0 list --tag urgent"]
20211
20608
  ]
20212
20609
  });
20213
- kind = Option19.String("--kind", { required: false });
20214
- issue = Option19.Boolean("--issue", false);
20215
- sortBy = Option19.String("--sort-by", { required: false });
20216
- limit = Option19.String("--limit", { required: false });
20217
- tag = Option19.String("--tag", { required: false });
20610
+ kind = Option20.String("--kind", { required: false });
20611
+ issue = Option20.Boolean("--issue", false);
20612
+ sortBy = Option20.String("--sort-by", { required: false });
20613
+ limit = Option20.String("--limit", { required: false });
20614
+ tag = Option20.String("--tag", { required: false });
20218
20615
  async run() {
20219
20616
  const stderrAnsi = this.ansiFor("stderr");
20220
20617
  const flags = this.#parseFlags(stderrAnsi);
@@ -20398,7 +20795,7 @@ function formatDataRow(r, w, ansi) {
20398
20795
  }
20399
20796
 
20400
20797
  // cli/commands/orphans.ts
20401
- import { Command as Command21, Option as Option20 } from "clipanion";
20798
+ import { Command as Command22, Option as Option21 } from "clipanion";
20402
20799
 
20403
20800
  // cli/i18n/orphans.texts.ts
20404
20801
  var ORPHANS_TEXTS = {
@@ -20481,7 +20878,7 @@ function isStringArray2(v) {
20481
20878
  }
20482
20879
  var OrphansCommand = class extends SmCommand {
20483
20880
  static paths = [["orphans"]];
20484
- static usage = Command21.Usage({
20881
+ static usage = Command22.Usage({
20485
20882
  category: "Browse",
20486
20883
  description: "List orphan / auto-rename issues from the last scan. --json emits an array conforming to issue.schema.json.",
20487
20884
  details: `
@@ -20496,7 +20893,7 @@ var OrphansCommand = class extends SmCommand {
20496
20893
  ["Just the ambiguous ones, JSON", "$0 orphans --kind ambiguous --json"]
20497
20894
  ]
20498
20895
  });
20499
- kind = Option20.String("--kind", { required: false });
20896
+ kind = Option21.String("--kind", { required: false });
20500
20897
  async run() {
20501
20898
  const stderrAnsi = this.ansiFor("stderr");
20502
20899
  let analyzerFilter = null;
@@ -20545,7 +20942,7 @@ var OrphansCommand = class extends SmCommand {
20545
20942
  };
20546
20943
  var OrphansReconcileCommand = class extends SmCommand {
20547
20944
  static paths = [["orphans", "reconcile"]];
20548
- static usage = Command21.Usage({
20945
+ static usage = Command22.Usage({
20549
20946
  category: "Browse",
20550
20947
  description: "Migrate state_* FKs from an orphan path to a live node, resolving the orphan issue.",
20551
20948
  details: `
@@ -20561,9 +20958,9 @@ var OrphansReconcileCommand = class extends SmCommand {
20561
20958
  ["Reattach orphan history", "$0 orphans reconcile skills/old.md --to skills/new.md"]
20562
20959
  ]
20563
20960
  });
20564
- orphanPath = Option20.String({ required: true });
20565
- to = Option20.String("--to", { required: true });
20566
- dryRun = Option20.Boolean("-n,--dry-run", false);
20961
+ orphanPath = Option21.String({ required: true });
20962
+ to = Option21.String("--to", { required: true });
20963
+ dryRun = Option21.Boolean("-n,--dry-run", false);
20567
20964
  async run() {
20568
20965
  const dbPath = resolveDbPath({ db: this.db, ...defaultRuntimeContext() });
20569
20966
  const exit = requireDbOrExit(dbPath, this.context.stderr);
@@ -20664,7 +21061,7 @@ var OrphansReconcileCommand = class extends SmCommand {
20664
21061
  };
20665
21062
  var OrphansUndoRenameCommand = class extends SmCommand {
20666
21063
  static paths = [["orphans", "undo-rename"]];
20667
- static usage = Command21.Usage({
21064
+ static usage = Command22.Usage({
20668
21065
  category: "Browse",
20669
21066
  description: "Reverse a medium- or ambiguous-confidence auto-rename. Migrates state_* FKs back, emits a new orphan on the prior path.",
20670
21067
  details: `
@@ -20684,10 +21081,10 @@ var OrphansUndoRenameCommand = class extends SmCommand {
20684
21081
  ["Undo an ambiguous, picking a candidate", "$0 orphans undo-rename skills/new.md --from skills/old-a.md"]
20685
21082
  ]
20686
21083
  });
20687
- newPath = Option20.String({ required: true });
20688
- from = Option20.String("--from", { required: false });
20689
- force = Option20.Boolean("--force", false);
20690
- dryRun = Option20.Boolean("-n,--dry-run", false);
21084
+ newPath = Option21.String({ required: true });
21085
+ from = Option21.String("--from", { required: false });
21086
+ force = Option21.Boolean("--force", false);
21087
+ dryRun = Option21.Boolean("-n,--dry-run", false);
20691
21088
  async run() {
20692
21089
  const dbPath = resolveDbPath({ db: this.db, ...defaultRuntimeContext() });
20693
21090
  const exit = requireDbOrExit(dbPath, this.context.stderr);
@@ -20910,7 +21307,7 @@ var ORPHANS_COMMANDS = [
20910
21307
  ];
20911
21308
 
20912
21309
  // cli/commands/plugins/list.ts
20913
- import { Command as Command22, Option as Option21 } from "clipanion";
21310
+ import { Command as Command23, Option as Option22 } from "clipanion";
20914
21311
 
20915
21312
  // cli/i18n/plugins.texts.ts
20916
21313
  var PLUGINS_TEXTS = {
@@ -21181,9 +21578,9 @@ function sortPluginsForPresentation(plugins) {
21181
21578
  }
21182
21579
 
21183
21580
  // cli/commands/plugins/shared.ts
21184
- import { resolve as resolve34 } from "path";
21581
+ import { resolve as resolve35 } from "path";
21185
21582
  function resolveSearchPaths2(opts, cwd) {
21186
- if (opts.pluginDir) return [resolve34(opts.pluginDir)];
21583
+ if (opts.pluginDir) return [resolve35(opts.pluginDir)];
21187
21584
  return [defaultProjectPluginsDir({ cwd })];
21188
21585
  }
21189
21586
  async function buildResolver() {
@@ -21301,7 +21698,7 @@ function wrapText(text, maxWidth) {
21301
21698
  // cli/commands/plugins/list.ts
21302
21699
  var PluginsListCommand = class extends SmCommand {
21303
21700
  static paths = [["plugins", "list"]];
21304
- static usage = Command22.Usage({
21701
+ static usage = Command23.Usage({
21305
21702
  category: "Plugins",
21306
21703
  description: "List discovered plugins, or one plugin's extensions.",
21307
21704
  details: `
@@ -21312,8 +21709,8 @@ var PluginsListCommand = class extends SmCommand {
21312
21709
  \`<plugin>/<ext>\` id is rejected with a redirect to \`sm plugins show\`.
21313
21710
  `
21314
21711
  });
21315
- id = Option21.String({ required: false });
21316
- pluginDir = Option21.String("--plugin-dir", { required: false });
21712
+ id = Option22.String({ required: false });
21713
+ pluginDir = Option22.String("--plugin-dir", { required: false });
21317
21714
  async run() {
21318
21715
  const plugins = await loadAll({ pluginDir: this.pluginDir });
21319
21716
  const resolveEnabled = await buildResolver();
@@ -21555,10 +21952,10 @@ function renderExtensionItems(items) {
21555
21952
  }
21556
21953
 
21557
21954
  // cli/commands/plugins/show.ts
21558
- import { Command as Command23, Option as Option22 } from "clipanion";
21955
+ import { Command as Command24, Option as Option23 } from "clipanion";
21559
21956
  var PluginsShowCommand = class extends SmCommand {
21560
21957
  static paths = [["plugins", "show"]];
21561
- static usage = Command23.Usage({
21958
+ static usage = Command24.Usage({
21562
21959
  category: "Plugins",
21563
21960
  description: "Show a single extension's detail.",
21564
21961
  details: `
@@ -21571,8 +21968,8 @@ var PluginsShowCommand = class extends SmCommand {
21571
21968
  accept resolves cleanly here too.
21572
21969
  `
21573
21970
  });
21574
- id = Option22.String({ required: true });
21575
- pluginDir = Option22.String("--plugin-dir", { required: false });
21971
+ id = Option23.String({ required: true });
21972
+ pluginDir = Option23.String("--plugin-dir", { required: false });
21576
21973
  async run() {
21577
21974
  const plugins = await loadAll({ pluginDir: this.pluginDir });
21578
21975
  const resolveEnabled = await buildResolver();
@@ -21713,7 +22110,7 @@ function renderExtensionFields(meta) {
21713
22110
  }
21714
22111
 
21715
22112
  // cli/commands/plugins/doctor.ts
21716
- import { Command as Command24, Option as Option23 } from "clipanion";
22113
+ import { Command as Command25, Option as Option24 } from "clipanion";
21717
22114
  var CONTRIB_ERROR_SAMPLE_CAP = 3;
21718
22115
  var STATUS_ORDER = [
21719
22116
  "enabled",
@@ -21726,12 +22123,12 @@ var STATUS_ORDER = [
21726
22123
  ];
21727
22124
  var PluginsDoctorCommand = class extends SmCommand {
21728
22125
  static paths = [["plugins", "doctor"]];
21729
- static usage = Command24.Usage({
22126
+ static usage = Command25.Usage({
21730
22127
  category: "Plugins",
21731
22128
  description: "Run the full load pass and summarise by failure mode.",
21732
22129
  details: "Exit code 0 when every plugin loads or is intentionally disabled; 1 when any plugin is in an error / incompat state."
21733
22130
  });
21734
- pluginDir = Option23.String("--plugin-dir", { required: false });
22131
+ pluginDir = Option24.String("--plugin-dir", { required: false });
21735
22132
  async run() {
21736
22133
  const plugins = await loadAll({ pluginDir: this.pluginDir });
21737
22134
  const resolveEnabled = await buildResolver();
@@ -22184,13 +22581,13 @@ function groupContributionErrorsByPlugin(errors) {
22184
22581
  }
22185
22582
 
22186
22583
  // cli/commands/plugins/toggle.ts
22187
- import { Command as Command25, Option as Option24 } from "clipanion";
22584
+ import { Command as Command26, Option as Option25 } from "clipanion";
22188
22585
  var TogglePluginsBase = class extends SmCommand {
22189
- all = Option24.Boolean("--all", false);
22190
- yes = Option24.Boolean("--yes,-y", false, {
22586
+ all = Option25.Boolean("--all", false);
22587
+ yes = Option25.Boolean("--yes,-y", false, {
22191
22588
  description: "Skip the interactive confirm when a bare plugin id (or --all) fans the toggle out across multiple extensions."
22192
22589
  });
22193
- ids = Option24.Rest({ name: "ids" });
22590
+ ids = Option25.Rest({ name: "ids" });
22194
22591
  async toggle(enabled) {
22195
22592
  const verb = enabled ? "enable" : "disable";
22196
22593
  const stderrAnsi = this.ansiFor("stderr");
@@ -22413,7 +22810,7 @@ async function purgeContributionsFor(adapter, id) {
22413
22810
  }
22414
22811
  var PluginsEnableCommand = class extends TogglePluginsBase {
22415
22812
  static paths = [["plugins", "enable"]];
22416
- static usage = Command25.Usage({
22813
+ static usage = Command26.Usage({
22417
22814
  category: "Plugins",
22418
22815
  description: "Enable one or more extensions (or --all). Persists in config_plugins.",
22419
22816
  details: `
@@ -22441,7 +22838,7 @@ var PluginsEnableCommand = class extends TogglePluginsBase {
22441
22838
  };
22442
22839
  var PluginsDisableCommand = class extends TogglePluginsBase {
22443
22840
  static paths = [["plugins", "disable"]];
22444
- static usage = Command25.Usage({
22841
+ static usage = Command26.Usage({
22445
22842
  category: "Plugins",
22446
22843
  description: "Disable one or more extensions (or --all). Persists in config_plugins; does not delete files.",
22447
22844
  details: `
@@ -22496,9 +22893,9 @@ function resolveBareToggle(id, catalogue) {
22496
22893
  }
22497
22894
 
22498
22895
  // cli/commands/plugins/create.ts
22499
- import { existsSync as existsSync25, mkdirSync as mkdirSync5, writeFileSync } from "fs";
22500
- import { dirname as dirname17, join as join18, resolve as resolve35 } from "path";
22501
- import { Command as Command26, Option as Option25 } from "clipanion";
22896
+ import { existsSync as existsSync26, mkdirSync as mkdirSync5, writeFileSync } from "fs";
22897
+ import { dirname as dirname18, join as join18, resolve as resolve36 } from "path";
22898
+ import { Command as Command27, Option as Option26 } from "clipanion";
22502
22899
 
22503
22900
  // cli/commands/plugins/scaffold/action.ts
22504
22901
  function indexStub(extId) {
@@ -22903,7 +23300,7 @@ function generateScaffold(kind, pluginId, specVersion) {
22903
23300
  // cli/commands/plugins/create.ts
22904
23301
  var PluginsCreateCommand = class extends SmCommand {
22905
23302
  static paths = [["plugins", "create"]];
22906
- static usage = Command26.Usage({
23303
+ static usage = Command27.Usage({
22907
23304
  category: "Plugins",
22908
23305
  description: "Scaffold a new plugin directory.",
22909
23306
  details: "Emits plugin.json + a per-kind extension stub + README. `<kind>` is one of: provider, extractor, analyzer, action, formatter, hook. The extractor stub ships one view contribution (slot `card.footer.left`) and one setting (`string-list`); edit to taste. Use `sm plugins slots list` to browse the slot / input-type catalog.",
@@ -22915,10 +23312,10 @@ var PluginsCreateCommand = class extends SmCommand {
22915
23312
  });
22916
23313
  // First positional: the extension kind (required). Declared before
22917
23314
  // `pluginId` so clipanion assigns it the first positional slot.
22918
- kind = Option25.String({ required: true, name: "kind" });
22919
- pluginId = Option25.String({ required: true, name: "plugin-id" });
22920
- at = Option25.String("--at", { required: false });
22921
- force = Option25.Boolean("--force", false);
23315
+ kind = Option26.String({ required: true, name: "kind" });
23316
+ pluginId = Option26.String({ required: true, name: "plugin-id" });
23317
+ at = Option26.String("--at", { required: false });
23318
+ force = Option26.Boolean("--force", false);
22922
23319
  async run() {
22923
23320
  const ansi = this.ansiFor("stderr");
22924
23321
  const errGlyph = ansi.red("\u2715");
@@ -22947,8 +23344,8 @@ var PluginsCreateCommand = class extends SmCommand {
22947
23344
  const kind = this.kind;
22948
23345
  const ctx = defaultRuntimeContext();
22949
23346
  const baseDir = defaultProjectPluginsDir(ctx);
22950
- const targetDir = this.at ? resolve35(this.at) : join18(baseDir, this.pluginId);
22951
- if (existsSync25(targetDir) && !this.force) {
23347
+ const targetDir = this.at ? resolve36(this.at) : join18(baseDir, this.pluginId);
23348
+ if (existsSync26(targetDir) && !this.force) {
22952
23349
  this.printer.error(
22953
23350
  tx(PLUGINS_TEXTS.createRefuseOverwrite, {
22954
23351
  glyph: errGlyph,
@@ -22962,7 +23359,7 @@ var PluginsCreateCommand = class extends SmCommand {
22962
23359
  const files = generateScaffold(kind, this.pluginId, specVersion);
22963
23360
  for (const file of files) {
22964
23361
  const abs = join18(targetDir, file.relPath);
22965
- mkdirSync5(dirname17(abs), { recursive: true });
23362
+ mkdirSync5(dirname18(abs), { recursive: true });
22966
23363
  writeFileSync(abs, file.contents);
22967
23364
  }
22968
23365
  const mainFile = `${kind}s/${this.pluginId}-${kind}/index.js`;
@@ -22977,7 +23374,7 @@ var PluginsCreateCommand = class extends SmCommand {
22977
23374
  };
22978
23375
 
22979
23376
  // cli/commands/plugins/slots.ts
22980
- import { Command as Command27 } from "clipanion";
23377
+ import { Command as Command28 } from "clipanion";
22981
23378
 
22982
23379
  // cli/commands/plugins/slots-catalog.ts
22983
23380
  var VIEW_SLOTS_CATALOG = [
@@ -23013,7 +23410,7 @@ var INPUT_TYPES_CATALOG = [
23013
23410
  // cli/commands/plugins/slots.ts
23014
23411
  var PluginsSlotsListCommand = class extends SmCommand {
23015
23412
  static paths = [["plugins", "slots", "list"]];
23016
- static usage = Command27.Usage({
23413
+ static usage = Command28.Usage({
23017
23414
  category: "Plugins",
23018
23415
  description: "Print the closed catalogs of view slots and input-types.",
23019
23416
  details: "Read-only. Use this when picking a slot / input-type for a new plugin."
@@ -23062,15 +23459,15 @@ var PluginsSlotsListCommand = class extends SmCommand {
23062
23459
  };
23063
23460
 
23064
23461
  // cli/commands/plugins/upgrade.ts
23065
- import { Command as Command28, Option as Option26 } from "clipanion";
23462
+ import { Command as Command29, Option as Option27 } from "clipanion";
23066
23463
  var PluginsUpgradeCommand = class extends SmCommand {
23067
23464
  static paths = [["plugins", "upgrade"]];
23068
- static usage = Command28.Usage({
23465
+ static usage = Command29.Usage({
23069
23466
  category: "Plugins",
23070
23467
  description: "Apply catalog migrations to plugin manifests.",
23071
23468
  details: "No migrations registered against catalog v1.0.0 yet; this verb is a no-op today. The structure exists so future slot renames / deprecations land without spec churn."
23072
23469
  });
23073
- pluginId = Option26.String({ required: false, name: "plugin-id" });
23470
+ pluginId = Option27.String({ required: false, name: "plugin-id" });
23074
23471
  async run() {
23075
23472
  this.printer.data(
23076
23473
  "sm plugins upgrade: no migrations registered for catalog v1.0.0.\n All loaded plugins are catalog-current.\n Run `sm plugins doctor` to surface any incompatible-catalog status.\n"
@@ -23080,7 +23477,7 @@ var PluginsUpgradeCommand = class extends SmCommand {
23080
23477
  };
23081
23478
 
23082
23479
  // cli/commands/plugins/config.ts
23083
- import { Command as Command29, Option as Option27 } from "clipanion";
23480
+ import { Command as Command30, Option as Option28 } from "clipanion";
23084
23481
 
23085
23482
  // cli/i18n/plugins-config.texts.ts
23086
23483
  var PLUGINS_CONFIG_TEXTS = {
@@ -23121,7 +23518,7 @@ var PLUGINS_CONFIG_TEXTS = {
23121
23518
  // cli/commands/plugins/config.ts
23122
23519
  var PluginsConfigCommand = class extends SmCommand {
23123
23520
  static paths = [["plugins", "config"]];
23124
- static usage = Command29.Usage({
23521
+ static usage = Command30.Usage({
23125
23522
  category: "Plugins",
23126
23523
  description: "Read or write an extension's declared settings.",
23127
23524
  details: `
@@ -23136,13 +23533,13 @@ var PluginsConfigCommand = class extends SmCommand {
23136
23533
  Secret values are shown as <redacted>. Run \`sm scan\` to apply.
23137
23534
  `
23138
23535
  });
23139
- id = Option27.String({ required: true });
23140
- settingId = Option27.String({ required: false });
23141
- value = Option27.String({ required: false });
23142
- reset = Option27.Boolean("--reset", false, {
23536
+ id = Option28.String({ required: true });
23537
+ settingId = Option28.String({ required: false });
23538
+ value = Option28.String({ required: false });
23539
+ reset = Option28.Boolean("--reset", false, {
23143
23540
  description: "Remove the override for <settingId> so the manifest default applies."
23144
23541
  });
23145
- pluginDir = Option27.String("--plugin-dir", { required: false });
23542
+ pluginDir = Option28.String("--plugin-dir", { required: false });
23146
23543
  // Read-only when listing; the write / reset paths emit their own
23147
23544
  // receipt. `sm config` exempts the config family from "done in <…>";
23148
23545
  // mirror that here for the read path. The write path keeps the line.
@@ -23463,8 +23860,8 @@ var PLUGIN_COMMANDS = [
23463
23860
 
23464
23861
  // cli/commands/refresh.ts
23465
23862
  import { readFile as readFile4 } from "fs/promises";
23466
- import { resolve as resolve36 } from "path";
23467
- import { Command as Command30, Option as Option28 } from "clipanion";
23863
+ import { resolve as resolve37 } from "path";
23864
+ import { Command as Command31, Option as Option29 } from "clipanion";
23468
23865
 
23469
23866
  // cli/i18n/refresh.texts.ts
23470
23867
  var REFRESH_TEXTS = {
@@ -23520,7 +23917,7 @@ var REFRESH_TEXTS = {
23520
23917
  // cli/commands/refresh.ts
23521
23918
  var RefreshCommand = class extends SmCommand {
23522
23919
  static paths = [["refresh"]];
23523
- static usage = Command30.Usage({
23920
+ static usage = Command31.Usage({
23524
23921
  category: "Scan",
23525
23922
  description: "Refresh enrichment rows: granular (single node) or batch (every stale row).",
23526
23923
  details: `
@@ -23542,11 +23939,11 @@ var RefreshCommand = class extends SmCommand {
23542
23939
  ["Refresh every node with stale enrichments", "$0 refresh --stale"]
23543
23940
  ]
23544
23941
  });
23545
- nodePath = Option28.String({ name: "node", required: false });
23546
- stale = Option28.Boolean("--stale", false, {
23942
+ nodePath = Option29.String({ name: "node", required: false });
23943
+ stale = Option29.Boolean("--stale", false, {
23547
23944
  description: "Refresh every node carrying a stale enrichment row (no-op in this revision; reserved for future Action-prob enrichments)."
23548
23945
  });
23549
- noPlugins = Option28.Boolean("--no-plugins", false, {
23946
+ noPlugins = Option29.Boolean("--no-plugins", false, {
23550
23947
  description: "Skip drop-in plugin discovery; use only the built-in extractor set."
23551
23948
  });
23552
23949
  // The remaining cyclomatic count comes from CLI ergonomics that don't
@@ -23771,7 +24168,7 @@ var RefreshCommand = class extends SmCommand {
23771
24168
  let body;
23772
24169
  try {
23773
24170
  assertContained(cwd, node.path);
23774
- const raw = await readFile4(resolve36(cwd, node.path), "utf8");
24171
+ const raw = await readFile4(resolve37(cwd, node.path), "utf8");
23775
24172
  body = stripFrontmatterFence(raw);
23776
24173
  } catch (err) {
23777
24174
  if (!this.json) {
@@ -23844,13 +24241,13 @@ var IntentionalFailCommand = class extends SmCommand {
23844
24241
  setTimeout(() => {
23845
24242
  throw new Error(INTENTIONAL_FAIL_TEXTS.errorMessage);
23846
24243
  }, 0);
23847
- await new Promise((resolve43) => setTimeout(resolve43, 5e3));
24244
+ await new Promise((resolve44) => setTimeout(resolve44, 5e3));
23848
24245
  return ExitCode.Issues;
23849
24246
  }
23850
24247
  };
23851
24248
 
23852
24249
  // cli/commands/scan.ts
23853
- import { Command as Command32, Option as Option30 } from "clipanion";
24250
+ import { Command as Command33, Option as Option31 } from "clipanion";
23854
24251
 
23855
24252
  // kernel/util/format-bytes.ts
23856
24253
  var UNITS = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"];
@@ -24009,10 +24406,10 @@ var SCAN_TEXTS = {
24009
24406
  };
24010
24407
 
24011
24408
  // cli/commands/watch.ts
24012
- import { Command as Command31, Option as Option29 } from "clipanion";
24409
+ import { Command as Command32, Option as Option30 } from "clipanion";
24013
24410
 
24014
24411
  // core/watcher/runtime.ts
24015
- import { dirname as dirname18, isAbsolute as isAbsolute12, relative as relative7, resolve as resolve37, sep as sep6 } from "path";
24412
+ import { dirname as dirname19, isAbsolute as isAbsolute13, relative as relative9, resolve as resolve38, sep as sep6 } from "path";
24016
24413
 
24017
24414
  // core/runtime/fresh-resolver.ts
24018
24415
  async function buildFreshResolver(deps) {
@@ -24058,7 +24455,7 @@ function applyPriorStateToRunOptions(runOptions, priorState, changedPaths) {
24058
24455
  }
24059
24456
  }
24060
24457
  function toIncrementalPaths(events, roots, cwd) {
24061
- const absRoots = roots.map((r) => isAbsolute12(r) ? r : resolve37(cwd, r));
24458
+ const absRoots = roots.map((r) => isAbsolute13(r) ? r : resolve38(cwd, r));
24062
24459
  const changed = /* @__PURE__ */ new Set();
24063
24460
  const removed = /* @__PURE__ */ new Set();
24064
24461
  for (const ev of events) {
@@ -24072,8 +24469,8 @@ function toIncrementalPaths(events, roots, cwd) {
24072
24469
  }
24073
24470
  function relativeFromRoots2(absolute, absRoots) {
24074
24471
  for (const root of absRoots) {
24075
- const rel = relative7(root, absolute);
24076
- if (rel === "" || rel.startsWith("..") || isAbsolute12(rel)) continue;
24472
+ const rel = relative9(root, absolute);
24473
+ if (rel === "" || rel.startsWith("..") || isAbsolute13(rel)) continue;
24077
24474
  return rel.split(sep6).join("/");
24078
24475
  }
24079
24476
  return null;
@@ -24316,7 +24713,7 @@ function createWatcherRuntime(opts) {
24316
24713
  roots: [
24317
24714
  cwd,
24318
24715
  // parent of `.skillmapignore`
24319
- dirname18(settingsPath)
24716
+ dirname19(settingsPath)
24320
24717
  // parent of `.skill-map/settings.json`
24321
24718
  ],
24322
24719
  cwd,
@@ -24606,7 +25003,7 @@ async function runWatchLoop(opts) {
24606
25003
  }
24607
25004
  var WatchCommand = class extends SmCommand {
24608
25005
  static paths = [["watch"]];
24609
- static usage = Command31.Usage({
25006
+ static usage = Command32.Usage({
24610
25007
  category: "Scan",
24611
25008
  description: "Watch roots and run an incremental scan after each debounced batch of filesystem events.",
24612
25009
  details: `
@@ -24630,25 +25027,25 @@ var WatchCommand = class extends SmCommand {
24630
25027
  ["Stream ScanResult per batch as ndjson", "$0 watch --json"]
24631
25028
  ]
24632
25029
  });
24633
- roots = Option29.Rest({ name: "roots" });
24634
- noTokens = Option29.Boolean("--no-tokens", false, {
25030
+ roots = Option30.Rest({ name: "roots" });
25031
+ noTokens = Option30.Boolean("--no-tokens", false, {
24635
25032
  description: "Skip per-node token counts (cl100k_base BPE)."
24636
25033
  });
24637
- strict = Option29.Boolean("--strict", false, {
25034
+ strict = Option30.Boolean("--strict", false, {
24638
25035
  description: "Promote frontmatter-validation findings from warn to error inside each batch. Does not change the watcher exit code."
24639
25036
  });
24640
- noPlugins = Option29.Boolean("--no-plugins", false, {
25037
+ noPlugins = Option30.Boolean("--no-plugins", false, {
24641
25038
  description: "Skip drop-in plugin discovery for the watcher session."
24642
25039
  });
24643
- maxConsecutiveFailures = Option29.String("--max-consecutive-failures", {
25040
+ maxConsecutiveFailures = Option30.String("--max-consecutive-failures", {
24644
25041
  required: false,
24645
25042
  description: "Shut down with exit 2 after N consecutive batch failures (default 5; 0 disables the breaker)."
24646
25043
  });
24647
- maxScan = Option29.String("--max-scan", {
25044
+ maxScan = Option30.String("--max-scan", {
24648
25045
  required: false,
24649
25046
  description: "Per-batch override of scan.maxScan (default 50000), the WALK-INTAKE ceiling. The scan walks, parses, analyzes, and reference-validates the full corpus up to this number. Bidirectional: raises OR lowers the ceiling. When a batch hits it, additional files are dropped in stable order and the UI surfaces the persistent truncation banner. Validation: integer >= 1."
24650
25047
  });
24651
- maxNodes = Option29.String("--max-nodes", {
25048
+ maxNodes = Option30.String("--max-nodes", {
24652
25049
  required: false,
24653
25050
  description: "Per-batch override of scan.maxNodes (default 256), the MAP RENDER cap (pure metadata): it does NOT bound the scan, only the graph projection. Bidirectional: raises OR lowers the render cap. Validation: integer >= 1."
24654
25051
  });
@@ -24735,7 +25132,7 @@ function parseMaxNodesLimit(raw, stderr, noColor) {
24735
25132
  // cli/commands/scan.ts
24736
25133
  var ScanCommand = class extends SmCommand {
24737
25134
  static paths = [["scan"]];
24738
- static usage = Command32.Usage({
25135
+ static usage = Command33.Usage({
24739
25136
  category: "Scan",
24740
25137
  description: "Scan roots for markdown nodes, run extractors and analyzers.",
24741
25138
  details: `
@@ -24770,39 +25167,39 @@ var ScanCommand = class extends SmCommand {
24770
25167
  ["What would the next incremental scan persist?", "$0 scan --changed -n --json"]
24771
25168
  ]
24772
25169
  });
24773
- roots = Option30.Rest({ name: "roots" });
24774
- noBuiltIns = Option30.Boolean("--no-built-ins", false, {
25170
+ roots = Option31.Rest({ name: "roots" });
25171
+ noBuiltIns = Option31.Boolean("--no-built-ins", false, {
24775
25172
  description: "Skip the built-in extension set. Yields a zero-filled ScanResult (kernel-empty-boot parity); skips DB persistence."
24776
25173
  });
24777
- noPlugins = Option30.Boolean("--no-plugins", false, {
25174
+ noPlugins = Option31.Boolean("--no-plugins", false, {
24778
25175
  description: "Skip drop-in plugin discovery. Only the built-in set runs. Combine with --no-built-ins for a fully empty pipeline."
24779
25176
  });
24780
- noTokens = Option30.Boolean("--no-tokens", false, {
25177
+ noTokens = Option31.Boolean("--no-tokens", false, {
24781
25178
  description: "Skip per-node token counts (cl100k_base BPE). Leaves node.tokens undefined; spec-valid since the field is optional."
24782
25179
  });
24783
- dryRun = Option30.Boolean("-n,--dry-run", false, {
25180
+ dryRun = Option31.Boolean("-n,--dry-run", false, {
24784
25181
  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."
24785
25182
  });
24786
- changed = Option30.Boolean("--changed", false, {
25183
+ changed = Option31.Boolean("--changed", false, {
24787
25184
  description: "Incremental scan: reuse unchanged nodes from the persisted prior snapshot. Degrades to a full scan if no prior snapshot exists."
24788
25185
  });
24789
- allowEmpty = Option30.Boolean("--allow-empty", false, {
25186
+ allowEmpty = Option31.Boolean("--allow-empty", false, {
24790
25187
  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."
24791
25188
  });
24792
- strict = Option30.Boolean("--strict", false, {
25189
+ strict = Option31.Boolean("--strict", false, {
24793
25190
  description: "Promote frontmatter-validation findings from warn to error (exit code 1 on any violation). Overrides scan.strict from config when both are set."
24794
25191
  });
24795
- watch = Option30.Boolean("--watch", false, {
25192
+ watch = Option31.Boolean("--watch", false, {
24796
25193
  description: "Long-running mode: watch the roots and trigger an incremental scan after each debounced batch of filesystem events. Alias of `sm watch`."
24797
25194
  });
24798
- yes = Option30.Boolean("--yes", false, {
25195
+ yes = Option31.Boolean("--yes", false, {
24799
25196
  description: "Non-interactive mode. For ambiguous activeProvider auto-detect, multiple provider markers (.claude/, .codex/, AGENTS.md, .cursor/) under the scan tree exit non-zero instead of prompting; set the lens manually via `sm config set activeProvider <id>` and re-run. Also auto-confirms the pre-1.0 schema-drift rebuild (when the DB was written by a different skill-map major.minor it is deleted and regenerated) instead of prompting."
24800
25197
  });
24801
- maxScan = Option30.String("--max-scan", {
25198
+ maxScan = Option31.String("--max-scan", {
24802
25199
  required: false,
24803
25200
  description: "Per-invocation override of `scan.maxScan` (default 50000). The WALK-INTAKE ceiling: the scan walks, parses, analyzes, and reference-validates the full corpus up to this number. Bidirectional: raises OR lowers the ceiling. When the walker hits it, additional files are dropped in stable order and the scan is marked truncated in scan_meta (the UI raises a persistent banner pointing at the .skillmapignore editor in Settings \u2192 Project). Validation: integer >= 1."
24804
25201
  });
24805
- maxNodes = Option30.String("--max-nodes", {
25202
+ maxNodes = Option31.String("--max-nodes", {
24806
25203
  required: false,
24807
25204
  description: "Per-invocation override of `scan.maxNodes` (default 256). The MAP RENDER cap (pure metadata): it does NOT bound the scan, only how many nodes the graph view projects onto the canvas. Bidirectional: raises OR lowers the render cap. Validation: integer >= 1."
24808
25205
  });
@@ -25163,10 +25560,10 @@ function capOverrides(caps) {
25163
25560
 
25164
25561
  // cli/commands/scan-compare.ts
25165
25562
  import { access, readFile as readFile5 } from "fs/promises";
25166
- import { Command as Command33, Option as Option31 } from "clipanion";
25563
+ import { Command as Command34, Option as Option32 } from "clipanion";
25167
25564
  var ScanCompareCommand = class extends SmCommand {
25168
25565
  static paths = [["scan", "compare-with"]];
25169
- static usage = Command33.Usage({
25566
+ static usage = Command34.Usage({
25170
25567
  category: "Scan",
25171
25568
  description: "Run a fresh scan in memory and emit a delta against the saved ScanResult dump at <dump>. Read-only.",
25172
25569
  details: `
@@ -25194,15 +25591,15 @@ var ScanCompareCommand = class extends SmCommand {
25194
25591
  ["JSON output for tooling", "$0 scan compare-with baseline.json --json"]
25195
25592
  ]
25196
25593
  });
25197
- dump = Option31.String({ required: true });
25198
- roots = Option31.Rest({ name: "roots" });
25199
- noTokens = Option31.Boolean("--no-tokens", false, {
25594
+ dump = Option32.String({ required: true });
25595
+ roots = Option32.Rest({ name: "roots" });
25596
+ noTokens = Option32.Boolean("--no-tokens", false, {
25200
25597
  description: "Skip per-node token counts during the fresh scan."
25201
25598
  });
25202
- strict = Option31.Boolean("--strict", false, {
25599
+ strict = Option32.Boolean("--strict", false, {
25203
25600
  description: "Promote layered-config warnings and frontmatter-validation findings from warn to error."
25204
25601
  });
25205
- noPlugins = Option31.Boolean("--no-plugins", false, {
25602
+ noPlugins = Option32.Boolean("--no-plugins", false, {
25206
25603
  description: "Skip drop-in plugin discovery."
25207
25604
  });
25208
25605
  // Cyclomatic count comes from CLI ergonomics: 3 distinct try/catch
@@ -25408,13 +25805,13 @@ function renderDeltaIssues(issues) {
25408
25805
 
25409
25806
  // cli/commands/serve.ts
25410
25807
  import { spawn as spawn2 } from "child_process";
25411
- import { existsSync as existsSync31 } from "fs";
25412
- import { Command as Command34, Option as Option32 } from "clipanion";
25808
+ import { existsSync as existsSync32 } from "fs";
25809
+ import { Command as Command35, Option as Option33 } from "clipanion";
25413
25810
 
25414
25811
  // kernel/util/dev-mode.ts
25415
25812
  import { sep as sep7 } from "path";
25416
- import { fileURLToPath as fileURLToPath5 } from "url";
25417
- var SELF_PATH = fileURLToPath5(import.meta.url);
25813
+ import { fileURLToPath as fileURLToPath6 } from "url";
25814
+ var SELF_PATH = fileURLToPath6(import.meta.url);
25418
25815
  var IS_DEV_BUILD = isDevBuildFromPath(SELF_PATH, sep7);
25419
25816
  function isDevBuildFromPath(filePath, separator = sep7) {
25420
25817
  return !filePath.includes(`${separator}node_modules${separator}`);
@@ -26370,7 +26767,7 @@ function contentTypeFor(format) {
26370
26767
  }
26371
26768
 
26372
26769
  // server/health.ts
26373
- import { existsSync as existsSync26 } from "fs";
26770
+ import { existsSync as existsSync27 } from "fs";
26374
26771
  var FALLBACK_SCHEMA_VERSION = "1";
26375
26772
  function buildHealth(deps) {
26376
26773
  const dev = isDevBuild();
@@ -26379,7 +26776,7 @@ function buildHealth(deps) {
26379
26776
  schemaVersion: FALLBACK_SCHEMA_VERSION,
26380
26777
  specVersion: deps.specVersion,
26381
26778
  implVersion: VERSION,
26382
- db: existsSync26(deps.dbPath) ? "present" : "missing",
26779
+ db: existsSync27(deps.dbPath) ? "present" : "missing",
26383
26780
  cwd: deps.cwd,
26384
26781
  dbPath: deps.dbPath,
26385
26782
  // Only emit when truthy so a published install keeps the wire
@@ -26512,9 +26909,9 @@ import { HTTPException as HTTPException8 } from "hono/http-exception";
26512
26909
  // server/node-body.ts
26513
26910
  import { constants as fsConstants2 } from "fs";
26514
26911
  import { open } from "fs/promises";
26515
- import { isAbsolute as isAbsolute13, resolve as resolvePath2, relative as relativePath, sep as sep8 } from "path";
26912
+ import { isAbsolute as isAbsolute14, resolve as resolvePath2, relative as relativePath, sep as sep8 } from "path";
26516
26913
  async function readNodeBody(cwd, relPath) {
26517
- if (isAbsolute13(relPath)) return null;
26914
+ if (isAbsolute14(relPath)) return null;
26518
26915
  const absRoot = resolvePath2(cwd);
26519
26916
  const absFile = resolvePath2(absRoot, relPath);
26520
26917
  const rel = relativePath(absRoot, absFile);
@@ -27501,12 +27898,12 @@ var parsePatchBody2 = makeBodyValidator(PATCH_BODY_SCHEMA, {
27501
27898
  import { HTTPException as HTTPException12 } from "hono/http-exception";
27502
27899
 
27503
27900
  // server/util/skillmapignore-io.ts
27504
- import { existsSync as existsSync27, readFileSync as readFileSync18, writeFileSync as writeFileSync2 } from "fs";
27505
- import { resolve as resolve38 } from "path";
27901
+ import { existsSync as existsSync28, readFileSync as readFileSync18, writeFileSync as writeFileSync2 } from "fs";
27902
+ import { resolve as resolve39 } from "path";
27506
27903
  var IGNORE_FILENAME2 = ".skillmapignore";
27507
27904
  function readPatterns(cwd) {
27508
- const path = resolve38(cwd, IGNORE_FILENAME2);
27509
- if (!existsSync27(path)) return [];
27905
+ const path = resolve39(cwd, IGNORE_FILENAME2);
27906
+ if (!existsSync28(path)) return [];
27510
27907
  let raw;
27511
27908
  try {
27512
27909
  raw = readFileSync18(path, "utf8");
@@ -27516,8 +27913,8 @@ function readPatterns(cwd) {
27516
27913
  return raw.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
27517
27914
  }
27518
27915
  function writePatterns(cwd, nextPatterns) {
27519
- const path = resolve38(cwd, IGNORE_FILENAME2);
27520
- const prior = existsSync27(path) ? safeRead(path) : "";
27916
+ const path = resolve39(cwd, IGNORE_FILENAME2);
27917
+ const prior = existsSync28(path) ? safeRead(path) : "";
27521
27918
  const content = buildContent(prior, nextPatterns);
27522
27919
  writeFileSync2(path, content, "utf8");
27523
27920
  }
@@ -27678,7 +28075,7 @@ var parsePatchBody3 = makeBodyValidator(PATCH_BODY_SCHEMA2, {
27678
28075
  });
27679
28076
 
27680
28077
  // server/routes/project-preferences.ts
27681
- import { statSync as statSync9 } from "fs";
28078
+ import { statSync as statSync10 } from "fs";
27682
28079
  import { HTTPException as HTTPException13 } from "hono/http-exception";
27683
28080
  function registerProjectPreferencesRoute(app, deps) {
27684
28081
  app.get("/api/project-preferences", (c) => {
@@ -27854,7 +28251,7 @@ function formatPathDetail(path, cwd) {
27854
28251
  function isExistingDirectory(entry, cwd) {
27855
28252
  const abs = resolveScanPath(entry, cwd);
27856
28253
  try {
27857
- return statSync9(abs).isDirectory();
28254
+ return statSync10(abs).isDirectory();
27858
28255
  } catch {
27859
28256
  return false;
27860
28257
  }
@@ -27896,7 +28293,7 @@ var parsePatchBody4 = makeBodyValidator(PATCH_BODY_SCHEMA3, {
27896
28293
  });
27897
28294
 
27898
28295
  // server/routes/active-provider.ts
27899
- import { existsSync as existsSync28 } from "fs";
28296
+ import { existsSync as existsSync29 } from "fs";
27900
28297
  import { HTTPException as HTTPException14 } from "hono/http-exception";
27901
28298
  function registerActiveProviderRoute(app, deps) {
27902
28299
  app.get("/api/active-provider", async (c) => {
@@ -27944,7 +28341,7 @@ function applyLensSwitch(deps, newValue) {
27944
28341
  });
27945
28342
  }
27946
28343
  const dbPath = resolveDbPath({ db: void 0, cwd });
27947
- if (!existsSync28(dbPath)) return { dropped: null };
28344
+ if (!existsSync29(dbPath)) return { dropped: null };
27948
28345
  const dropResult = dropScanZone(dbPath);
27949
28346
  return {
27950
28347
  dropped: {
@@ -27974,7 +28371,7 @@ var parsePatchBody5 = makeBodyValidator(PATCH_BODY_SCHEMA4, {
27974
28371
 
27975
28372
  // server/routes/actions.ts
27976
28373
  import { HTTPException as HTTPException16 } from "hono/http-exception";
27977
- import { resolve as resolve39 } from "path";
28374
+ import { resolve as resolve40 } from "path";
27978
28375
 
27979
28376
  // server/routes/node-loader.ts
27980
28377
  import { HTTPException as HTTPException15 } from "hono/http-exception";
@@ -28034,7 +28431,7 @@ function registerActionsRoutes(app, deps) {
28034
28431
  let absPath;
28035
28432
  try {
28036
28433
  assertContained(deps.runtimeContext.cwd, node.path);
28037
- absPath = resolve39(deps.runtimeContext.cwd, node.path);
28434
+ absPath = resolve40(deps.runtimeContext.cwd, node.path);
28038
28435
  } catch (err) {
28039
28436
  throw new HTTPException16(400, { message: formatErrorMessage(err) });
28040
28437
  }
@@ -28142,14 +28539,14 @@ async function withScanMutex(fn) {
28142
28539
  if (inFlight !== null) {
28143
28540
  throw new ScanBusyError();
28144
28541
  }
28145
- let resolve43;
28542
+ let resolve44;
28146
28543
  inFlight = new Promise((r) => {
28147
- resolve43 = r;
28544
+ resolve44 = r;
28148
28545
  });
28149
28546
  try {
28150
28547
  return await fn();
28151
28548
  } finally {
28152
- resolve43();
28549
+ resolve44();
28153
28550
  inFlight = null;
28154
28551
  }
28155
28552
  }
@@ -28665,7 +29062,7 @@ function registerUpdateStatusRoute(app, deps) {
28665
29062
  }
28666
29063
 
28667
29064
  // server/static.ts
28668
- import { existsSync as existsSync29 } from "fs";
29065
+ import { existsSync as existsSync30 } from "fs";
28669
29066
  import { readFile as readFile6 } from "fs/promises";
28670
29067
  import { extname, join as join19 } from "path";
28671
29068
  import { serveStatic } from "@hono/node-server/serve-static";
@@ -28720,7 +29117,7 @@ function createSpaFallback(opts) {
28720
29117
  if (c.req.method !== "GET" && c.req.method !== "HEAD") return c.notFound();
28721
29118
  if (opts.uiDist === null) return htmlResponse(c, placeholder);
28722
29119
  const indexPath = join19(opts.uiDist, INDEX_HTML);
28723
- if (!existsSync29(indexPath)) return htmlResponse(c, placeholder);
29120
+ if (!existsSync30(indexPath)) return htmlResponse(c, placeholder);
28724
29121
  return fileResponse(c, indexPath);
28725
29122
  };
28726
29123
  }
@@ -29446,9 +29843,9 @@ function validateNoUi(noUi, uiDist) {
29446
29843
  }
29447
29844
 
29448
29845
  // server/paths.ts
29449
- import { existsSync as existsSync30, statSync as statSync10 } from "fs";
29450
- import { dirname as dirname19, isAbsolute as isAbsolute14, join as join20, resolve as resolve40 } from "path";
29451
- import { fileURLToPath as fileURLToPath6 } from "url";
29846
+ import { existsSync as existsSync31, statSync as statSync11 } from "fs";
29847
+ import { dirname as dirname20, isAbsolute as isAbsolute15, join as join20, resolve as resolve41 } from "path";
29848
+ import { fileURLToPath as fileURLToPath7 } from "url";
29452
29849
  var DEFAULT_UI_REL = join20("ui", "dist", "ui", "browser");
29453
29850
  var PACKAGE_UI_REL = "ui";
29454
29851
  var INDEX_HTML2 = "index.html";
@@ -29458,13 +29855,13 @@ function resolveDefaultUiDist(ctx) {
29458
29855
  return walkUpForUi(ctx.cwd);
29459
29856
  }
29460
29857
  function resolveExplicitUiDist(ctx, raw) {
29461
- return isAbsolute14(raw) ? raw : resolve40(ctx.cwd, raw);
29858
+ return isAbsolute15(raw) ? raw : resolve41(ctx.cwd, raw);
29462
29859
  }
29463
29860
  function isUiBundleDir(path) {
29464
- if (!existsSync30(path)) return false;
29861
+ if (!existsSync31(path)) return false;
29465
29862
  try {
29466
- if (!statSync10(path).isDirectory()) return false;
29467
- return existsSync30(join20(path, INDEX_HTML2));
29863
+ if (!statSync11(path).isDirectory()) return false;
29864
+ return existsSync31(join20(path, INDEX_HTML2));
29468
29865
  } catch {
29469
29866
  return false;
29470
29867
  }
@@ -29472,7 +29869,7 @@ function isUiBundleDir(path) {
29472
29869
  function resolvePackageBundledUi() {
29473
29870
  let here;
29474
29871
  try {
29475
- here = dirname19(fileURLToPath6(import.meta.url));
29872
+ here = dirname20(fileURLToPath7(import.meta.url));
29476
29873
  } catch {
29477
29874
  return null;
29478
29875
  }
@@ -29485,18 +29882,18 @@ function resolvePackageBundledUiFrom(here) {
29485
29882
  if (isUiBundleDir(candidate)) return candidate;
29486
29883
  const distHere = join20(current, "dist", PACKAGE_UI_REL);
29487
29884
  if (isUiBundleDir(distHere)) return distHere;
29488
- const parent = dirname19(current);
29885
+ const parent = dirname20(current);
29489
29886
  if (parent === current) return null;
29490
29887
  current = parent;
29491
29888
  }
29492
29889
  return null;
29493
29890
  }
29494
29891
  function walkUpForUi(startDir) {
29495
- let current = resolve40(startDir);
29892
+ let current = resolve41(startDir);
29496
29893
  for (let i = 0; i < 64; i++) {
29497
29894
  const candidate = join20(current, DEFAULT_UI_REL);
29498
29895
  if (isUiBundleDir(candidate)) return candidate;
29499
- const parent = dirname19(current);
29896
+ const parent = dirname20(current);
29500
29897
  if (parent === current) return null;
29501
29898
  current = parent;
29502
29899
  }
@@ -29792,169 +30189,10 @@ var SERVE_TEXTS = {
29792
30189
  driftDeclined: "{{glyph}} sm serve: cache rebuild declined; the {{dbVersion}} cache cannot be reused on {{currentVersion}} ({{reason}}).\n {{hint}}\n"
29793
30190
  };
29794
30191
 
29795
- // cli/util/serve-banner.ts
29796
- import { relative as relative8, isAbsolute as isAbsolute15 } from "path";
29797
- var ESC2 = {
29798
- reset: "\x1B[0m",
29799
- bold: "\x1B[1m",
29800
- dim: "\x1B[2m",
29801
- underline: "\x1B[4m",
29802
- /** 256-color violet (xterm 141). */
29803
- violet: "\x1B[38;5;141m",
29804
- /** 256-color green (xterm 42). */
29805
- green: "\x1B[38;5;42m",
29806
- /** 256-color yellow (xterm 214), matches `cli/util/ansi.ts:yellow`. */
29807
- yellow: "\x1B[38;5;214m"
29808
- };
29809
- var LOGO_LINES = [
29810
- " ____ _ _ _ _ __ __ ",
29811
- " / ___|| | _(_) | | | \\/ | __ _ _ __ ",
29812
- " \\___ \\| |/ / | | | | |\\/| |/ _` | '_ \\ ",
29813
- " ___) | <| | | | | | | | (_| | |_) |",
29814
- " |____/|_|\\_\\_|_|_| |_| |_|\\__,_| .__/ ",
29815
- " |_| "
29816
- ];
29817
- var LOGO_WIDTH = 40;
29818
- function renderBanner(input) {
29819
- const url = `http://${input.host}:${input.port}`;
29820
- const dbDisplay = formatDbPath(input.dbPath, input.cwd);
29821
- const browserLine = input.openBrowser ? "Opening browser\u2026 Press Ctrl+C to stop." : `Visit ${url}/ in your browser. Press Ctrl+C to stop.`;
29822
- if (!input.isTTY) {
29823
- return renderFlat({
29824
- host: input.host,
29825
- port: input.port,
29826
- dbPath: input.dbPath,
29827
- openBrowser: input.openBrowser,
29828
- dev: input.dev === true
29829
- });
29830
- }
29831
- return renderFiglet({
29832
- version: input.version,
29833
- url,
29834
- dbDisplay,
29835
- pathDisplay: formatCwdPath(input.cwd),
29836
- browserLine,
29837
- colorEnabled: input.colorEnabled,
29838
- referencePaths: input.referencePaths ?? [],
29839
- dev: input.dev === true
29840
- });
29841
- }
29842
- function resolveColorEnabled(opts) {
29843
- if (opts.noColorFlag) return false;
29844
- const noColor = opts.env["NO_COLOR"];
29845
- if (noColor !== void 0 && noColor !== "") return false;
29846
- const forceColor = opts.env["FORCE_COLOR"];
29847
- if (forceColor !== void 0 && forceColor !== "") return true;
29848
- return opts.isTTY;
29849
- }
29850
- function renderFlat(input) {
29851
- const safeHost = sanitizeForTerminal(input.host);
29852
- const safeDb = sanitizeForTerminal(input.dbPath);
29853
- const url = `http://${safeHost}:${input.port}`;
29854
- const devSuffix = input.dev ? " [dev]" : "";
29855
- const linesOut = [];
29856
- linesOut.push(`sm serve${devSuffix}: listening on ${url} (db=${safeDb})`);
29857
- if (input.openBrowser) {
29858
- linesOut.push(`sm serve: opening ${url}/ in your browser. Press Ctrl+C to stop.`);
29859
- } else {
29860
- linesOut.push(`sm serve: visit ${url}/ in your browser. Press Ctrl+C to stop.`);
29861
- }
29862
- return linesOut.join("\n") + "\n";
29863
- }
29864
- function renderLogoBlock(input) {
29865
- const { dimOpen, dimClose, violetOpen, violetClose } = resolveAnsi(input.colorEnabled);
29866
- const logoLines = LOGO_LINES.map((line) => `${violetOpen}${line}${violetClose}`);
29867
- const versionText = `v${input.version}`;
29868
- const versionPad = Math.max(0, LOGO_WIDTH - versionText.length);
29869
- const versionLine = `${" ".repeat(versionPad)}${dimOpen}${versionText}${dimClose}`;
29870
- return `${logoLines.join("\n")}
29871
-
29872
- ${versionLine}
29873
-
29874
- `;
29875
- }
29876
- function renderFiglet(input) {
29877
- const {
29878
- dimOpen,
29879
- dimClose,
29880
- greenUnderline,
29881
- greenUnderlineClose,
29882
- violetOpen,
29883
- violetClose,
29884
- yellowOpen,
29885
- yellowClose
29886
- } = resolveAnsi(input.colorEnabled);
29887
- const logoLines = LOGO_LINES.map((line) => `${violetOpen}${line}${violetClose}`);
29888
- const versionText = `v${input.version}`;
29889
- const devText = "[dev]";
29890
- const versionWidth = input.dev ? devText.length : versionText.length;
29891
- const versionPad = Math.max(0, LOGO_WIDTH - versionWidth);
29892
- const versionLine = input.dev ? `${" ".repeat(versionPad)}${yellowOpen}${devText}${yellowClose}` : `${" ".repeat(versionPad)}${dimOpen}${versionText}${dimClose}`;
29893
- const lines = [];
29894
- lines.push(...logoLines);
29895
- lines.push("");
29896
- lines.push(versionLine);
29897
- lines.push("");
29898
- lines.push(` ${dimOpen}Server${dimClose} ${greenUnderline}${input.url}${greenUnderlineClose}`);
29899
- lines.push(` ${dimOpen}Path${dimClose} ${input.pathDisplay}`);
29900
- lines.push(` ${dimOpen}DB${dimClose} ${input.dbDisplay}`);
29901
- lines.push(...renderListRows("Refs", input.referencePaths, dimOpen, dimClose));
29902
- lines.push("");
29903
- lines.push(` ${dimOpen}${input.browserLine}${dimClose}`);
29904
- lines.push("");
29905
- return lines.join("\n") + "\n";
29906
- }
29907
- function renderListRows(label, values, dimOpen, dimClose) {
29908
- if (values.length === 0) return [];
29909
- const out = [];
29910
- const labelPad = " ".repeat(Math.max(1, 9 - label.length));
29911
- const continuationPad = " ".repeat(11);
29912
- out.push(` ${dimOpen}${label}${dimClose}${labelPad}${sanitizeForTerminal(values[0])}`);
29913
- for (let i = 1; i < values.length; i += 1) {
29914
- out.push(`${continuationPad}${sanitizeForTerminal(values[i])}`);
29915
- }
29916
- return out;
29917
- }
29918
- var EMPTY_ANSI = {
29919
- dimOpen: "",
29920
- dimClose: "",
29921
- greenUnderline: "",
29922
- greenUnderlineClose: "",
29923
- violetOpen: "",
29924
- violetClose: "",
29925
- yellowOpen: "",
29926
- yellowClose: ""
29927
- };
29928
- var ENABLED_ANSI = {
29929
- dimOpen: ESC2.dim,
29930
- dimClose: ESC2.reset,
29931
- greenUnderline: `${ESC2.green}${ESC2.underline}`,
29932
- greenUnderlineClose: ESC2.reset,
29933
- violetOpen: ESC2.violet,
29934
- violetClose: ESC2.reset,
29935
- yellowOpen: ESC2.yellow,
29936
- yellowClose: ESC2.reset
29937
- };
29938
- function resolveAnsi(colorEnabled) {
29939
- return colorEnabled ? ENABLED_ANSI : EMPTY_ANSI;
29940
- }
29941
- function formatDbPath(dbPath, cwd) {
29942
- const safe = sanitizeForTerminal(dbPath);
29943
- if (!isAbsolute15(safe)) return safe;
29944
- const rel = relative8(cwd, safe);
29945
- if (rel === "" || rel.startsWith("..") || isAbsolute15(rel)) {
29946
- return safe;
29947
- }
29948
- return rel;
29949
- }
29950
- function formatCwdPath(cwd) {
29951
- return sanitizeForTerminal(cwd);
29952
- }
29953
-
29954
30192
  // cli/commands/serve.ts
29955
30193
  var ServeCommand = class extends SmCommand {
29956
30194
  static paths = [["serve"]];
29957
- static usage = Command34.Usage({
30195
+ static usage = Command35.Usage({
29958
30196
  category: "Setup",
29959
30197
  description: "Start the Hono BFF (single-port: REST + WebSocket + SPA bundle).",
29960
30198
  details: `
@@ -29978,18 +30216,18 @@ var ServeCommand = class extends SmCommand {
29978
30216
  ["Point at a pre-built UI bundle", "$0 serve --ui-dist ./ui/dist/browser"]
29979
30217
  ]
29980
30218
  });
29981
- port = Option32.String("--port", {
30219
+ port = Option33.String("--port", {
29982
30220
  required: false,
29983
30221
  description: "Listening port (default 4242). 0 = OS-assigned."
29984
30222
  });
29985
- host = Option32.String("--host", {
30223
+ host = Option33.String("--host", {
29986
30224
  required: false,
29987
30225
  description: "Listening host (default 127.0.0.1). Loopback-only enforced when --dev-cors is set."
29988
30226
  });
29989
- noBuiltIns = Option32.Boolean("--no-built-ins", false, {
30227
+ noBuiltIns = Option33.Boolean("--no-built-ins", false, {
29990
30228
  description: "Skip built-in plugin registration (parity with sm scan --no-built-ins)."
29991
30229
  });
29992
- noPlugins = Option32.Boolean("--no-plugins", false, {
30230
+ noPlugins = Option33.Boolean("--no-plugins", false, {
29993
30231
  description: "Skip drop-in plugin discovery."
29994
30232
  });
29995
30233
  // `Option.Boolean('--open', true)`, Clipanion's parser auto-derives
@@ -29999,35 +30237,35 @@ var ServeCommand = class extends SmCommand {
29999
30237
  // two registrations for the same flag and rejects the invocation
30000
30238
  // with "Ambiguous Syntax Error". Same convention shipped by every
30001
30239
  // other `--no-...` flag in the CLI tree.
30002
- open = Option32.Boolean("--open", true, {
30240
+ open = Option33.Boolean("--open", true, {
30003
30241
  description: "Auto-open the SPA in the user's default browser after listen. --no-open opts out."
30004
30242
  });
30005
- devCors = Option32.Boolean("--dev-cors", false, {
30243
+ devCors = Option33.Boolean("--dev-cors", false, {
30006
30244
  description: "Enable permissive CORS for the Angular dev-server proxy workflow."
30007
30245
  });
30008
30246
  // `--ui-dist` is intentionally undocumented in the Usage block above
30009
30247
  // (the demo build pipeline + tests rely on it; everyday users never
30010
30248
  // need it). Clipanion still exposes it on the parser; the Usage
30011
30249
  // omission is the "hidden" contract per the 14.1 brief.
30012
- uiDist = Option32.String("--ui-dist", { required: false, hidden: true });
30013
- noUi = Option32.Boolean("--no-ui", false, {
30250
+ uiDist = Option33.String("--ui-dist", { required: false, hidden: true });
30251
+ noUi = Option33.Boolean("--no-ui", false, {
30014
30252
  description: "Don't serve the Angular UI bundle. Use this when running the BFF alongside `ui:dev` (Angular dev server with HMR). The root `/` then renders an inline placeholder pointing the user at the dev server."
30015
30253
  });
30016
- noWatcher = Option32.Boolean("--no-watcher", false, {
30254
+ noWatcher = Option33.Boolean("--no-watcher", false, {
30017
30255
  description: "Disable the chokidar-fed scan-and-broadcast loop. Use only for CI / read-only deployments."
30018
30256
  });
30019
- yes = Option32.Boolean("--yes", false, {
30257
+ yes = Option33.Boolean("--yes", false, {
30020
30258
  description: "Skip the interactive prompt and rebuild the local cache when the on-disk DB has drifted (version skew or an inline schema change). Non-TTY invocations rebuild without asking regardless of this flag."
30021
30259
  });
30022
30260
  // `--watcher-debounce-ms` is undocumented sugar for advanced users
30023
30261
  // who want to tighten / relax the watcher's batching window without
30024
30262
  // editing settings.json. Hidden flag, the Usage block omits it.
30025
- watcherDebounceMs = Option32.String("--watcher-debounce-ms", { required: false, hidden: true });
30026
- maxScan = Option32.String("--max-scan", {
30263
+ watcherDebounceMs = Option33.String("--watcher-debounce-ms", { required: false, hidden: true });
30264
+ maxScan = Option33.String("--max-scan", {
30027
30265
  required: false,
30028
30266
  description: "Per-invocation override of scan.maxScan (default 50000), the WALK-INTAKE ceiling. The scan walks, parses, analyzes, and reference-validates the full corpus up to this number. Bidirectional: raises OR lowers the ceiling. Applies to every scan the server runs (initial watcher pass, debounced batches, POST /api/scan, GET /api/scan?fresh=1). Same flag is honoured on the bare `sm` invocation, which routes to `sm serve`."
30029
30267
  });
30030
- maxNodes = Option32.String("--max-nodes", {
30268
+ maxNodes = Option33.String("--max-nodes", {
30031
30269
  required: false,
30032
30270
  description: "Per-invocation override of scan.maxNodes (default 256), the MAP RENDER cap (pure metadata): it does NOT bound the scan, only how many nodes the graph view projects onto the canvas. Bidirectional: raises OR lowers the render cap. Same flag is honoured on the bare `sm` invocation, which routes to `sm serve`."
30033
30271
  });
@@ -30057,7 +30295,7 @@ var ServeCommand = class extends SmCommand {
30057
30295
  return ExitCode.Error;
30058
30296
  }
30059
30297
  const dbPath = resolveDbPath({ db: this.db, ...runtimeCtx });
30060
- if (this.db !== void 0 && !existsSync31(dbPath)) {
30298
+ if (this.db !== void 0 && !existsSync32(dbPath)) {
30061
30299
  this.printer.info(
30062
30300
  tx(SERVE_TEXTS.dbNotFound, {
30063
30301
  glyph: errGlyph,
@@ -30397,7 +30635,7 @@ function tryOpenBrowser(url, stderr, warnGlyph) {
30397
30635
  }
30398
30636
 
30399
30637
  // cli/commands/show.ts
30400
- import { Command as Command35, Option as Option33 } from "clipanion";
30638
+ import { Command as Command36, Option as Option34 } from "clipanion";
30401
30639
 
30402
30640
  // cli/i18n/show.texts.ts
30403
30641
  var SHOW_TEXTS = {
@@ -30448,7 +30686,7 @@ var SHOW_TEXTS = {
30448
30686
  // cli/commands/show.ts
30449
30687
  var ShowCommand = class extends SmCommand {
30450
30688
  static paths = [["show"]];
30451
- static usage = Command35.Usage({
30689
+ static usage = Command36.Usage({
30452
30690
  category: "Browse",
30453
30691
  description: "Node detail: weight, frontmatter, links, issues.",
30454
30692
  details: `
@@ -30464,7 +30702,7 @@ var ShowCommand = class extends SmCommand {
30464
30702
  ["Machine-readable detail", "$0 show .claude/agents/architect.md --json"]
30465
30703
  ]
30466
30704
  });
30467
- nodePath = Option33.String({ required: true });
30705
+ nodePath = Option34.String({ required: true });
30468
30706
  async run() {
30469
30707
  const dbPath = resolveDbPath({ db: this.db, ...defaultRuntimeContext() });
30470
30708
  const exit = requireDbOrExit(dbPath, this.context.stderr);
@@ -30705,8 +30943,8 @@ function rankConfidenceForGrouping(c) {
30705
30943
 
30706
30944
  // cli/commands/sidecar.ts
30707
30945
  import { unlink as unlink3 } from "fs/promises";
30708
- import { resolve as resolve41 } from "path";
30709
- import { Command as Command36, Option as Option34 } from "clipanion";
30946
+ import { resolve as resolve42 } from "path";
30947
+ import { Command as Command37, Option as Option35 } from "clipanion";
30710
30948
 
30711
30949
  // cli/i18n/sidecar.texts.ts
30712
30950
  var SIDECAR_TEXTS = {
@@ -30787,7 +31025,7 @@ async function runWithSidecarConsent(bag, ansi, dispatch) {
30787
31025
  }
30788
31026
  var SidecarRefreshCommand = class extends SmCommand {
30789
31027
  static paths = [["sidecar", "refresh"]];
30790
- static usage = Command36.Usage({
31028
+ static usage = Command37.Usage({
30791
31029
  category: "Actions",
30792
31030
  description: "Refresh a sidecar's `for.{bodyHash, frontmatterHash}` to match the live node. Does NOT bump the version.",
30793
31031
  details: `
@@ -30804,8 +31042,8 @@ var SidecarRefreshCommand = class extends SmCommand {
30804
31042
  ["Refresh a node's sidecar hashes", "$0 sidecar refresh .claude/agents/architect.md"]
30805
31043
  ]
30806
31044
  });
30807
- nodePath = Option34.String({ required: true });
30808
- yes = Option34.Boolean("--yes", false, {
31045
+ nodePath = Option35.String({ required: true });
31046
+ yes = Option35.Boolean("--yes", false, {
30809
31047
  description: "Confirm writing .sm sidecar files in this project (sets allowEditSmFiles=true on first run)."
30810
31048
  });
30811
31049
  async run() {
@@ -30863,7 +31101,7 @@ var SidecarRefreshCommand = class extends SmCommand {
30863
31101
  let absPath;
30864
31102
  try {
30865
31103
  assertContained(ctx.cwd, node.path);
30866
- absPath = resolve41(ctx.cwd, node.path);
31104
+ absPath = resolve42(ctx.cwd, node.path);
30867
31105
  } catch (err) {
30868
31106
  this.printer.error(
30869
31107
  tx(SIDECAR_TEXTS.refreshFailed, { glyph: errGlyph, message: formatErrorMessage(err) })
@@ -30927,7 +31165,7 @@ var SidecarRefreshCommand = class extends SmCommand {
30927
31165
  };
30928
31166
  var SidecarPruneCommand = class extends SmCommand {
30929
31167
  static paths = [["sidecar", "prune"]];
30930
- static usage = Command36.Usage({
31168
+ static usage = Command37.Usage({
30931
31169
  category: "Actions",
30932
31170
  description: "Delete orphan .sm files (sidecars whose accompanying .md no longer exists).",
30933
31171
  details: `
@@ -30949,8 +31187,8 @@ var SidecarPruneCommand = class extends SmCommand {
30949
31187
  ["Delete every orphan .sm file (non-interactive)", "$0 sidecar prune --yes"]
30950
31188
  ]
30951
31189
  });
30952
- dryRun = Option34.Boolean("-n,--dry-run", false);
30953
- yes = Option34.Boolean("--yes,--force", false, {
31190
+ dryRun = Option35.Boolean("-n,--dry-run", false);
31191
+ yes = Option35.Boolean("--yes,--force", false, {
30954
31192
  description: "Skip the interactive confirmation prompt. Required for non-interactive callers (CI, pre-commit hooks)."
30955
31193
  });
30956
31194
  // Complexity is from per-orphan handling, empty-set / dry-run /
@@ -31070,7 +31308,7 @@ var SidecarPruneCommand = class extends SmCommand {
31070
31308
  };
31071
31309
  var SidecarAnnotateCommand = class extends SmCommand {
31072
31310
  static paths = [["sidecar", "annotate"]];
31073
- static usage = Command36.Usage({
31311
+ static usage = Command37.Usage({
31074
31312
  category: "Actions",
31075
31313
  description: "Scaffold an empty `<basename>.sm` next to a node ready for editing.",
31076
31314
  details: `
@@ -31088,9 +31326,9 @@ var SidecarAnnotateCommand = class extends SmCommand {
31088
31326
  ["Overwrite an existing one", "$0 sidecar annotate .claude/agents/architect.md --force"]
31089
31327
  ]
31090
31328
  });
31091
- nodePath = Option34.String({ required: true });
31092
- force = Option34.Boolean("--force", false);
31093
- yes = Option34.Boolean("--yes", false, {
31329
+ nodePath = Option35.String({ required: true });
31330
+ force = Option35.Boolean("--force", false);
31331
+ yes = Option35.Boolean("--yes", false, {
31094
31332
  description: "Confirm writing .sm sidecar files in this project (sets allowEditSmFiles=true on first run)."
31095
31333
  });
31096
31334
  async run() {
@@ -31147,7 +31385,7 @@ var SidecarAnnotateCommand = class extends SmCommand {
31147
31385
  let absPath;
31148
31386
  try {
31149
31387
  assertContained(ctx.cwd, node.path);
31150
- absPath = resolve41(ctx.cwd, node.path);
31388
+ absPath = resolve42(ctx.cwd, node.path);
31151
31389
  } catch (err) {
31152
31390
  this.printer.error(
31153
31391
  tx(SIDECAR_TEXTS.annotateFailed, { glyph: errGlyph, message: formatErrorMessage(err) })
@@ -31229,7 +31467,7 @@ var SIDECAR_COMMANDS = [
31229
31467
  ];
31230
31468
 
31231
31469
  // cli/commands/stubs.ts
31232
- import { Command as Command37, Option as Option35 } from "clipanion";
31470
+ import { Command as Command38, Option as Option36 } from "clipanion";
31233
31471
 
31234
31472
  // cli/i18n/stubs.texts.ts
31235
31473
  var STUBS_TEXTS = {
@@ -31255,7 +31493,7 @@ var StubCommand = class extends SmCommand {
31255
31493
  };
31256
31494
  var DoctorCommand = class extends StubCommand {
31257
31495
  static paths = [["doctor"]];
31258
- static usage = Command37.Usage({
31496
+ static usage = Command38.Usage({
31259
31497
  category: "Setup",
31260
31498
  description: planned("Diagnostic report: DB integrity, pending migrations, orphan rows, plugin status, runner availability.")
31261
31499
  });
@@ -31263,18 +31501,18 @@ var DoctorCommand = class extends StubCommand {
31263
31501
  };
31264
31502
  var FindingsCommand = class extends StubCommand {
31265
31503
  static paths = [["findings"]];
31266
- static usage = Command37.Usage({
31504
+ static usage = Command38.Usage({
31267
31505
  category: "Browse",
31268
31506
  description: planned("Probabilistic findings: injection, stale summaries, low confidence.")
31269
31507
  });
31270
- kind = Option35.String("--kind", { required: false });
31271
- since = Option35.String("--since", { required: false });
31272
- threshold = Option35.String("--threshold", { required: false });
31508
+ kind = Option36.String("--kind", { required: false });
31509
+ since = Option36.String("--since", { required: false });
31510
+ threshold = Option36.String("--threshold", { required: false });
31273
31511
  verbName = "findings";
31274
31512
  };
31275
31513
  var ActionsListCommand = class extends StubCommand {
31276
31514
  static paths = [["actions", "list"]];
31277
- static usage = Command37.Usage({
31515
+ static usage = Command38.Usage({
31278
31516
  category: "Jobs",
31279
31517
  description: planned("Registered action types (manifest view).")
31280
31518
  });
@@ -31282,103 +31520,103 @@ var ActionsListCommand = class extends StubCommand {
31282
31520
  };
31283
31521
  var ActionsShowCommand = class extends StubCommand {
31284
31522
  static paths = [["actions", "show"]];
31285
- static usage = Command37.Usage({
31523
+ static usage = Command38.Usage({
31286
31524
  category: "Jobs",
31287
31525
  description: planned("Full action manifest, including preconditions and expected duration.")
31288
31526
  });
31289
- id = Option35.String({ required: true });
31527
+ id = Option36.String({ required: true });
31290
31528
  verbName = "actions show";
31291
31529
  };
31292
31530
  var JobSubmitCommand = class extends StubCommand {
31293
31531
  static paths = [["job", "submit"]];
31294
- static usage = Command37.Usage({
31532
+ static usage = Command38.Usage({
31295
31533
  category: "Jobs",
31296
31534
  description: planned("Enqueue a single job or fan out to every matching node (--all).")
31297
31535
  });
31298
- action = Option35.String({ required: true });
31299
- node = Option35.String("-n", { required: false });
31300
- all = Option35.Boolean("--all", false);
31536
+ action = Option36.String({ required: true });
31537
+ node = Option36.String("-n", { required: false });
31538
+ all = Option36.Boolean("--all", false);
31301
31539
  // CLI flag stays `--run`; field name is `runFlag` per the
31302
31540
  // shadow-avoidance convention documented on `SmCommand`.
31303
- runFlag = Option35.Boolean("--run", false);
31304
- force = Option35.Boolean("--force", false);
31305
- ttl = Option35.String("--ttl", { required: false });
31306
- priority = Option35.String("--priority", { required: false });
31541
+ runFlag = Option36.Boolean("--run", false);
31542
+ force = Option36.Boolean("--force", false);
31543
+ ttl = Option36.String("--ttl", { required: false });
31544
+ priority = Option36.String("--priority", { required: false });
31307
31545
  verbName = "job submit";
31308
31546
  };
31309
31547
  var JobListCommand = class extends StubCommand {
31310
31548
  static paths = [["job", "list"]];
31311
- static usage = Command37.Usage({ category: "Jobs", description: planned("List jobs.") });
31312
- status = Option35.String("--status", { required: false });
31313
- action = Option35.String("--action", { required: false });
31314
- node = Option35.String("--node", { required: false });
31549
+ static usage = Command38.Usage({ category: "Jobs", description: planned("List jobs.") });
31550
+ status = Option36.String("--status", { required: false });
31551
+ action = Option36.String("--action", { required: false });
31552
+ node = Option36.String("--node", { required: false });
31315
31553
  verbName = "job list";
31316
31554
  };
31317
31555
  var JobShowCommand = class extends StubCommand {
31318
31556
  static paths = [["job", "show"]];
31319
- static usage = Command37.Usage({ category: "Jobs", description: planned("Job detail: state, claim time, TTL, runner, content hash.") });
31320
- id = Option35.String({ required: true });
31557
+ static usage = Command38.Usage({ category: "Jobs", description: planned("Job detail: state, claim time, TTL, runner, content hash.") });
31558
+ id = Option36.String({ required: true });
31321
31559
  verbName = "job show";
31322
31560
  };
31323
31561
  var JobPreviewCommand = class extends StubCommand {
31324
31562
  static paths = [["job", "preview"]];
31325
- static usage = Command37.Usage({ category: "Jobs", description: planned("Render the job MD file without executing.") });
31326
- id = Option35.String({ required: true });
31563
+ static usage = Command38.Usage({ category: "Jobs", description: planned("Render the job MD file without executing.") });
31564
+ id = Option36.String({ required: true });
31327
31565
  verbName = "job preview";
31328
31566
  };
31329
31567
  var JobClaimCommand = class extends StubCommand {
31330
31568
  static paths = [["job", "claim"]];
31331
- static usage = Command37.Usage({
31569
+ static usage = Command38.Usage({
31332
31570
  category: "Jobs",
31333
31571
  description: planned("Atomic primitive: return next queued job id, mark it running.")
31334
31572
  });
31335
- filter = Option35.String("--filter", { required: false });
31573
+ filter = Option36.String("--filter", { required: false });
31336
31574
  verbName = "job claim";
31337
31575
  };
31338
31576
  var JobRunCommand = class extends StubCommand {
31339
31577
  static paths = [["job", "run"]];
31340
- static usage = Command37.Usage({
31578
+ static usage = Command38.Usage({
31341
31579
  category: "Jobs",
31342
31580
  description: planned("Full CLI-runner loop: claim + spawn + record.")
31343
31581
  });
31344
- all = Option35.Boolean("--all", false);
31345
- max = Option35.String("--max", { required: false });
31582
+ all = Option36.Boolean("--all", false);
31583
+ max = Option36.String("--max", { required: false });
31346
31584
  verbName = "job run";
31347
31585
  };
31348
31586
  var JobStatusCommand = class extends StubCommand {
31349
31587
  static paths = [["job", "status"]];
31350
- static usage = Command37.Usage({
31588
+ static usage = Command38.Usage({
31351
31589
  category: "Jobs",
31352
31590
  description: planned("Counts (per status) or single-job status.")
31353
31591
  });
31354
- id = Option35.String({ required: false });
31592
+ id = Option36.String({ required: false });
31355
31593
  verbName = "job status";
31356
31594
  };
31357
31595
  var JobCancelCommand = class extends StubCommand {
31358
31596
  static paths = [["job", "cancel"]];
31359
- static usage = Command37.Usage({
31597
+ static usage = Command38.Usage({
31360
31598
  category: "Jobs",
31361
31599
  description: planned("Force a running job to failed with reason user-cancelled.")
31362
31600
  });
31363
- id = Option35.String({ required: false });
31364
- all = Option35.Boolean("--all", false);
31601
+ id = Option36.String({ required: false });
31602
+ all = Option36.Boolean("--all", false);
31365
31603
  verbName = "job cancel";
31366
31604
  };
31367
31605
  var RecordCommand = class extends StubCommand {
31368
31606
  static paths = [["record"]];
31369
- static usage = Command37.Usage({
31607
+ static usage = Command38.Usage({
31370
31608
  category: "Jobs",
31371
31609
  description: planned("Close a running job with success or failure. Nonce is the sole credential.")
31372
31610
  });
31373
- id = Option35.String("--id", { required: true });
31374
- nonce = Option35.String("--nonce", { required: true });
31375
- status = Option35.String("--status", { required: true });
31376
- report = Option35.String("--report", { required: false });
31377
- tokensIn = Option35.String("--tokens-in", { required: false });
31378
- tokensOut = Option35.String("--tokens-out", { required: false });
31379
- durationMs = Option35.String("--duration-ms", { required: false });
31380
- model = Option35.String("--model", { required: false });
31381
- error = Option35.String("--error", { required: false });
31611
+ id = Option36.String("--id", { required: true });
31612
+ nonce = Option36.String("--nonce", { required: true });
31613
+ status = Option36.String("--status", { required: true });
31614
+ report = Option36.String("--report", { required: false });
31615
+ tokensIn = Option36.String("--tokens-in", { required: false });
31616
+ tokensOut = Option36.String("--tokens-out", { required: false });
31617
+ durationMs = Option36.String("--duration-ms", { required: false });
31618
+ model = Option36.String("--model", { required: false });
31619
+ error = Option36.String("--error", { required: false });
31382
31620
  verbName = "record";
31383
31621
  };
31384
31622
  var STUB_COMMANDS = [
@@ -31398,11 +31636,11 @@ var STUB_COMMANDS = [
31398
31636
  ];
31399
31637
 
31400
31638
  // cli/commands/tutorial.ts
31401
- import { cpSync as cpSync2, existsSync as existsSync32, mkdirSync as mkdirSync6, readdirSync as readdirSync10, rmSync as rmSync2, statSync as statSync11 } from "fs";
31402
- import { dirname as dirname20, join as join21, resolve as resolve42 } from "path";
31403
- import { createInterface as createInterface5 } from "readline";
31404
- import { fileURLToPath as fileURLToPath7 } from "url";
31405
- import { Command as Command38, Option as Option36 } from "clipanion";
31639
+ import { cpSync as cpSync3, existsSync as existsSync33, mkdirSync as mkdirSync6, rmSync as rmSync2, statSync as statSync12 } from "fs";
31640
+ import { dirname as dirname21, join as join21, resolve as resolve43 } from "path";
31641
+ import { createInterface as createInterface6 } from "readline";
31642
+ import { fileURLToPath as fileURLToPath8 } from "url";
31643
+ import { Command as Command39, Option as Option37 } from "clipanion";
31406
31644
 
31407
31645
  // cli/i18n/tutorial.texts.ts
31408
31646
  var TUTORIAL_TEXTS = {
@@ -31464,7 +31702,7 @@ var TRIGGER_EN = "run the tutorial";
31464
31702
  var TRIGGER_ES = "ejecuta el tutorial";
31465
31703
  var TutorialCommand = class extends SmCommand {
31466
31704
  static paths = [["tutorial"]];
31467
- static usage = Command38.Usage({
31705
+ static usage = Command39.Usage({
31468
31706
  category: "Setup",
31469
31707
  description: "Materialize an interactive tester tutorial as a Claude Code skill folder under `<cwd>/.claude/skills/`.",
31470
31708
  details: `
@@ -31493,18 +31731,18 @@ var TutorialCommand = class extends SmCommand {
31493
31731
  // more. Accept one so a stale `sm tutorial master` lands on a friendly
31494
31732
  // usage error (guarded in `run()`) instead of clipanion's generic
31495
31733
  // "extraneous argument" message.
31496
- legacyPositional = Option36.String({ required: false });
31734
+ legacyPositional = Option37.String({ required: false });
31497
31735
  // Named `forProvider`, NOT `for` (reserved word). The CLI surface stays
31498
31736
  // `--for`; selects the destination Provider whose `scaffold.skillDir`
31499
31737
  // the skill is materialised under, skipping the interactive prompt.
31500
- forProvider = Option36.String("--for", {
31738
+ forProvider = Option37.String("--for", {
31501
31739
  required: false,
31502
31740
  description: "Destination provider id (e.g. claude). Skips the prompt."
31503
31741
  });
31504
- force = Option36.Boolean("--force", false, {
31742
+ force = Option37.Boolean("--force", false, {
31505
31743
  description: "Overwrite an existing target directory without prompting."
31506
31744
  });
31507
- experimental = Option36.Boolean("--experimental", false, {
31745
+ experimental = Option37.Boolean("--experimental", false, {
31508
31746
  description: "Offer experimental providers (e.g. agent-skills) as destinations. They ship disabled; enable the chosen one with `sm plugins enable <id>`."
31509
31747
  });
31510
31748
  async run() {
@@ -31556,8 +31794,8 @@ var TutorialCommand = class extends SmCommand {
31556
31794
  }
31557
31795
  try {
31558
31796
  rmSync2(targetDir, { recursive: true, force: true });
31559
- mkdirSync6(dirname20(targetDir), { recursive: true });
31560
- cpSync2(sourceDir, targetDir, { recursive: true });
31797
+ mkdirSync6(dirname21(targetDir), { recursive: true });
31798
+ cpSync3(sourceDir, targetDir, { recursive: true });
31561
31799
  } catch (err) {
31562
31800
  this.printer.error(
31563
31801
  tx(TUTORIAL_TEXTS.writeFailed, {
@@ -31694,7 +31932,7 @@ function classifyAnswer(trimmed, targets, def) {
31694
31932
  async function promptForTarget(targets, def, stdin, stderr, glyph) {
31695
31933
  stderr.write(renderTargetLines(targets, def, glyph) + "\n");
31696
31934
  const defIndex = targets.findIndex((t) => t.id === def.id);
31697
- const rl = createInterface5({ input: stdin, output: stderr });
31935
+ const rl = createInterface6({ input: stdin, output: stderr });
31698
31936
  try {
31699
31937
  for (let attempt = 0; attempt < 5; attempt += 1) {
31700
31938
  const answer = await new Promise(
@@ -31708,35 +31946,21 @@ async function promptForTarget(targets, def, stdin, stderr, glyph) {
31708
31946
  rl.close();
31709
31947
  }
31710
31948
  }
31711
- function displayCwd(cwd) {
31712
- const segments = cwd.split("/").filter((s) => s.length > 0);
31713
- if (segments.length === 0) return "./";
31714
- return `./${segments[segments.length - 1]}/`;
31715
- }
31716
- function isDirEmpty(dir) {
31717
- return readdirSync10(dir).length === 0;
31718
- }
31719
- function listCwdEntries(dir) {
31720
- const entries = readdirSync10(dir).sort();
31721
- const shown = entries.slice(0, 5);
31722
- const more = entries.length > shown.length ? ", ..." : "";
31723
- return shown.join(", ") + more;
31724
- }
31725
- var cachedSourceDir;
31949
+ var cachedSourceDir2;
31726
31950
  function resolveSkillSourceDir() {
31727
- if (cachedSourceDir !== void 0) return cachedSourceDir;
31728
- const here = dirname20(fileURLToPath7(import.meta.url));
31951
+ if (cachedSourceDir2 !== void 0) return cachedSourceDir2;
31952
+ const here = dirname21(fileURLToPath8(import.meta.url));
31729
31953
  const candidates = [
31730
31954
  // dev: src/cli/commands/ → repo-root .claude/skills/sm-tutorial/
31731
- resolve42(here, "../../..", SKILL_SOURCE_DIR),
31955
+ resolve43(here, "../../..", SKILL_SOURCE_DIR),
31732
31956
  // bundled: dist/cli.js → dist/cli/tutorial/sm-tutorial (sibling)
31733
- resolve42(here, "cli/tutorial", SKILL_SLUG),
31957
+ resolve43(here, "cli/tutorial", SKILL_SLUG),
31734
31958
  // bundled fallback: any-depth → cli/tutorial/sm-tutorial
31735
- resolve42(here, "../cli/tutorial", SKILL_SLUG)
31959
+ resolve43(here, "../cli/tutorial", SKILL_SLUG)
31736
31960
  ];
31737
31961
  for (const candidate of candidates) {
31738
- if (existsSync32(candidate) && statSync11(candidate).isDirectory()) {
31739
- cachedSourceDir = candidate;
31962
+ if (existsSync33(candidate) && statSync12(candidate).isDirectory()) {
31963
+ cachedSourceDir2 = candidate;
31740
31964
  return candidate;
31741
31965
  }
31742
31966
  }
@@ -31746,7 +31970,7 @@ function resolveSkillSourceDir() {
31746
31970
  }
31747
31971
 
31748
31972
  // cli/commands/version.ts
31749
- import { Command as Command39 } from "clipanion";
31973
+ import { Command as Command40 } from "clipanion";
31750
31974
 
31751
31975
  // cli/i18n/version.texts.ts
31752
31976
  var VERSION_TEXTS = {
@@ -31761,7 +31985,7 @@ var VERSION_TEXTS = {
31761
31985
  // cli/commands/version.ts
31762
31986
  var VersionCommand = class extends SmCommand {
31763
31987
  static paths = [["version"]];
31764
- static usage = Command39.Usage({
31988
+ static usage = Command40.Usage({
31765
31989
  category: "Introspection",
31766
31990
  description: "Print the CLI / spec / runtime / db-schema version matrix."
31767
31991
  });
@@ -31836,6 +32060,7 @@ cli.register(RootHelpCommand);
31836
32060
  cli.register(HelpCommand);
31837
32061
  cli.register(InitCommand);
31838
32062
  cli.register(TutorialCommand);
32063
+ cli.register(ExampleCommand);
31839
32064
  cli.register(IntentionalFailCommand);
31840
32065
  cli.register(ScanCommand);
31841
32066
  cli.register(ScanCompareCommand);
@@ -31868,7 +32093,7 @@ var logLevel = resolveLogLevel({
31868
32093
  errStream: process.stderr
31869
32094
  });
31870
32095
  configureLogger(new Logger({ level: logLevel, stream: process.stderr }));
31871
- var bareArgs = resolveBareInvocation(args);
32096
+ var bareArgs = await resolveBareInvocation(args);
31872
32097
  var routedArgs = routeHelpArgs(bareArgs ?? args, cli);
31873
32098
  var telemetryVerb = routedArgs[0];
31874
32099
  await maybeRunFirstRunPrompt();
@@ -31926,23 +32151,41 @@ await lifecycleDispatcher.dispatch(
31926
32151
  await closeSentryCli();
31927
32152
  await flushUsageCli();
31928
32153
  process.exit(exitCode);
31929
- function resolveBareInvocation(rawArgs) {
31930
- if (rawArgs.length === 0) return resolveBareDefault();
32154
+ async function resolveBareInvocation(rawArgs) {
32155
+ if (rawArgs.length === 0) return resolveNoArgsBare();
31931
32156
  const first = rawArgs[0];
31932
32157
  const passthrough = /* @__PURE__ */ new Set(["--help", "-h", "--version", "-V", "-v"]);
31933
32158
  if (first !== void 0 && first.startsWith("-") && !passthrough.has(first)) {
31934
32159
  const isSingleDashLong = !first.startsWith("--") && first.length > 2;
31935
32160
  if (isSingleDashLong) return null;
31936
- if (existsSync33(defaultProjectDbPath(defaultRuntimeContext()))) {
32161
+ if (existsSync34(defaultProjectDbPath(defaultRuntimeContext()))) {
31937
32162
  return ["serve", ...rawArgs];
31938
32163
  }
31939
32164
  return resolveBareDefault();
31940
32165
  }
31941
32166
  return null;
31942
32167
  }
32168
+ async function resolveNoArgsBare() {
32169
+ const ctx = defaultRuntimeContext();
32170
+ const stdin = process.stdin;
32171
+ const result = await decideBareNoArgs(
32172
+ {
32173
+ hasDb: existsSync34(defaultProjectDbPath(ctx)),
32174
+ isTty: stdin.isTTY === true,
32175
+ isEmptyDir: isDirEmpty(ctx.cwd)
32176
+ },
32177
+ () => {
32178
+ const stderr = process.stderr;
32179
+ const ansi = ansiFor({ isTTY: stderr.isTTY === true, noColorFlag: false });
32180
+ return promptEmptyFolderChoice(stdin, stderr, ansi);
32181
+ }
32182
+ );
32183
+ if (result.kind === "route") return result.argv;
32184
+ return resolveBareDefault();
32185
+ }
31943
32186
  function resolveBareDefault() {
31944
32187
  const ctx = defaultRuntimeContext();
31945
- if (existsSync33(defaultProjectDbPath(ctx))) {
32188
+ if (existsSync34(defaultProjectDbPath(ctx))) {
31946
32189
  return ["serve"];
31947
32190
  }
31948
32191
  const stderr = process.stderr;
@@ -31951,10 +32194,12 @@ function resolveBareDefault() {
31951
32194
  tx(ENTRY_TEXTS.bareNoProject, {
31952
32195
  glyph: ansi.red("\u2715"),
31953
32196
  cwd: ctx.cwd,
31954
- hint: ansi.dim(ENTRY_TEXTS.bareNoProjectHint)
32197
+ hint: ansi.dim(
32198
+ isDirEmpty(ctx.cwd) ? ENTRY_TEXTS.bareEmptyHint : ENTRY_TEXTS.bareNoProjectHint
32199
+ )
31955
32200
  })
31956
32201
  );
31957
32202
  process.exit(ExitCode.Error);
31958
32203
  }
31959
32204
  //# sourceMappingURL=cli.js.map
31960
- //# debugId=09459eb1-3d6f-565d-9552-4819baae6f1a
32205
+ //# debugId=43bb2367-1e14-5df2-9221-a5e7a877a666