@skill-map/cli 0.13.0 → 0.14.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,5 +1,5 @@
1
1
  // cli/entry.ts
2
- import { existsSync as existsSync19 } from "fs";
2
+ import { existsSync as existsSync20 } from "fs";
3
3
  import { Builtins, Cli as Cli2 } from "clipanion";
4
4
 
5
5
  // kernel/adapters/silent-logger.ts
@@ -58,6 +58,7 @@ var ENTRY_TEXTS = {
58
58
  parseErrorIncompleteCommand: "incomplete command '{{name}}'",
59
59
  parseErrorSubcommandList: "Available subcommands: {{suggestions}}.",
60
60
  parseErrorVerbUsage: "{{verb}}: {{message}}",
61
+ parseErrorMissingPositional: "{{verb}}: missing required positional argument(s) {{positionals}}",
61
62
  parseErrorFlagSuggestion: "Did you mean '{{suggestion}}'?",
62
63
  parseErrorVerbSuggestion: "Did you mean {{suggestions}}?",
63
64
  parseErrorVerbHelpHint: "Run 'sm help {{verb}}' for usage.",
@@ -275,6 +276,46 @@ var ExitCode = {
275
276
  NotFound: 5
276
277
  };
277
278
 
279
+ // cli/util/edit-distance.ts
280
+ function editDistance(a, b, max) {
281
+ if (a === b) return 0;
282
+ if (Math.abs(a.length - b.length) > max) return max + 1;
283
+ const m = a.length;
284
+ const n = b.length;
285
+ if (m === 0) return n;
286
+ if (n === 0) return m;
287
+ let prev = Array.from({ length: n + 1 }, (_, i) => i);
288
+ let curr = new Array(n + 1);
289
+ for (let i = 1; i <= m; i++) {
290
+ const rowMin = fillEditRow({ a, b, i, prev, curr });
291
+ if (rowMin > max) return max + 1;
292
+ [prev, curr] = [curr, prev];
293
+ }
294
+ return prev[n];
295
+ }
296
+ function fillEditRow(args2) {
297
+ const { a, b, i, prev, curr } = args2;
298
+ curr[0] = i;
299
+ let rowMin = curr[0];
300
+ for (let j = 1; j < curr.length; j++) {
301
+ const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;
302
+ const value = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
303
+ curr[j] = value;
304
+ if (value < rowMin) rowMin = value;
305
+ }
306
+ return rowMin;
307
+ }
308
+ function closestMatches(target, candidates, options) {
309
+ const lcTarget = target.toLowerCase();
310
+ const ranked = [];
311
+ for (const c of candidates) {
312
+ const distance = editDistance(lcTarget, c.toLowerCase(), options.maxDistance);
313
+ if (distance <= options.maxDistance) ranked.push({ value: c, distance });
314
+ }
315
+ ranked.sort((a, b) => a.distance - b.distance || a.value.localeCompare(b.value));
316
+ return ranked.slice(0, options.topN).map((r) => r.value);
317
+ }
318
+
278
319
  // cli/util/parse-error.ts
279
320
  function isClipanionParseError(err) {
280
321
  if (!err || typeof err !== "object") return false;
@@ -297,10 +338,7 @@ function formatParseError(params) {
297
338
  return renderError(error.message.trim(), null);
298
339
  }
299
340
  const verbPrefix = matchedVerbPrefix(args2, verbPaths);
300
- if (verbPrefix) {
301
- const headline = tx(ENTRY_TEXTS.parseErrorVerbUsage, { verb: verbPrefix, message: error.message.trim() });
302
- return renderError(headline, tx(ENTRY_TEXTS.parseErrorVerbHelpHint, { verb: verbPrefix }));
303
- }
341
+ if (verbPrefix) return formatVerbScopedError(verbPrefix, error.message);
304
342
  const subcommands = subcommandsUnder(firstToken, verbPaths);
305
343
  if (subcommands.length > 0) {
306
344
  const headline = tx(ENTRY_TEXTS.parseErrorIncompleteCommand, { name: firstToken });
@@ -313,6 +351,22 @@ function formatParseError(params) {
313
351
  const suggestion = candidates.length > 0 ? tx(ENTRY_TEXTS.parseErrorVerbSuggestion, { suggestions: formatSuggestionList(candidates) }) : null;
314
352
  return renderError(tx(ENTRY_TEXTS.parseErrorUnknownCommand, { name: firstToken }), suggestion);
315
353
  }
354
+ function formatVerbScopedError(verbPrefix, errorMessage) {
355
+ const helpHint = tx(ENTRY_TEXTS.parseErrorVerbHelpHint, { verb: verbPrefix });
356
+ const missing = extractMissingPositionals(errorMessage);
357
+ if (missing !== null) {
358
+ const headline2 = tx(ENTRY_TEXTS.parseErrorMissingPositional, {
359
+ verb: verbPrefix,
360
+ positionals: missing
361
+ });
362
+ return renderError(headline2, helpHint);
363
+ }
364
+ const headline = tx(ENTRY_TEXTS.parseErrorVerbUsage, {
365
+ verb: verbPrefix,
366
+ message: firstLine(errorMessage)
367
+ });
368
+ return renderError(headline, helpHint);
369
+ }
316
370
  function subcommandsUnder(namespace, verbPaths) {
317
371
  const matches = verbPaths.filter((path) => path.length >= 2 && path[0] === namespace).map((path) => path.join(" ")).sort();
318
372
  return matches.slice(0, 3);
@@ -342,6 +396,16 @@ function extractOffendingFlag(message) {
342
396
  const match = /Unsupported option name \("([^"]+)"\)/.exec(message);
343
397
  return match ? match[1] : null;
344
398
  }
399
+ function extractMissingPositionals(message) {
400
+ if (!message.startsWith("Not enough positional arguments")) return null;
401
+ const hintLine = message.split("\n").map((l) => l.trim()).find((l) => l.startsWith("$"));
402
+ if (!hintLine) return "";
403
+ const names = [...hintLine.matchAll(/<([A-Za-z][A-Za-z0-9_-]*)>/g)].map((m) => `<${m[1]}>`);
404
+ return names.length > 0 ? names.join(" ") : "";
405
+ }
406
+ function firstLine(message) {
407
+ return message.split("\n")[0].trim();
408
+ }
345
409
  function suggestFlag(token) {
346
410
  if (!token.startsWith("-")) return null;
347
411
  if (token.startsWith("--")) return null;
@@ -349,34 +413,6 @@ function suggestFlag(token) {
349
413
  const longForm = "-" + token;
350
414
  return tx(ENTRY_TEXTS.parseErrorFlagSuggestion, { suggestion: longForm });
351
415
  }
352
- function editDistance(a, b, max) {
353
- if (a === b) return 0;
354
- if (Math.abs(a.length - b.length) > max) return max + 1;
355
- const m = a.length;
356
- const n = b.length;
357
- if (m === 0) return n;
358
- if (n === 0) return m;
359
- let prev = Array.from({ length: n + 1 }, (_, i) => i);
360
- let curr = new Array(n + 1);
361
- for (let i = 1; i <= m; i++) {
362
- const rowMin = fillEditRow({ a, b, i, prev, curr });
363
- if (rowMin > max) return max + 1;
364
- [prev, curr] = [curr, prev];
365
- }
366
- return prev[n];
367
- }
368
- function fillEditRow(args2) {
369
- const { a, b, i, prev, curr } = args2;
370
- curr[0] = i;
371
- let rowMin = curr[0];
372
- for (let j = 1; j < curr.length; j++) {
373
- const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;
374
- const value = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
375
- curr[j] = value;
376
- if (value < rowMin) rowMin = value;
377
- }
378
- return rowMin;
379
- }
380
416
  function closestVerbs(typed, verbPaths) {
381
417
  const target = typed.toLowerCase();
382
418
  const distanceCap = target.length <= 4 ? 2 : 3;
@@ -983,7 +1019,7 @@ function normalizeTrigger(source) {
983
1019
 
984
1020
  // built-in-plugins/extractors/slash/index.ts
985
1021
  var ID2 = "slash";
986
- var SLASH_RE = /(?:^|[^A-Za-z0-9_/])(\/[a-z0-9][a-z0-9_-]*(?::[a-z0-9][a-z0-9_-]*)?)/gi;
1022
+ var SLASH_RE = /(?<![A-Za-z0-9_/.:?#])(\/[a-z0-9][a-z0-9_-]*(?::[a-z0-9][a-z0-9_-]*)?)/gi;
987
1023
  var slashExtractor = {
988
1024
  id: ID2,
989
1025
  pluginId: "claude",
@@ -1126,6 +1162,77 @@ function lineFor(lineStarts, offset) {
1126
1162
  return lo + 1;
1127
1163
  }
1128
1164
 
1165
+ // built-in-plugins/extractors/markdown-link/index.ts
1166
+ import { posix as pathPosix } from "path";
1167
+ var ID5 = "markdown-link";
1168
+ var LINK_RE = /(?<!!)\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
1169
+ var URL_SCHEME_RE = /^[a-z][a-z0-9+.-]*:/i;
1170
+ var markdownLinkExtractor = {
1171
+ id: ID5,
1172
+ pluginId: "core",
1173
+ kind: "extractor",
1174
+ version: "1.0.0",
1175
+ description: "Detects [text](path) markdown links and emits one references link per resolved file path.",
1176
+ stability: "stable",
1177
+ mode: "deterministic",
1178
+ emitsLinkKinds: ["references"],
1179
+ defaultConfidence: "high",
1180
+ scope: "body",
1181
+ extract(ctx) {
1182
+ const seen = /* @__PURE__ */ new Set();
1183
+ const lineStarts = computeLineStarts2(ctx.body);
1184
+ const sourceDir = pathPosix.dirname(ctx.node.path);
1185
+ for (const match of ctx.body.matchAll(LINK_RE)) {
1186
+ const original = match[2];
1187
+ const resolved = resolveTarget(sourceDir, original);
1188
+ if (resolved === null) continue;
1189
+ if (seen.has(resolved)) continue;
1190
+ seen.add(resolved);
1191
+ const offset = match.index ?? 0;
1192
+ const link2 = {
1193
+ source: ctx.node.path,
1194
+ target: resolved,
1195
+ kind: "references",
1196
+ confidence: "high",
1197
+ sources: [ID5],
1198
+ trigger: {
1199
+ originalTrigger: original,
1200
+ normalizedTrigger: resolved
1201
+ },
1202
+ location: { line: lineFor2(lineStarts, offset) }
1203
+ };
1204
+ ctx.emitLink(link2);
1205
+ }
1206
+ }
1207
+ };
1208
+ function resolveTarget(sourceDir, raw) {
1209
+ const noFragment = raw.split("#", 1)[0];
1210
+ const noQuery = noFragment.split("?", 1)[0];
1211
+ const trimmed = noQuery.trim();
1212
+ if (trimmed.length === 0) return null;
1213
+ if (URL_SCHEME_RE.test(trimmed)) return null;
1214
+ if (trimmed.startsWith("/")) return null;
1215
+ const joined = sourceDir === "." ? trimmed : `${sourceDir}/${trimmed}`;
1216
+ return pathPosix.normalize(joined);
1217
+ }
1218
+ function computeLineStarts2(body) {
1219
+ const starts = [0];
1220
+ for (let i = 0; i < body.length; i += 1) {
1221
+ if (body.charCodeAt(i) === 10) starts.push(i + 1);
1222
+ }
1223
+ return starts;
1224
+ }
1225
+ function lineFor2(lineStarts, offset) {
1226
+ let lo = 0;
1227
+ let hi = lineStarts.length - 1;
1228
+ while (lo < hi) {
1229
+ const mid = lo + hi + 1 >>> 1;
1230
+ if (lineStarts[mid] <= offset) lo = mid;
1231
+ else hi = mid - 1;
1232
+ }
1233
+ return lo + 1;
1234
+ }
1235
+
1129
1236
  // built-in-plugins/i18n/trigger-collision.texts.ts
1130
1237
  var TRIGGER_COLLISION_TEXTS = {
1131
1238
  /**
@@ -1152,14 +1259,14 @@ var TRIGGER_COLLISION_TEXTS = {
1152
1259
  };
1153
1260
 
1154
1261
  // built-in-plugins/rules/trigger-collision/index.ts
1155
- var ID5 = "trigger-collision";
1262
+ var ID6 = "trigger-collision";
1156
1263
  var ADVERTISING_KINDS = /* @__PURE__ */ new Set([
1157
1264
  "command",
1158
1265
  "skill",
1159
1266
  "agent"
1160
1267
  ]);
1161
1268
  var triggerCollisionRule = {
1162
- id: ID5,
1269
+ id: ID6,
1163
1270
  pluginId: "core",
1164
1271
  kind: "rule",
1165
1272
  mode: "deterministic",
@@ -1259,7 +1366,7 @@ function analyzeTriggerBucket(normalized, claims) {
1259
1366
  part: parts[0]
1260
1367
  });
1261
1368
  return {
1262
- ruleId: ID5,
1369
+ ruleId: ID6,
1263
1370
  severity: "error",
1264
1371
  nodeIds,
1265
1372
  message,
@@ -1278,9 +1385,9 @@ var BROKEN_REF_TEXTS = {
1278
1385
  };
1279
1386
 
1280
1387
  // built-in-plugins/rules/broken-ref/index.ts
1281
- var ID6 = "broken-ref";
1388
+ var ID7 = "broken-ref";
1282
1389
  var brokenRefRule = {
1283
- id: ID6,
1390
+ id: ID7,
1284
1391
  pluginId: "core",
1285
1392
  kind: "rule",
1286
1393
  version: "1.0.0",
@@ -1294,7 +1401,7 @@ var brokenRefRule = {
1294
1401
  for (const link2 of ctx.links) {
1295
1402
  if (isResolved(link2, byPath3, byNormalizedName)) continue;
1296
1403
  issues.push({
1297
- ruleId: ID6,
1404
+ ruleId: ID7,
1298
1405
  severity: "warn",
1299
1406
  nodeIds: [link2.source],
1300
1407
  message: tx(BROKEN_REF_TEXTS.message, {
@@ -1342,9 +1449,9 @@ var SUPERSEDED_TEXTS = {
1342
1449
  };
1343
1450
 
1344
1451
  // built-in-plugins/rules/superseded/index.ts
1345
- var ID7 = "superseded";
1452
+ var ID8 = "superseded";
1346
1453
  var supersededRule = {
1347
- id: ID7,
1454
+ id: ID8,
1348
1455
  pluginId: "core",
1349
1456
  kind: "rule",
1350
1457
  version: "1.0.0",
@@ -1359,7 +1466,7 @@ var supersededRule = {
1359
1466
  const supersededBy = meta["supersededBy"];
1360
1467
  if (typeof supersededBy !== "string" || supersededBy.length === 0) continue;
1361
1468
  issues.push({
1362
- ruleId: ID7,
1469
+ ruleId: ID8,
1363
1470
  severity: "info",
1364
1471
  nodeIds: [node.path],
1365
1472
  message: tx(SUPERSEDED_TEXTS.message, {
@@ -1380,9 +1487,9 @@ var LINK_CONFLICT_TEXTS = {
1380
1487
  };
1381
1488
 
1382
1489
  // built-in-plugins/rules/link-conflict/index.ts
1383
- var ID8 = "link-conflict";
1490
+ var ID9 = "link-conflict";
1384
1491
  var linkConflictRule = {
1385
- id: ID8,
1492
+ id: ID9,
1386
1493
  pluginId: "core",
1387
1494
  kind: "rule",
1388
1495
  version: "1.0.0",
@@ -1431,7 +1538,7 @@ var linkConflictRule = {
1431
1538
  const [source, target] = key.split("\0");
1432
1539
  const kindList = variants.map((v) => v.kind).join(" / ");
1433
1540
  issues.push({
1434
- ruleId: ID8,
1541
+ ruleId: ID9,
1435
1542
  severity: "warn",
1436
1543
  nodeIds: [source, target],
1437
1544
  message: tx(LINK_CONFLICT_TEXTS.message, {
@@ -1477,10 +1584,10 @@ var ASCII_FORMATTER_TEXTS = {
1477
1584
  };
1478
1585
 
1479
1586
  // built-in-plugins/formatters/ascii/index.ts
1480
- var ID9 = "ascii";
1587
+ var ID10 = "ascii";
1481
1588
  var KIND_ORDER = ["agent", "command", "hook", "skill", "note"];
1482
1589
  var asciiFormatter = {
1483
- id: ID9,
1590
+ id: ID10,
1484
1591
  pluginId: "core",
1485
1592
  kind: "formatter",
1486
1593
  version: "1.0.0",
@@ -1743,9 +1850,9 @@ var VALIDATE_ALL_TEXTS = {
1743
1850
  };
1744
1851
 
1745
1852
  // built-in-plugins/rules/validate-all/index.ts
1746
- var ID10 = "validate-all";
1853
+ var ID11 = "validate-all";
1747
1854
  var validateAllRule = {
1748
- id: ID10,
1855
+ id: ID11,
1749
1856
  pluginId: "core",
1750
1857
  kind: "rule",
1751
1858
  version: "1.0.0",
@@ -1768,7 +1875,7 @@ function collectNodeFindings(v, node, out) {
1768
1875
  const result = v.validate("node", toNodeForSchema(node));
1769
1876
  if (result.ok) return;
1770
1877
  out.push({
1771
- ruleId: ID10,
1878
+ ruleId: ID11,
1772
1879
  severity: "error",
1773
1880
  nodeIds: [node.path],
1774
1881
  message: tx(VALIDATE_ALL_TEXTS.nodeFailure, {
@@ -1782,7 +1889,7 @@ function collectLinkFindings(v, link2, out) {
1782
1889
  const result = v.validate("link", toLinkForSchema(link2));
1783
1890
  if (result.ok) return;
1784
1891
  out.push({
1785
- ruleId: ID10,
1892
+ ruleId: ID11,
1786
1893
  severity: "error",
1787
1894
  nodeIds: [link2.source],
1788
1895
  message: tx(VALIDATE_ALL_TEXTS.linkFailure, {
@@ -1843,6 +1950,7 @@ var builtInBundles = [
1843
1950
  granularity: "extension",
1844
1951
  extensions: [
1845
1952
  externalUrlCounterExtractor,
1953
+ markdownLinkExtractor,
1846
1954
  triggerCollisionRule,
1847
1955
  brokenRefRule,
1848
1956
  supersededRule,
@@ -2850,7 +2958,7 @@ var AsyncMutex = class {
2850
2958
  this.#locked = true;
2851
2959
  return;
2852
2960
  }
2853
- await new Promise((resolve20) => this.#waiters.push(resolve20));
2961
+ await new Promise((resolve21) => this.#waiters.push(resolve21));
2854
2962
  this.#locked = true;
2855
2963
  }
2856
2964
  unlock() {
@@ -4097,6 +4205,7 @@ async function loadNodeEnrichments(db, nodePath) {
4097
4205
  }));
4098
4206
  }
4099
4207
  function parseJsonObject(s) {
4208
+ if (s === null || s === void 0) return {};
4100
4209
  const parsed = JSON.parse(s);
4101
4210
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
4102
4211
  return parsed;
@@ -4104,6 +4213,7 @@ function parseJsonObject(s) {
4104
4213
  return {};
4105
4214
  }
4106
4215
  function parseJsonArray(s) {
4216
+ if (s === null || s === void 0) return [];
4107
4217
  const parsed = JSON.parse(s);
4108
4218
  return Array.isArray(parsed) ? parsed : [];
4109
4219
  }
@@ -5114,6 +5224,7 @@ function formatErrorMessage(err) {
5114
5224
  // cli/i18n/config.texts.ts
5115
5225
  var CONFIG_TEXTS = {
5116
5226
  unknownKey: "Unknown config key: {{key}}\n",
5227
+ unknownKeySuggestion: "Did you mean {{suggestions}}?\n",
5117
5228
  valueWithLayer: "{{value}} (from {{layer}})\n",
5118
5229
  invalidAfterSet: "Invalid config after set: {{errors}}\n",
5119
5230
  setWritten: "{{key}} = {{value}} (wrote {{path}})\n",
@@ -5143,6 +5254,29 @@ function assertSafeSegments(segments, key) {
5143
5254
  if (FORBIDDEN_SEGMENTS.has(seg)) throw new ForbiddenSegmentError(seg, key);
5144
5255
  }
5145
5256
  }
5257
+ function isPlainObject2(v) {
5258
+ return !!v && typeof v === "object" && !Array.isArray(v);
5259
+ }
5260
+ function enumerateConfigPaths(obj, prefix = "") {
5261
+ if (!isPlainObject2(obj)) return prefix ? [prefix] : [];
5262
+ const out = [];
5263
+ for (const [key, value] of Object.entries(obj)) {
5264
+ const path = prefix ? `${prefix}.${key}` : key;
5265
+ if (isPlainObject2(value)) {
5266
+ out.push(...enumerateConfigPaths(value, path));
5267
+ } else {
5268
+ out.push(path);
5269
+ }
5270
+ }
5271
+ return out;
5272
+ }
5273
+ function suggestConfigKey(effective, typed) {
5274
+ const candidates = enumerateConfigPaths(effective);
5275
+ const matches = closestMatches(typed, candidates, { topN: 3, maxDistance: 3 });
5276
+ if (matches.length === 0) return null;
5277
+ const formatted = matches.map((m) => `'${m}'`).join(", ");
5278
+ return tx(CONFIG_TEXTS.unknownKeySuggestion, { suggestions: formatted });
5279
+ }
5146
5280
  function getAtPath(obj, dotPath) {
5147
5281
  const segments = dotPath.split(".").filter(Boolean);
5148
5282
  assertSafeSegments(segments, dotPath);
@@ -5244,6 +5378,17 @@ function tryLoadConfig(opts, stderr) {
5244
5378
  return { ok: false, exitCode: ExitCode.Error };
5245
5379
  }
5246
5380
  }
5381
+ function safeGetAtPath(effective, key, stderr) {
5382
+ try {
5383
+ return { ok: true, value: getAtPath(effective, key) };
5384
+ } catch (err) {
5385
+ if (err instanceof ForbiddenSegmentError) {
5386
+ stderr.write(tx(CONFIG_TEXTS.forbiddenKeySegment, { segment: err.segment, key: err.key }));
5387
+ return { ok: false, exitCode: ExitCode.Error };
5388
+ }
5389
+ throw err;
5390
+ }
5391
+ }
5247
5392
  function* iterDotPaths(obj, prefix = "") {
5248
5393
  if (obj === null || typeof obj !== "object" || Array.isArray(obj)) {
5249
5394
  if (prefix) yield [prefix, obj];
@@ -5321,18 +5466,13 @@ var ConfigGetCommand = class extends SmCommand {
5321
5466
  if (!result.ok) return result.exitCode;
5322
5467
  const { effective, warnings } = result.loaded;
5323
5468
  for (const w of warnings) this.context.stderr.write(w + "\n");
5324
- let value;
5325
- try {
5326
- value = getAtPath(effective, this.key);
5327
- } catch (err) {
5328
- if (err instanceof ForbiddenSegmentError) {
5329
- this.context.stderr.write(tx(CONFIG_TEXTS.forbiddenKeySegment, { segment: err.segment, key: err.key }));
5330
- return ExitCode.Error;
5331
- }
5332
- throw err;
5333
- }
5469
+ const lookup = safeGetAtPath(effective, this.key, this.context.stderr);
5470
+ if (!lookup.ok) return lookup.exitCode;
5471
+ const { value } = lookup;
5334
5472
  if (value === void 0) {
5335
5473
  this.context.stderr.write(tx(CONFIG_TEXTS.unknownKey, { key: this.key }));
5474
+ const suggestion = suggestConfigKey(effective, this.key);
5475
+ if (suggestion !== null) this.context.stderr.write(suggestion);
5336
5476
  return ExitCode.NotFound;
5337
5477
  }
5338
5478
  if (this.json) {
@@ -6195,9 +6335,9 @@ var DB_TEXTS = {
6195
6335
  migrateKernelUpToDate: "kernel \xB7 Already up to date.\n",
6196
6336
  migrateKernelApplied: "kernel \xB7 Applied {{count}} migration(s)\n",
6197
6337
  migrateKernelAppliedWithBackup: "kernel \xB7 Applied {{count}} migration(s) \xB7 backup: {{backupPath}}\n",
6198
- // --- shell / dump (system sqlite3 binary required) ------------------
6338
+ // --- shell (system sqlite3 binary required for the interactive REPL) ---
6199
6339
  shellSqlite3NotFound: "sqlite3 binary not found on PATH. Install it (macOS: brew install sqlite; Debian/Ubuntu: apt install sqlite3) or use `sm db dump` for read-only inspection.\n",
6200
- dumpSqlite3NotFound: "sqlite3 binary not found on PATH. Install it to use `sm db dump`.\n",
6340
+ // --- dump (pure node:sqlite, no external binary) ----------------------
6201
6341
  dumpInvalidTable: "--tables: refusing non-identifier name {{table}}. Table names must match [a-zA-Z_][a-zA-Z0-9_]*\n",
6202
6342
  // --- plugin migration runner -----------------------------------------
6203
6343
  pluginMigrateFailure: "plugin {{pluginId}} \xB7 {{reason}}\n",
@@ -6488,18 +6628,12 @@ var DbDumpCommand = class extends SmCommand {
6488
6628
  static usage = Command5.Usage({
6489
6629
  category: "Database",
6490
6630
  description: "SQL dump to stdout.",
6491
- details: "Read-only. Use --tables <names...> to limit the dump to specific tables."
6631
+ details: "Read-only. Pure node:sqlite \u2014 no external `sqlite3` binary required. Use --tables <names...> to limit the dump to specific tables."
6492
6632
  });
6493
6633
  tables = Option5.Array("--tables", { required: false });
6494
- // CLI orchestrator: each branch (db existence, per-table identifier
6495
- // gate, sqlite3-not-found fallback, exit-status passthrough) is a
6496
- // single dispatcher decision. Splitting per branch scatters the gate
6497
- // away from the value it gates.
6498
- // eslint-disable-next-line complexity
6499
6634
  async run() {
6500
6635
  const path = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });
6501
6636
  if (!assertDbExists(path, this.context.stderr)) return ExitCode.NotFound;
6502
- const args2 = ["-readonly", path, ".dump"];
6503
6637
  if (this.tables && this.tables.length > 0) {
6504
6638
  for (const t of this.tables) {
6505
6639
  if (!SAFE_SQL_IDENTIFIER_RE.test(t)) {
@@ -6507,16 +6641,65 @@ var DbDumpCommand = class extends SmCommand {
6507
6641
  return ExitCode.Error;
6508
6642
  }
6509
6643
  }
6510
- args2.push(...this.tables);
6511
6644
  }
6512
- const result = spawnSync2("sqlite3", args2, { stdio: ["ignore", "inherit", "inherit"] });
6513
- if (result.error && result.error.code === "ENOENT") {
6514
- this.context.stderr.write(DB_TEXTS.dumpSqlite3NotFound);
6645
+ try {
6646
+ dumpDatabaseToStream(path, this.context.stdout, this.tables ?? null);
6647
+ return ExitCode.Ok;
6648
+ } catch (err) {
6649
+ this.context.stderr.write(`sm db dump: ${err.message}
6650
+ `);
6515
6651
  return ExitCode.Error;
6516
6652
  }
6517
- return result.status ?? 0;
6518
6653
  }
6519
6654
  };
6655
+ function dumpDatabaseToStream(dbPath, out, tables) {
6656
+ const db = new DatabaseSync4(dbPath, { readOnly: true });
6657
+ try {
6658
+ out.write("PRAGMA foreign_keys=OFF;\n");
6659
+ out.write("BEGIN TRANSACTION;\n");
6660
+ const objects = listSchemaObjects(db, tables);
6661
+ for (const obj of objects) {
6662
+ if (!obj.sql) continue;
6663
+ out.write(`${obj.sql};
6664
+ `);
6665
+ }
6666
+ for (const obj of objects) {
6667
+ if (obj.type !== "table") continue;
6668
+ writeTableData(db, out, obj.name);
6669
+ }
6670
+ out.write("COMMIT;\n");
6671
+ } finally {
6672
+ db.close();
6673
+ }
6674
+ }
6675
+ function listSchemaObjects(db, tables) {
6676
+ const baseQuery = "SELECT type, name, sql FROM sqlite_master WHERE type IN ('table','index','trigger','view') AND name NOT LIKE 'sqlite_%'";
6677
+ if (tables === null || tables.length === 0) {
6678
+ return db.prepare(`${baseQuery} ORDER BY rootpage`).all();
6679
+ }
6680
+ const placeholders = tables.map(() => "?").join(",");
6681
+ const sql4 = `${baseQuery} AND (name IN (${placeholders}) OR tbl_name IN (${placeholders})) ORDER BY rootpage`;
6682
+ return db.prepare(sql4).all(...tables, ...tables);
6683
+ }
6684
+ function writeTableData(db, out, tableName) {
6685
+ const quoted = `"${tableName.replace(/"/g, '""')}"`;
6686
+ for (const row of db.prepare(`SELECT * FROM ${quoted}`).iterate()) {
6687
+ const values = Object.values(row).map(formatSqlValue).join(",");
6688
+ out.write(`INSERT INTO ${quoted} VALUES(${values});
6689
+ `);
6690
+ }
6691
+ }
6692
+ function formatSqlNumber(value) {
6693
+ return Number.isFinite(value) ? String(value) : "NULL";
6694
+ }
6695
+ function formatSqlValue(value) {
6696
+ if (value === null || value === void 0) return "NULL";
6697
+ if (typeof value === "number") return formatSqlNumber(value);
6698
+ if (typeof value === "bigint") return value.toString();
6699
+ if (typeof value === "boolean") return value ? "1" : "0";
6700
+ if (value instanceof Uint8Array) return `X'${Buffer.from(value).toString("hex")}'`;
6701
+ return `'${String(value).replace(/'/g, "''")}'`;
6702
+ }
6520
6703
  var DbMigrateCommand = class extends SmCommand {
6521
6704
  static paths = [["db", "migrate"]];
6522
6705
  static usage = Command5.Usage({
@@ -6906,18 +7089,19 @@ var ExportCommand = class extends SmCommand {
6906
7089
  (POSIX glob \u2014 \`*\` matches a single segment, \`**\` matches across
6907
7090
  segments).
6908
7091
 
6909
- Pass an empty query (\`""\`) to export every node.
7092
+ Pass an empty query (\`""\`) \u2014 or omit the argument entirely \u2014 to
7093
+ export every node.
6910
7094
 
6911
7095
  Run \`sm scan\` first to populate the DB.
6912
7096
  `,
6913
7097
  examples: [
7098
+ ["Whole graph (no query)", "$0 export --format md"],
6914
7099
  ["Every command node", '$0 export "kind=command" --format json'],
6915
7100
  ["Skills + agents with issues", '$0 export "kind=skill,agent has=issues" --format md'],
6916
- ["Files under a path glob", '$0 export "path=.claude/commands/**" --format json'],
6917
- ["Whole graph as Markdown", '$0 export "" --format md']
7101
+ ["Files under a path glob", '$0 export "path=.claude/commands/**" --format json']
6918
7102
  ]
6919
7103
  });
6920
- query = Option6.String({ required: true });
7104
+ query = Option6.String({ required: false });
6921
7105
  format = Option6.String("--format", { required: false });
6922
7106
  async run() {
6923
7107
  const format = (this.format ?? "json").toLowerCase();
@@ -6942,7 +7126,7 @@ var ExportCommand = class extends SmCommand {
6942
7126
  }
6943
7127
  let parsedQuery;
6944
7128
  try {
6945
- parsedQuery = parseExportQuery(this.query);
7129
+ parsedQuery = parseExportQuery(this.query ?? "");
6946
7130
  } catch (err) {
6947
7131
  if (err instanceof ExportQueryError) {
6948
7132
  this.context.stderr.write(tx(EXPORT_TEXTS.errorPrefix, { message: err.message }));
@@ -7169,16 +7353,108 @@ var GraphCommand = class extends SmCommand {
7169
7353
  }
7170
7354
  };
7171
7355
 
7356
+ // cli/commands/guide.ts
7357
+ import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
7358
+ import { writeFile } from "fs/promises";
7359
+ import { dirname as dirname9, join as join10, resolve as resolve13 } from "path";
7360
+ import { fileURLToPath as fileURLToPath5 } from "url";
7361
+ import { Command as Command8, Option as Option8 } from "clipanion";
7362
+
7363
+ // cli/i18n/guide.texts.ts
7364
+ var GUIDE_TEXTS = {
7365
+ // Success — written to stdout after `<cwd>/sm-guide.md` is created.
7366
+ written: 'Listo. sm-guide.md creado en {{cwd}}. Abr\xED Claude Code ac\xE1 y decile "gu\xEDame" para arrancar la gu\xEDa interactiva.\n',
7367
+ // Refusal — `sm-guide.md` already exists and `--force` was not set.
7368
+ // Goes to stderr, exit code 2 (operational error per spec § Exit codes).
7369
+ alreadyExists: "sm guide: sm-guide.md ya existe en {{cwd}}. Us\xE1 `--force` para sobrescribir.\n",
7370
+ // I/O failure on write or on reading the bundled SKILL source.
7371
+ writeFailed: "sm guide: no se pudo escribir sm-guide.md: {{message}}\n",
7372
+ sourceMissing: "sm guide: no se pudo leer la gu\xEDa empaquetada (SKILL.md) desde la instalaci\xF3n. Reinstal\xE1 @skill-map/cli o report\xE1 el bug.\n"
7373
+ };
7374
+
7375
+ // cli/commands/guide.ts
7376
+ var SM_GUIDE_FILENAME = "sm-guide.md";
7377
+ var GuideCommand = class extends SmCommand {
7378
+ static paths = [["guide"]];
7379
+ static usage = Command8.Usage({
7380
+ category: "Setup",
7381
+ description: "Materialize the interactive tester guide (sm-guide.md) in the current directory.",
7382
+ details: `
7383
+ Drops the canonical SKILL.md content as ./sm-guide.md so a tester
7384
+ can open Claude Code in the cwd and trigger the sm-guide skill
7385
+ ("gu\xEDame"). Top-level only \u2014 no subdirectory is created.
7386
+
7387
+ Does NOT require an initialized .skill-map/ project. Refuses to
7388
+ overwrite an existing sm-guide.md unless --force is passed.
7389
+ `,
7390
+ examples: [
7391
+ ["Materialize the guide in the cwd", "$0 guide"],
7392
+ ["Overwrite an existing sm-guide.md", "$0 guide --force"]
7393
+ ]
7394
+ });
7395
+ force = Option8.Boolean("--force", false, {
7396
+ description: "Overwrite an existing sm-guide.md without prompting."
7397
+ });
7398
+ async run() {
7399
+ const ctx = defaultRuntimeContext();
7400
+ const target = join10(ctx.cwd, SM_GUIDE_FILENAME);
7401
+ if (await pathExists(target) && !this.force) {
7402
+ this.context.stderr.write(tx(GUIDE_TEXTS.alreadyExists, { cwd: ctx.cwd }));
7403
+ return ExitCode.Error;
7404
+ }
7405
+ let body;
7406
+ try {
7407
+ body = loadBundledGuideText();
7408
+ } catch {
7409
+ this.context.stderr.write(GUIDE_TEXTS.sourceMissing);
7410
+ return ExitCode.Error;
7411
+ }
7412
+ try {
7413
+ await writeFile(target, body);
7414
+ } catch (err) {
7415
+ this.context.stderr.write(
7416
+ tx(GUIDE_TEXTS.writeFailed, { message: formatErrorMessage(err) })
7417
+ );
7418
+ return ExitCode.Error;
7419
+ }
7420
+ this.context.stdout.write(tx(GUIDE_TEXTS.written, { cwd: ctx.cwd }));
7421
+ return ExitCode.Ok;
7422
+ }
7423
+ };
7424
+ var cachedGuide = null;
7425
+ function loadBundledGuideText() {
7426
+ if (cachedGuide !== null) return cachedGuide;
7427
+ cachedGuide = readGuideFromDisk();
7428
+ return cachedGuide;
7429
+ }
7430
+ function readGuideFromDisk() {
7431
+ const here = dirname9(fileURLToPath5(import.meta.url));
7432
+ const candidates = [
7433
+ // dev: src/cli/commands/ → repo-root .claude/skills/sm-guide/SKILL.md
7434
+ resolve13(here, "../../../.claude/skills/sm-guide/SKILL.md"),
7435
+ // bundled: dist/cli.js → dist/cli/guide/sm-guide.md (sibling)
7436
+ resolve13(here, "cli/guide/sm-guide.md"),
7437
+ // bundled fallback: any-depth → cli/guide/sm-guide.md
7438
+ resolve13(here, "../cli/guide/sm-guide.md")
7439
+ ];
7440
+ for (const candidate of candidates) {
7441
+ if (existsSync12(candidate)) {
7442
+ return readFileSync10(candidate, "utf8");
7443
+ }
7444
+ }
7445
+ throw new Error(`SKILL.md not found in any candidate location (last tried: ${candidates[candidates.length - 1]})`);
7446
+ }
7447
+
7172
7448
  // cli/commands/help.ts
7173
- import { readFileSync as readFileSync10 } from "fs";
7449
+ import { readFileSync as readFileSync11 } from "fs";
7174
7450
  import { createRequire as createRequire4 } from "module";
7175
- import { resolve as resolve13 } from "path";
7176
- import { Command as Command8, Option as Option8 } from "clipanion";
7451
+ import { resolve as resolve14 } from "path";
7452
+ import { Command as Command9, Option as Option9 } from "clipanion";
7177
7453
 
7178
7454
  // package.json
7179
7455
  var package_default = {
7180
7456
  name: "@skill-map/cli",
7181
- version: "0.13.0",
7457
+ version: "0.14.0",
7182
7458
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
7183
7459
  license: "MIT",
7184
7460
  type: "module",
@@ -7237,7 +7513,7 @@ var package_default = {
7237
7513
  },
7238
7514
  dependencies: {
7239
7515
  "@hono/node-server": "2.0.1",
7240
- "@skill-map/spec": "*",
7516
+ "@skill-map/spec": "0.14.1",
7241
7517
  ajv: "8.18.0",
7242
7518
  "ajv-formats": "3.0.1",
7243
7519
  chokidar: "5.0.0",
@@ -7354,9 +7630,9 @@ var HELP_TEXTS = {
7354
7630
  };
7355
7631
 
7356
7632
  // cli/commands/help.ts
7357
- var HelpCommand = class extends Command8 {
7633
+ var HelpCommand = class extends Command9 {
7358
7634
  static paths = [["help"]];
7359
- static usage = Command8.Usage({
7635
+ static usage = Command9.Usage({
7360
7636
  category: "Introspection",
7361
7637
  description: "Self-describing introspection. --format human|md|json.",
7362
7638
  details: `
@@ -7370,8 +7646,8 @@ var HelpCommand = class extends Command8 {
7370
7646
  json \u2014 structured surface dump per spec/cli-contract.md.
7371
7647
  `
7372
7648
  });
7373
- verbParts = Option8.Rest({ required: 0 });
7374
- format = Option8.String("--format", "human");
7649
+ verbParts = Option9.Rest({ required: 0 });
7650
+ format = Option9.String("--format", "human");
7375
7651
  async execute() {
7376
7652
  const format = normalizeFormat(this.format);
7377
7653
  if (!format) {
@@ -7493,8 +7769,8 @@ function resolveSpecVersion() {
7493
7769
  try {
7494
7770
  const req = createRequire4(import.meta.url);
7495
7771
  const indexPath = req.resolve("@skill-map/spec/index.json");
7496
- const pkgPath = resolve13(indexPath, "..", "package.json");
7497
- const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
7772
+ const pkgPath = resolve14(indexPath, "..", "package.json");
7773
+ const pkg = JSON.parse(readFileSync11(pkgPath, "utf8"));
7498
7774
  return pkg.version;
7499
7775
  } catch {
7500
7776
  return "unknown";
@@ -7689,7 +7965,7 @@ function renderCompactOverview(verbs) {
7689
7965
  lines.push(HELP_TEXTS.compactFooter);
7690
7966
  return lines.join("\n") + "\n";
7691
7967
  }
7692
- var RootHelpCommand = class extends Command8 {
7968
+ var RootHelpCommand = class extends Command9 {
7693
7969
  static paths = [["-h"], ["--help"]];
7694
7970
  async execute() {
7695
7971
  const rawDefs = this.cli.definitions();
@@ -7745,13 +8021,13 @@ function registeredVerbPaths(cli2) {
7745
8021
  }
7746
8022
 
7747
8023
  // cli/commands/init.ts
7748
- import { mkdir as mkdir2, readFile as readFile2, writeFile } from "fs/promises";
7749
- import { join as join10 } from "path";
7750
- import { Command as Command9, Option as Option9 } from "clipanion";
8024
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
8025
+ import { join as join11 } from "path";
8026
+ import { Command as Command10, Option as Option10 } from "clipanion";
7751
8027
 
7752
8028
  // kernel/orchestrator.ts
7753
8029
  import { createHash } from "crypto";
7754
- import { existsSync as existsSync12, statSync as statSync3 } from "fs";
8030
+ import { existsSync as existsSync13, statSync as statSync3 } from "fs";
7755
8031
  import { Tiktoken } from "js-tiktoken/lite";
7756
8032
  import cl100k_base from "js-tiktoken/ranks/cl100k_base";
7757
8033
  import yaml2 from "js-yaml";
@@ -7886,7 +8162,7 @@ function validateRoots(roots) {
7886
8162
  throw new Error(ORCHESTRATOR_TEXTS.runScanRootEmptyArray);
7887
8163
  }
7888
8164
  for (const root of roots) {
7889
- if (!existsSync12(root) || !statSync3(root).isDirectory()) {
8165
+ if (!existsSync13(root) || !statSync3(root).isDirectory()) {
7890
8166
  throw new Error(tx(ORCHESTRATOR_TEXTS.runScanRootMissing, { root }));
7891
8167
  }
7892
8168
  }
@@ -8662,10 +8938,10 @@ function recomputeExternalRefsCount(nodes, externalLinks, cachedPaths) {
8662
8938
  }
8663
8939
 
8664
8940
  // kernel/scan/watcher.ts
8665
- import { resolve as resolve14, relative as relative4, sep as sep2 } from "path";
8941
+ import { resolve as resolve15, relative as relative4, sep as sep2 } from "path";
8666
8942
  import chokidar from "chokidar";
8667
8943
  function createChokidarWatcher(opts) {
8668
- const absRoots = opts.roots.map((r) => resolve14(opts.cwd, r));
8944
+ const absRoots = opts.roots.map((r) => resolve15(opts.cwd, r));
8669
8945
  const ignoreFilter = opts.ignoreFilter;
8670
8946
  const ignored = ignoreFilter ? (path) => {
8671
8947
  const rel = relativePathFromRoots(path, absRoots);
@@ -8911,7 +9187,7 @@ function createCliProgressEmitter(stderr) {
8911
9187
  // cli/commands/init.ts
8912
9188
  var InitCommand = class extends SmCommand {
8913
9189
  static paths = [["init"]];
8914
- static usage = Command9.Usage({
9190
+ static usage = Command10.Usage({
8915
9191
  category: "Setup",
8916
9192
  description: "Bootstrap the current scope: scaffold .skill-map/, provision DB, run first scan.",
8917
9193
  details: `
@@ -8935,16 +9211,16 @@ var InitCommand = class extends SmCommand {
8935
9211
  ["Preview what would be created", "$0 init --dry-run"]
8936
9212
  ]
8937
9213
  });
8938
- noScan = Option9.Boolean("--no-scan", false, {
9214
+ noScan = Option10.Boolean("--no-scan", false, {
8939
9215
  description: "Skip the first scan after scaffolding."
8940
9216
  });
8941
- force = Option9.Boolean("--force", false, {
9217
+ force = Option10.Boolean("--force", false, {
8942
9218
  description: "Overwrite an existing settings.json / settings.local.json / .skill-mapignore."
8943
9219
  });
8944
- strict = Option9.Boolean("--strict", false, {
9220
+ strict = Option10.Boolean("--strict", false, {
8945
9221
  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."
8946
9222
  });
8947
- dryRun = Option9.Boolean("-n,--dry-run", false, {
9223
+ dryRun = Option10.Boolean("-n,--dry-run", false, {
8948
9224
  description: "Preview the scope provisioning without touching the filesystem or the DB. Honours --force for the would-overwrite preview. Skips the first scan unconditionally \u2014 dry-run never persists."
8949
9225
  });
8950
9226
  // CLI orchestrator: paths setup + dry-run branch (delegated to
@@ -8955,7 +9231,7 @@ var InitCommand = class extends SmCommand {
8955
9231
  async run() {
8956
9232
  const ctx = defaultRuntimeContext();
8957
9233
  const scopeRoot = this.global ? ctx.homedir : ctx.cwd;
8958
- const skillMapDir = join10(scopeRoot, SKILL_MAP_DIR);
9234
+ const skillMapDir = join11(scopeRoot, SKILL_MAP_DIR);
8959
9235
  const settingsPath = defaultSettingsPath(scopeRoot);
8960
9236
  const localPath = defaultLocalSettingsPath(scopeRoot);
8961
9237
  const ignorePath = defaultIgnoreFilePath(scopeRoot);
@@ -8979,17 +9255,17 @@ var InitCommand = class extends SmCommand {
8979
9255
  return ExitCode.Ok;
8980
9256
  }
8981
9257
  await mkdir2(skillMapDir, { recursive: true });
8982
- await writeFile(settingsPath, JSON.stringify({ schemaVersion: 1 }, null, 2) + "\n");
9258
+ await writeFile2(settingsPath, JSON.stringify({ schemaVersion: 1 }, null, 2) + "\n");
8983
9259
  if (!await pathExists(localPath) || this.force) {
8984
- await writeFile(localPath, "{}\n");
9260
+ await writeFile2(localPath, "{}\n");
8985
9261
  }
8986
9262
  if (!await pathExists(ignorePath) || this.force) {
8987
- await writeFile(ignorePath, loadBundledIgnoreText());
9263
+ await writeFile2(ignorePath, loadBundledIgnoreText());
8988
9264
  }
8989
9265
  if (!this.global) {
8990
9266
  const updated = await ensureGitignoreEntries(scopeRoot, GITIGNORE_ENTRIES);
8991
9267
  if (updated) {
8992
- const gitignorePath = join10(scopeRoot, ".gitignore");
9268
+ const gitignorePath = join11(scopeRoot, ".gitignore");
8993
9269
  this.context.stdout.write(
8994
9270
  GITIGNORE_ENTRIES.length === 1 ? tx(INIT_TEXTS.gitignoreUpdatedSingular, { path: gitignorePath }) : tx(INIT_TEXTS.gitignoreUpdatedPlural, {
8995
9271
  path: gitignorePath,
@@ -9028,7 +9304,7 @@ async function dryRunFileMessage(path) {
9028
9304
  }
9029
9305
  async function writeDryRunGitignorePlan(stdout, scopeRoot) {
9030
9306
  const wouldAdd = await previewGitignoreEntries(scopeRoot, GITIGNORE_ENTRIES);
9031
- const gitignorePath = join10(scopeRoot, ".gitignore");
9307
+ const gitignorePath = join11(scopeRoot, ".gitignore");
9032
9308
  if (wouldAdd.length === 0) {
9033
9309
  stdout.write(tx(INIT_TEXTS.dryRunWouldLeaveGitignoreUnchanged, { path: gitignorePath }));
9034
9310
  } else if (wouldAdd.length === 1) {
@@ -9103,7 +9379,7 @@ async function runFirstScan(scopeRoot, homedir2, dbPath, strict, stdout, stderr)
9103
9379
  return hasErrors ? ExitCode.Issues : ExitCode.Ok;
9104
9380
  }
9105
9381
  async function previewGitignoreEntries(scopeRoot, entries) {
9106
- const path = join10(scopeRoot, ".gitignore");
9382
+ const path = join11(scopeRoot, ".gitignore");
9107
9383
  const body = await pathExists(path) ? await readFile2(path, "utf8") : "";
9108
9384
  const present = new Set(
9109
9385
  body.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"))
@@ -9111,7 +9387,7 @@ async function previewGitignoreEntries(scopeRoot, entries) {
9111
9387
  return entries.filter((entry) => !present.has(entry));
9112
9388
  }
9113
9389
  async function ensureGitignoreEntries(scopeRoot, entries) {
9114
- const path = join10(scopeRoot, ".gitignore");
9390
+ const path = join11(scopeRoot, ".gitignore");
9115
9391
  let body = "";
9116
9392
  if (await pathExists(path)) {
9117
9393
  body = await readFile2(path, "utf8");
@@ -9128,12 +9404,12 @@ async function ensureGitignoreEntries(scopeRoot, entries) {
9128
9404
  present.add(entry);
9129
9405
  changed = true;
9130
9406
  }
9131
- if (changed) await writeFile(path, body);
9407
+ if (changed) await writeFile2(path, body);
9132
9408
  return changed;
9133
9409
  }
9134
9410
 
9135
9411
  // cli/commands/history.ts
9136
- import { Command as Command10, Option as Option10 } from "clipanion";
9412
+ import { Command as Command11, Option as Option11 } from "clipanion";
9137
9413
 
9138
9414
  // cli/i18n/option-validators.texts.ts
9139
9415
  var OPTION_VALIDATORS_TEXTS = {
@@ -9222,7 +9498,7 @@ function parseStatuses(input, stderr) {
9222
9498
  }
9223
9499
  var HistoryCommand = class extends SmCommand {
9224
9500
  static paths = [["history"]];
9225
- static usage = Command10.Usage({
9501
+ static usage = Command11.Usage({
9226
9502
  category: "History",
9227
9503
  description: "Filter execution records. --json emits an array conforming to execution-record.schema.json.",
9228
9504
  details: `
@@ -9242,12 +9518,12 @@ var HistoryCommand = class extends SmCommand {
9242
9518
  ["Machine-readable, scoped to one node", "$0 history -n skills/foo.md --json"]
9243
9519
  ]
9244
9520
  });
9245
- node = Option10.String("-n", { required: false });
9246
- action = Option10.String("--action", { required: false });
9247
- status = Option10.String("--status", { required: false });
9248
- since = Option10.String("--since", { required: false });
9249
- until = Option10.String("--until", { required: false });
9250
- limit = Option10.String("--limit", { required: false });
9521
+ node = Option11.String("-n", { required: false });
9522
+ action = Option11.String("--action", { required: false });
9523
+ status = Option11.String("--status", { required: false });
9524
+ since = Option11.String("--since", { required: false });
9525
+ until = Option11.String("--until", { required: false });
9526
+ limit = Option11.String("--limit", { required: false });
9251
9527
  // CLI list verb: many optional filter flags (`--node`, `--action`,
9252
9528
  // `--status`, `--since`, `--until`, `--limit`, `--json`, `--quiet`)
9253
9529
  // each adding a guarded mutation to the filter or render path. Each
@@ -9295,7 +9571,7 @@ var HistoryCommand = class extends SmCommand {
9295
9571
  };
9296
9572
  var HistoryStatsCommand = class extends SmCommand {
9297
9573
  static paths = [["history", "stats"]];
9298
- static usage = Command10.Usage({
9574
+ static usage = Command11.Usage({
9299
9575
  category: "History",
9300
9576
  description: "Aggregate counts, tokens, periods, top nodes, and error rates over state_executions. --json conforms to history-stats.schema.json.",
9301
9577
  details: `
@@ -9313,10 +9589,10 @@ var HistoryStatsCommand = class extends SmCommand {
9313
9589
  ["Top 5 nodes, JSON", "$0 history stats --top 5 --json"]
9314
9590
  ]
9315
9591
  });
9316
- since = Option10.String("--since", { required: false });
9317
- until = Option10.String("--until", { required: false });
9318
- period = Option10.String("--period", { required: false });
9319
- top = Option10.String("--top", { required: false });
9592
+ since = Option11.String("--since", { required: false });
9593
+ until = Option11.String("--until", { required: false });
9594
+ period = Option11.String("--period", { required: false });
9595
+ top = Option11.String("--top", { required: false });
9320
9596
  // CLI stats verb: range parsing + window flags + period flag + JSON
9321
9597
  // branch + per-period iteration. Each branch is a single-purpose
9322
9598
  // gate; the data work lives in `aggregateHistoryStats`.
@@ -9497,11 +9773,11 @@ function formatRow(...cols) {
9497
9773
 
9498
9774
  // cli/commands/jobs.ts
9499
9775
  import { unlink } from "fs/promises";
9500
- import { Command as Command11, Option as Option11 } from "clipanion";
9776
+ import { Command as Command12, Option as Option12 } from "clipanion";
9501
9777
 
9502
9778
  // kernel/jobs/orphan-files.ts
9503
9779
  import { readdirSync as readdirSync6, statSync as statSync4 } from "fs";
9504
- import { join as join11, resolve as resolve15 } from "path";
9780
+ import { join as join12, resolve as resolve16 } from "path";
9505
9781
  function findOrphanJobFiles(jobsDir, referencedPaths) {
9506
9782
  let entries;
9507
9783
  try {
@@ -9516,7 +9792,7 @@ function findOrphanJobFiles(jobsDir, referencedPaths) {
9516
9792
  const orphans = [];
9517
9793
  for (const name of entries) {
9518
9794
  if (!name.endsWith(".md")) continue;
9519
- const abs = resolve15(join11(jobsDir, name));
9795
+ const abs = resolve16(join12(jobsDir, name));
9520
9796
  if (!referencedPaths.has(abs)) orphans.push(abs);
9521
9797
  }
9522
9798
  orphans.sort();
@@ -9545,7 +9821,7 @@ var JOBS_TEXTS = {
9545
9821
  // cli/commands/jobs.ts
9546
9822
  var JobPruneCommand = class extends SmCommand {
9547
9823
  static paths = [["job", "prune"]];
9548
- static usage = Command11.Usage({
9824
+ static usage = Command12.Usage({
9549
9825
  category: "Jobs",
9550
9826
  description: "Retention GC for completed / failed jobs (per config policy). --orphan-files removes MD files with no DB row.",
9551
9827
  details: `
@@ -9572,10 +9848,10 @@ var JobPruneCommand = class extends SmCommand {
9572
9848
  ["Preview without touching the DB", "$0 job prune --dry-run --json"]
9573
9849
  ]
9574
9850
  });
9575
- orphanFiles = Option11.Boolean("--orphan-files", false, {
9851
+ orphanFiles = Option12.Boolean("--orphan-files", false, {
9576
9852
  description: "Also remove MD files in .skill-map/jobs/ that have no matching state_jobs row."
9577
9853
  });
9578
- dryRun = Option11.Boolean("-n,--dry-run", false, {
9854
+ dryRun = Option12.Boolean("-n,--dry-run", false, {
9579
9855
  description: "Report what would be pruned without touching the DB or filesystem."
9580
9856
  });
9581
9857
  async run() {
@@ -9692,7 +9968,7 @@ function formatPolicy(seconds) {
9692
9968
  }
9693
9969
 
9694
9970
  // cli/commands/list.ts
9695
- import { Command as Command12, Option as Option12 } from "clipanion";
9971
+ import { Command as Command13, Option as Option13 } from "clipanion";
9696
9972
 
9697
9973
  // cli/i18n/list.texts.ts
9698
9974
  var LIST_TEXTS = {
@@ -9720,7 +9996,7 @@ var SORT_BY = {
9720
9996
  var PATH_COL_WIDTH = 50;
9721
9997
  var ListCommand = class extends SmCommand {
9722
9998
  static paths = [["list"]];
9723
- static usage = Command12.Usage({
9999
+ static usage = Command13.Usage({
9724
10000
  category: "Browse",
9725
10001
  description: "Tabular listing of nodes. --json emits an array conforming to node.schema.json.",
9726
10002
  details: `
@@ -9741,10 +10017,10 @@ var ListCommand = class extends SmCommand {
9741
10017
  ["Only nodes with issues, machine-readable", "$0 list --issue --json"]
9742
10018
  ]
9743
10019
  });
9744
- kind = Option12.String("--kind", { required: false });
9745
- issue = Option12.Boolean("--issue", false);
9746
- sortBy = Option12.String("--sort-by", { required: false });
9747
- limit = Option12.String("--limit", { required: false });
10020
+ kind = Option13.String("--kind", { required: false });
10021
+ issue = Option13.Boolean("--issue", false);
10022
+ sortBy = Option13.String("--sort-by", { required: false });
10023
+ limit = Option13.String("--limit", { required: false });
9748
10024
  async run() {
9749
10025
  let sortColumn = "path";
9750
10026
  let sortDirection = "asc";
@@ -9846,7 +10122,7 @@ function formatRow2(path, kind, out, inCount, ext, issues, bytes) {
9846
10122
  }
9847
10123
 
9848
10124
  // cli/commands/orphans.ts
9849
- import { Command as Command13, Option as Option13 } from "clipanion";
10125
+ import { Command as Command14, Option as Option14 } from "clipanion";
9850
10126
 
9851
10127
  // cli/i18n/orphans.texts.ts
9852
10128
  var ORPHANS_TEXTS = {
@@ -9901,7 +10177,7 @@ function isStringArray(v) {
9901
10177
  }
9902
10178
  var OrphansCommand = class extends SmCommand {
9903
10179
  static paths = [["orphans"]];
9904
- static usage = Command13.Usage({
10180
+ static usage = Command14.Usage({
9905
10181
  category: "Browse",
9906
10182
  description: "List orphan / auto-rename issues from the last scan. --json emits an array conforming to issue.schema.json.",
9907
10183
  details: `
@@ -9916,7 +10192,7 @@ var OrphansCommand = class extends SmCommand {
9916
10192
  ["Just the ambiguous ones, JSON", "$0 orphans --kind ambiguous --json"]
9917
10193
  ]
9918
10194
  });
9919
- kind = Option13.String("--kind", { required: false });
10195
+ kind = Option14.String("--kind", { required: false });
9920
10196
  async run() {
9921
10197
  let ruleFilter = null;
9922
10198
  if (this.kind !== void 0) {
@@ -9954,7 +10230,7 @@ var OrphansCommand = class extends SmCommand {
9954
10230
  };
9955
10231
  var OrphansReconcileCommand = class extends SmCommand {
9956
10232
  static paths = [["orphans", "reconcile"]];
9957
- static usage = Command13.Usage({
10233
+ static usage = Command14.Usage({
9958
10234
  category: "Browse",
9959
10235
  description: "Migrate state_* FKs from an orphan path to a live node, resolving the orphan issue.",
9960
10236
  details: `
@@ -9970,9 +10246,9 @@ var OrphansReconcileCommand = class extends SmCommand {
9970
10246
  ["Reattach orphan history", "$0 orphans reconcile skills/old.md --to skills/new.md"]
9971
10247
  ]
9972
10248
  });
9973
- orphanPath = Option13.String({ required: true });
9974
- to = Option13.String("--to", { required: true });
9975
- dryRun = Option13.Boolean("-n,--dry-run", false);
10249
+ orphanPath = Option14.String({ required: true });
10250
+ to = Option14.String("--to", { required: true });
10251
+ dryRun = Option14.Boolean("-n,--dry-run", false);
9976
10252
  async run() {
9977
10253
  const dbPath = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });
9978
10254
  if (!assertDbExists(dbPath, this.context.stderr)) return ExitCode.NotFound;
@@ -10042,7 +10318,7 @@ var OrphansReconcileCommand = class extends SmCommand {
10042
10318
  };
10043
10319
  var OrphansUndoRenameCommand = class extends SmCommand {
10044
10320
  static paths = [["orphans", "undo-rename"]];
10045
- static usage = Command13.Usage({
10321
+ static usage = Command14.Usage({
10046
10322
  category: "Browse",
10047
10323
  description: "Reverse a medium- or ambiguous-confidence auto-rename. Migrates state_* FKs back, emits a new orphan on the prior path.",
10048
10324
  details: `
@@ -10062,10 +10338,10 @@ var OrphansUndoRenameCommand = class extends SmCommand {
10062
10338
  ["Undo an ambiguous, picking a candidate", "$0 orphans undo-rename skills/new.md --from skills/old-a.md"]
10063
10339
  ]
10064
10340
  });
10065
- newPath = Option13.String({ required: true });
10066
- from = Option13.String("--from", { required: false });
10067
- force = Option13.Boolean("--force", false);
10068
- dryRun = Option13.Boolean("-n,--dry-run", false);
10341
+ newPath = Option14.String({ required: true });
10342
+ from = Option14.String("--from", { required: false });
10343
+ force = Option14.Boolean("--force", false);
10344
+ dryRun = Option14.Boolean("-n,--dry-run", false);
10069
10345
  async run() {
10070
10346
  const dbPath = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });
10071
10347
  if (!assertDbExists(dbPath, this.context.stderr)) return ExitCode.NotFound;
@@ -10229,9 +10505,9 @@ var ORPHANS_COMMANDS = [
10229
10505
  ];
10230
10506
 
10231
10507
  // cli/commands/plugins.ts
10232
- import { existsSync as existsSync13 } from "fs";
10233
- import { join as join12, resolve as resolve16 } from "path";
10234
- import { Command as Command14, Option as Option14 } from "clipanion";
10508
+ import { existsSync as existsSync14 } from "fs";
10509
+ import { join as join13, resolve as resolve17 } from "path";
10510
+ import { Command as Command15, Option as Option15 } from "clipanion";
10235
10511
 
10236
10512
  // cli/i18n/plugins.texts.ts
10237
10513
  var PLUGINS_TEXTS = {
@@ -10307,7 +10583,7 @@ var PLUGINS_TEXTS = {
10307
10583
 
10308
10584
  // cli/commands/plugins.ts
10309
10585
  function resolveSearchPaths2(opts, cwd, homedir2) {
10310
- if (opts.pluginDir) return [resolve16(opts.pluginDir)];
10586
+ if (opts.pluginDir) return [resolve17(opts.pluginDir)];
10311
10587
  const ctx = { cwd, homedir: homedir2 };
10312
10588
  const project = defaultProjectPluginsDir(ctx);
10313
10589
  const user = defaultUserPluginsDir(ctx);
@@ -10376,12 +10652,12 @@ function builtInRows(resolveEnabled) {
10376
10652
  }
10377
10653
  var PluginsListCommand = class extends SmCommand {
10378
10654
  static paths = [["plugins", "list"]];
10379
- static usage = Command14.Usage({
10655
+ static usage = Command15.Usage({
10380
10656
  category: "Plugins",
10381
10657
  description: "List discovered plugins and their load status.",
10382
10658
  details: "Scans <scope>/.skill-map/plugins and ~/.skill-map/plugins (or --plugin-dir <path>). Built-in bundles (claude, core) are listed alongside user plugins."
10383
10659
  });
10384
- pluginDir = Option14.String("--plugin-dir", { required: false });
10660
+ pluginDir = Option15.String("--plugin-dir", { required: false });
10385
10661
  async run() {
10386
10662
  const plugins = await loadAll({ global: this.global, pluginDir: this.pluginDir });
10387
10663
  const resolveEnabled = await buildResolver(this.global);
@@ -10443,12 +10719,12 @@ function renderPluginRow(p) {
10443
10719
  }
10444
10720
  var PluginsShowCommand = class extends SmCommand {
10445
10721
  static paths = [["plugins", "show"]];
10446
- static usage = Command14.Usage({
10722
+ static usage = Command15.Usage({
10447
10723
  category: "Plugins",
10448
10724
  description: "Show a single plugin's manifest + loaded extensions."
10449
10725
  });
10450
- id = Option14.String({ required: true });
10451
- pluginDir = Option14.String("--plugin-dir", { required: false });
10726
+ id = Option15.String({ required: true });
10727
+ pluginDir = Option15.String("--plugin-dir", { required: false });
10452
10728
  async run() {
10453
10729
  const plugins = await loadAll({ global: this.global, pluginDir: this.pluginDir });
10454
10730
  const resolveEnabled = await buildResolver(this.global);
@@ -10625,7 +10901,7 @@ function appendUnknownKindWarnings(out, extractorQualifiedId, applicableKinds, k
10625
10901
  }
10626
10902
  function expandHome(p, homedir2) {
10627
10903
  if (p === "~") return homedir2;
10628
- if (p.startsWith("~/")) return join12(homedir2, p.slice(2));
10904
+ if (p.startsWith("~/")) return join13(homedir2, p.slice(2));
10629
10905
  return p;
10630
10906
  }
10631
10907
  function collectExplorationDirWarnings(plugins, homedir2) {
@@ -10634,7 +10910,7 @@ function collectExplorationDirWarnings(plugins, homedir2) {
10634
10910
  const dir = instance["explorationDir"];
10635
10911
  if (typeof dir !== "string" || dir.length === 0) return;
10636
10912
  const resolved = expandHome(dir, homedir2);
10637
- if (!existsSync13(resolved)) {
10913
+ if (!existsSync14(resolved)) {
10638
10914
  out.push({
10639
10915
  providerQualifiedId: qualifiedExtensionId(pluginId, id),
10640
10916
  explorationDir: dir,
@@ -10646,12 +10922,12 @@ function collectExplorationDirWarnings(plugins, homedir2) {
10646
10922
  }
10647
10923
  var PluginsDoctorCommand = class extends SmCommand {
10648
10924
  static paths = [["plugins", "doctor"]];
10649
- static usage = Command14.Usage({
10925
+ static usage = Command15.Usage({
10650
10926
  category: "Plugins",
10651
10927
  description: "Run the full load pass and summarise by failure mode.",
10652
10928
  details: "Exit code 0 when every plugin loads or is intentionally disabled; 1 when any plugin is in an error / incompat state."
10653
10929
  });
10654
- pluginDir = Option14.String("--plugin-dir", { required: false });
10930
+ pluginDir = Option15.String("--plugin-dir", { required: false });
10655
10931
  // Doctor verb: counts by status + applicableKinds warnings +
10656
10932
  // explorationDir warnings + bad-plugins issues, each with its own
10657
10933
  // gated render. Branching is intrinsic to the multi-section diagnostic
@@ -10817,8 +11093,8 @@ function resolveToggleTarget(id, catalogue, verb) {
10817
11093
  return { key: bundle.id };
10818
11094
  }
10819
11095
  var TogglePluginsBase = class extends SmCommand {
10820
- all = Option14.Boolean("--all", false);
10821
- id = Option14.String({ required: false });
11096
+ all = Option15.Boolean("--all", false);
11097
+ id = Option15.String({ required: false });
10822
11098
  // eslint-disable-next-line complexity
10823
11099
  async toggle(enabled) {
10824
11100
  const verb = enabled ? "enable" : "disable";
@@ -10869,7 +11145,7 @@ var TogglePluginsBase = class extends SmCommand {
10869
11145
  };
10870
11146
  var PluginsEnableCommand = class extends TogglePluginsBase {
10871
11147
  static paths = [["plugins", "enable"]];
10872
- static usage = Command14.Usage({
11148
+ static usage = Command15.Usage({
10873
11149
  category: "Plugins",
10874
11150
  description: "Enable a plugin (or --all). Persists in config_plugins.",
10875
11151
  details: `
@@ -10891,7 +11167,7 @@ var PluginsEnableCommand = class extends TogglePluginsBase {
10891
11167
  };
10892
11168
  var PluginsDisableCommand = class extends TogglePluginsBase {
10893
11169
  static paths = [["plugins", "disable"]];
10894
- static usage = Command14.Usage({
11170
+ static usage = Command15.Usage({
10895
11171
  category: "Plugins",
10896
11172
  description: "Disable a plugin (or --all). Persists in config_plugins; does not delete files.",
10897
11173
  details: `
@@ -10927,8 +11203,8 @@ var PLUGIN_COMMANDS = [
10927
11203
 
10928
11204
  // cli/commands/refresh.ts
10929
11205
  import { readFile as readFile3 } from "fs/promises";
10930
- import { resolve as resolve18 } from "path";
10931
- import { Command as Command15, Option as Option15 } from "clipanion";
11206
+ import { resolve as resolve19 } from "path";
11207
+ import { Command as Command16, Option as Option16 } from "clipanion";
10932
11208
 
10933
11209
  // cli/i18n/refresh.texts.ts
10934
11210
  var REFRESH_TEXTS = {
@@ -10957,12 +11233,12 @@ var REFRESH_TEXTS = {
10957
11233
  };
10958
11234
 
10959
11235
  // cli/util/path-guard.ts
10960
- import { isAbsolute as isAbsolute3, resolve as resolve17, sep as sep3 } from "path";
11236
+ import { isAbsolute as isAbsolute3, resolve as resolve18, sep as sep3 } from "path";
10961
11237
  function assertContained2(cwd, rel) {
10962
11238
  if (isAbsolute3(rel)) {
10963
11239
  throw new Error(`node path is absolute, refusing to read: ${rel}`);
10964
11240
  }
10965
- const abs = resolve17(cwd, rel);
11241
+ const abs = resolve18(cwd, rel);
10966
11242
  if (abs !== cwd && !abs.startsWith(cwd + sep3)) {
10967
11243
  throw new Error(`node path escapes repo root: ${rel}`);
10968
11244
  }
@@ -10971,7 +11247,7 @@ function assertContained2(cwd, rel) {
10971
11247
  // cli/commands/refresh.ts
10972
11248
  var RefreshCommand = class extends SmCommand {
10973
11249
  static paths = [["refresh"]];
10974
- static usage = Command15.Usage({
11250
+ static usage = Command16.Usage({
10975
11251
  category: "Scan",
10976
11252
  description: "Refresh enrichment rows: granular (single node) or batch (every stale row).",
10977
11253
  details: `
@@ -10996,11 +11272,11 @@ var RefreshCommand = class extends SmCommand {
10996
11272
  ["Refresh every node with stale enrichments", "$0 refresh --stale"]
10997
11273
  ]
10998
11274
  });
10999
- nodePath = Option15.String({ name: "node", required: false });
11000
- stale = Option15.Boolean("--stale", false, {
11275
+ nodePath = Option16.String({ name: "node", required: false });
11276
+ stale = Option16.Boolean("--stale", false, {
11001
11277
  description: "Refresh every node whose probabilistic enrichment row is flagged stale=1."
11002
11278
  });
11003
- noPlugins = Option15.Boolean("--no-plugins", false, {
11279
+ noPlugins = Option16.Boolean("--no-plugins", false, {
11004
11280
  description: "Skip drop-in plugin discovery; use only the built-in extractor set."
11005
11281
  });
11006
11282
  // The remaining cyclomatic count comes from CLI ergonomics that don't
@@ -11136,7 +11412,7 @@ var RefreshCommand = class extends SmCommand {
11136
11412
  let body;
11137
11413
  try {
11138
11414
  assertContained2(cwd, node.path);
11139
- const raw = await readFile3(resolve18(cwd, node.path), "utf8");
11415
+ const raw = await readFile3(resolve19(cwd, node.path), "utf8");
11140
11416
  body = stripFrontmatterFence(raw);
11141
11417
  } catch (err) {
11142
11418
  this.context.stderr.write(
@@ -11185,7 +11461,7 @@ function stripFrontmatterFence(text) {
11185
11461
  var REFRESH_COMMANDS = [RefreshCommand];
11186
11462
 
11187
11463
  // cli/commands/scan.ts
11188
- import { Command as Command17, Option as Option17 } from "clipanion";
11464
+ import { Command as Command18, Option as Option18 } from "clipanion";
11189
11465
 
11190
11466
  // cli/i18n/scan.texts.ts
11191
11467
  var SCAN_TEXTS = {
@@ -11377,7 +11653,7 @@ async function runEphemeralPath(opts, dbPath, strict, loadPrior, runScanWith) {
11377
11653
  }
11378
11654
 
11379
11655
  // cli/commands/watch.ts
11380
- import { Command as Command16, Option as Option16 } from "clipanion";
11656
+ import { Command as Command17, Option as Option17 } from "clipanion";
11381
11657
 
11382
11658
  // cli/i18n/watch.texts.ts
11383
11659
  var WATCH_TEXTS = {
@@ -11556,7 +11832,7 @@ async function runWatchLoop(opts) {
11556
11832
  }
11557
11833
  var WatchCommand = class extends SmCommand {
11558
11834
  static paths = [["watch"]];
11559
- static usage = Command16.Usage({
11835
+ static usage = Command17.Usage({
11560
11836
  category: "Scan",
11561
11837
  description: "Watch roots and run an incremental scan after each debounced batch of filesystem events.",
11562
11838
  details: `
@@ -11580,17 +11856,17 @@ var WatchCommand = class extends SmCommand {
11580
11856
  ["Stream ScanResult per batch as ndjson", "$0 watch --json"]
11581
11857
  ]
11582
11858
  });
11583
- roots = Option16.Rest({ name: "roots" });
11584
- noTokens = Option16.Boolean("--no-tokens", false, {
11859
+ roots = Option17.Rest({ name: "roots" });
11860
+ noTokens = Option17.Boolean("--no-tokens", false, {
11585
11861
  description: "Skip per-node token counts (cl100k_base BPE)."
11586
11862
  });
11587
- strict = Option16.Boolean("--strict", false, {
11863
+ strict = Option17.Boolean("--strict", false, {
11588
11864
  description: "Promote frontmatter-validation findings from warn to error inside each batch. Does not change the watcher exit code."
11589
11865
  });
11590
- noPlugins = Option16.Boolean("--no-plugins", false, {
11866
+ noPlugins = Option17.Boolean("--no-plugins", false, {
11591
11867
  description: "Skip drop-in plugin discovery for the watcher session."
11592
11868
  });
11593
- maxConsecutiveFailures = Option16.String("--max-consecutive-failures", {
11869
+ maxConsecutiveFailures = Option17.String("--max-consecutive-failures", {
11594
11870
  required: false,
11595
11871
  description: "Shut down with exit 2 after N consecutive batch failures (default 5; 0 disables the breaker)."
11596
11872
  });
@@ -11628,7 +11904,7 @@ function parseBreakerLimit(raw, stderr) {
11628
11904
  // cli/commands/scan.ts
11629
11905
  var ScanCommand = class extends SmCommand {
11630
11906
  static paths = [["scan"]];
11631
- static usage = Command17.Usage({
11907
+ static usage = Command18.Usage({
11632
11908
  category: "Scan",
11633
11909
  description: "Scan roots for markdown nodes, run extractors and rules.",
11634
11910
  details: `
@@ -11657,29 +11933,29 @@ var ScanCommand = class extends SmCommand {
11657
11933
  ["What would the next incremental scan persist?", "$0 scan --changed -n --json"]
11658
11934
  ]
11659
11935
  });
11660
- roots = Option17.Rest({ name: "roots" });
11661
- noBuiltIns = Option17.Boolean("--no-built-ins", false, {
11936
+ roots = Option18.Rest({ name: "roots" });
11937
+ noBuiltIns = Option18.Boolean("--no-built-ins", false, {
11662
11938
  description: "Skip the built-in extension set. Yields a zero-filled ScanResult (kernel-empty-boot parity); skips DB persistence."
11663
11939
  });
11664
- noPlugins = Option17.Boolean("--no-plugins", false, {
11940
+ noPlugins = Option18.Boolean("--no-plugins", false, {
11665
11941
  description: "Skip drop-in plugin discovery. Only the built-in set runs. Combine with --no-built-ins for a fully empty pipeline."
11666
11942
  });
11667
- noTokens = Option17.Boolean("--no-tokens", false, {
11943
+ noTokens = Option18.Boolean("--no-tokens", false, {
11668
11944
  description: "Skip per-node token counts (cl100k_base BPE). Leaves node.tokens undefined; spec-valid since the field is optional."
11669
11945
  });
11670
- dryRun = Option17.Boolean("-n,--dry-run", false, {
11946
+ dryRun = Option18.Boolean("-n,--dry-run", false, {
11671
11947
  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."
11672
11948
  });
11673
- changed = Option17.Boolean("--changed", false, {
11949
+ changed = Option18.Boolean("--changed", false, {
11674
11950
  description: "Incremental scan: reuse unchanged nodes from the persisted prior snapshot. Degrades to a full scan if no prior snapshot exists."
11675
11951
  });
11676
- allowEmpty = Option17.Boolean("--allow-empty", false, {
11952
+ allowEmpty = Option18.Boolean("--allow-empty", false, {
11677
11953
  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."
11678
11954
  });
11679
- strict = Option17.Boolean("--strict", false, {
11955
+ strict = Option18.Boolean("--strict", false, {
11680
11956
  description: "Promote frontmatter-validation findings from warn to error (exit code 1 on any violation). Overrides scan.strict from config when both are set."
11681
11957
  });
11682
- watch = Option17.Boolean("--watch", false, {
11958
+ watch = Option18.Boolean("--watch", false, {
11683
11959
  description: "Long-running mode: watch the roots and trigger an incremental scan after each debounced batch of filesystem events. Alias of `sm watch`."
11684
11960
  });
11685
11961
  async run() {
@@ -11777,11 +12053,11 @@ var ScanCommand = class extends SmCommand {
11777
12053
  };
11778
12054
 
11779
12055
  // cli/commands/scan-compare.ts
11780
- import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
11781
- import { Command as Command18, Option as Option18 } from "clipanion";
12056
+ import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
12057
+ import { Command as Command19, Option as Option19 } from "clipanion";
11782
12058
  var ScanCompareCommand = class extends SmCommand {
11783
12059
  static paths = [["scan", "compare-with"]];
11784
- static usage = Command18.Usage({
12060
+ static usage = Command19.Usage({
11785
12061
  category: "Scan",
11786
12062
  description: "Run a fresh scan in memory and emit a delta against the saved ScanResult dump at <dump>. Read-only.",
11787
12063
  details: `
@@ -11809,15 +12085,15 @@ var ScanCompareCommand = class extends SmCommand {
11809
12085
  ["JSON output for tooling", "$0 scan compare-with baseline.json --json"]
11810
12086
  ]
11811
12087
  });
11812
- dump = Option18.String({ required: true });
11813
- roots = Option18.Rest({ name: "roots" });
11814
- noTokens = Option18.Boolean("--no-tokens", false, {
12088
+ dump = Option19.String({ required: true });
12089
+ roots = Option19.Rest({ name: "roots" });
12090
+ noTokens = Option19.Boolean("--no-tokens", false, {
11815
12091
  description: "Skip per-node token counts during the fresh scan."
11816
12092
  });
11817
- strict = Option18.Boolean("--strict", false, {
12093
+ strict = Option19.Boolean("--strict", false, {
11818
12094
  description: "Promote layered-config warnings and frontmatter-validation findings from warn to error."
11819
12095
  });
11820
- noPlugins = Option18.Boolean("--no-plugins", false, {
12096
+ noPlugins = Option19.Boolean("--no-plugins", false, {
11821
12097
  description: "Skip drop-in plugin discovery."
11822
12098
  });
11823
12099
  // Cyclomatic count comes from CLI ergonomics: 3 distinct try/catch
@@ -11888,12 +12164,12 @@ var ScanCompareCommand = class extends SmCommand {
11888
12164
  }
11889
12165
  };
11890
12166
  function loadAndValidateDump(path) {
11891
- if (!existsSync14(path)) {
12167
+ if (!existsSync15(path)) {
11892
12168
  throw new Error(tx(SCAN_TEXTS.compareDumpNotFound, { path }));
11893
12169
  }
11894
12170
  let raw;
11895
12171
  try {
11896
- raw = readFileSync11(path, "utf8");
12172
+ raw = readFileSync12(path, "utf8");
11897
12173
  } catch (err) {
11898
12174
  const message = formatErrorMessage(err);
11899
12175
  throw new Error(tx(SCAN_TEXTS.compareDumpReadFailed, { path, message }), { cause: err });
@@ -12002,8 +12278,8 @@ function renderDeltaIssues(issues) {
12002
12278
 
12003
12279
  // cli/commands/serve.ts
12004
12280
  import { spawn } from "child_process";
12005
- import { existsSync as existsSync18 } from "fs";
12006
- import { Command as Command19, Option as Option19 } from "clipanion";
12281
+ import { existsSync as existsSync19 } from "fs";
12282
+ import { Command as Command20, Option as Option20 } from "clipanion";
12007
12283
 
12008
12284
  // server/index.ts
12009
12285
  import { serve } from "@hono/node-server";
@@ -12184,7 +12460,7 @@ function contentTypeFor(format) {
12184
12460
  }
12185
12461
 
12186
12462
  // server/health.ts
12187
- import { existsSync as existsSync15 } from "fs";
12463
+ import { existsSync as existsSync16 } from "fs";
12188
12464
  var FALLBACK_SCHEMA_VERSION = "1";
12189
12465
  function buildHealth(deps) {
12190
12466
  return {
@@ -12193,7 +12469,7 @@ function buildHealth(deps) {
12193
12469
  specVersion: deps.specVersion,
12194
12470
  implVersion: VERSION,
12195
12471
  scope: deps.scope,
12196
- db: existsSync15(deps.dbPath) ? "present" : "missing"
12472
+ db: existsSync16(deps.dbPath) ? "present" : "missing"
12197
12473
  };
12198
12474
  }
12199
12475
  async function resolveSpecVersion2() {
@@ -12633,9 +12909,9 @@ function emptyScanResult() {
12633
12909
  }
12634
12910
 
12635
12911
  // server/static.ts
12636
- import { existsSync as existsSync16 } from "fs";
12912
+ import { existsSync as existsSync17 } from "fs";
12637
12913
  import { readFile as readFile5 } from "fs/promises";
12638
- import { extname, join as join13 } from "path";
12914
+ import { extname, join as join14 } from "path";
12639
12915
  import { serveStatic } from "@hono/node-server/serve-static";
12640
12916
  var INDEX_HTML = "index.html";
12641
12917
  var PLACEHOLDER_HTML = `<!doctype html>
@@ -12666,8 +12942,8 @@ function createSpaFallback(uiDist) {
12666
12942
  return async (c, _next) => {
12667
12943
  if (c.req.method !== "GET" && c.req.method !== "HEAD") return c.notFound();
12668
12944
  if (uiDist === null) return htmlResponse(c, PLACEHOLDER_HTML);
12669
- const indexPath = join13(uiDist, INDEX_HTML);
12670
- if (!existsSync16(indexPath)) return htmlResponse(c, PLACEHOLDER_HTML);
12945
+ const indexPath = join14(uiDist, INDEX_HTML);
12946
+ if (!existsSync17(indexPath)) return htmlResponse(c, PLACEHOLDER_HTML);
12671
12947
  return fileResponse(c, indexPath);
12672
12948
  };
12673
12949
  }
@@ -13257,10 +13533,10 @@ function validateWatcherDebounce(value) {
13257
13533
  }
13258
13534
 
13259
13535
  // server/paths.ts
13260
- import { existsSync as existsSync17, statSync as statSync5 } from "fs";
13261
- import { dirname as dirname9, isAbsolute as isAbsolute5, join as join14, resolve as resolve19 } from "path";
13262
- import { fileURLToPath as fileURLToPath5 } from "url";
13263
- var DEFAULT_UI_REL = join14("ui", "dist", "ui", "browser");
13536
+ import { existsSync as existsSync18, statSync as statSync5 } from "fs";
13537
+ import { dirname as dirname10, isAbsolute as isAbsolute5, join as join15, resolve as resolve20 } from "path";
13538
+ import { fileURLToPath as fileURLToPath6 } from "url";
13539
+ var DEFAULT_UI_REL = join15("ui", "dist", "ui", "browser");
13264
13540
  var PACKAGE_UI_REL = "ui";
13265
13541
  var INDEX_HTML2 = "index.html";
13266
13542
  function resolveDefaultUiDist(ctx) {
@@ -13269,13 +13545,13 @@ function resolveDefaultUiDist(ctx) {
13269
13545
  return walkUpForUi(ctx.cwd);
13270
13546
  }
13271
13547
  function resolveExplicitUiDist(ctx, raw) {
13272
- return isAbsolute5(raw) ? raw : resolve19(ctx.cwd, raw);
13548
+ return isAbsolute5(raw) ? raw : resolve20(ctx.cwd, raw);
13273
13549
  }
13274
13550
  function isUiBundleDir(path) {
13275
- if (!existsSync17(path)) return false;
13551
+ if (!existsSync18(path)) return false;
13276
13552
  try {
13277
13553
  if (!statSync5(path).isDirectory()) return false;
13278
- return existsSync17(join14(path, INDEX_HTML2));
13554
+ return existsSync18(join15(path, INDEX_HTML2));
13279
13555
  } catch {
13280
13556
  return false;
13281
13557
  }
@@ -13283,7 +13559,7 @@ function isUiBundleDir(path) {
13283
13559
  function resolvePackageBundledUi() {
13284
13560
  let here;
13285
13561
  try {
13286
- here = dirname9(fileURLToPath5(import.meta.url));
13562
+ here = dirname10(fileURLToPath6(import.meta.url));
13287
13563
  } catch {
13288
13564
  return null;
13289
13565
  }
@@ -13292,22 +13568,22 @@ function resolvePackageBundledUi() {
13292
13568
  function resolvePackageBundledUiFrom(here) {
13293
13569
  let current = here;
13294
13570
  for (let i = 0; i < 8; i++) {
13295
- const candidate = join14(current, PACKAGE_UI_REL);
13571
+ const candidate = join15(current, PACKAGE_UI_REL);
13296
13572
  if (isUiBundleDir(candidate)) return candidate;
13297
- const distHere = join14(current, "dist", PACKAGE_UI_REL);
13573
+ const distHere = join15(current, "dist", PACKAGE_UI_REL);
13298
13574
  if (isUiBundleDir(distHere)) return distHere;
13299
- const parent = dirname9(current);
13575
+ const parent = dirname10(current);
13300
13576
  if (parent === current) return null;
13301
13577
  current = parent;
13302
13578
  }
13303
13579
  return null;
13304
13580
  }
13305
13581
  function walkUpForUi(startDir) {
13306
- let current = resolve19(startDir);
13582
+ let current = resolve20(startDir);
13307
13583
  for (let i = 0; i < 64; i++) {
13308
- const candidate = join14(current, DEFAULT_UI_REL);
13584
+ const candidate = join15(current, DEFAULT_UI_REL);
13309
13585
  if (isUiBundleDir(candidate)) return candidate;
13310
- const parent = dirname9(current);
13586
+ const parent = dirname10(current);
13311
13587
  if (parent === current) return null;
13312
13588
  current = parent;
13313
13589
  }
@@ -13467,7 +13743,7 @@ var SERVE_TEXTS = {
13467
13743
  // cli/commands/serve.ts
13468
13744
  var ServeCommand = class extends SmCommand {
13469
13745
  static paths = [["serve"]];
13470
- static usage = Command19.Usage({
13746
+ static usage = Command20.Usage({
13471
13747
  category: "Setup",
13472
13748
  description: "Start the Hono BFF (single-port: REST + WebSocket + SPA bundle).",
13473
13749
  details: `
@@ -13492,22 +13768,22 @@ var ServeCommand = class extends SmCommand {
13492
13768
  ["Point at a pre-built UI bundle", "$0 serve --ui-dist ./ui/dist/browser"]
13493
13769
  ]
13494
13770
  });
13495
- port = Option19.String("--port", {
13771
+ port = Option20.String("--port", {
13496
13772
  required: false,
13497
13773
  description: "Listening port (default 4242). 0 = OS-assigned."
13498
13774
  });
13499
- host = Option19.String("--host", {
13775
+ host = Option20.String("--host", {
13500
13776
  required: false,
13501
13777
  description: "Listening host (default 127.0.0.1). Loopback-only enforced when --dev-cors is set."
13502
13778
  });
13503
- scope = Option19.String("--scope", {
13779
+ scope = Option20.String("--scope", {
13504
13780
  required: false,
13505
13781
  description: "project | global. Alias for -g/--global. Default: project."
13506
13782
  });
13507
- noBuiltIns = Option19.Boolean("--no-built-ins", false, {
13783
+ noBuiltIns = Option20.Boolean("--no-built-ins", false, {
13508
13784
  description: "Skip built-in plugin registration (parity with sm scan --no-built-ins)."
13509
13785
  });
13510
- noPlugins = Option19.Boolean("--no-plugins", false, {
13786
+ noPlugins = Option20.Boolean("--no-plugins", false, {
13511
13787
  description: "Skip drop-in plugin discovery."
13512
13788
  });
13513
13789
  // `Option.Boolean('--open', true)` — Clipanion's parser auto-derives
@@ -13517,24 +13793,24 @@ var ServeCommand = class extends SmCommand {
13517
13793
  // two registrations for the same flag and rejects the invocation
13518
13794
  // with "Ambiguous Syntax Error". Same convention shipped by every
13519
13795
  // other `--no-...` flag in the CLI tree.
13520
- open = Option19.Boolean("--open", true, {
13796
+ open = Option20.Boolean("--open", true, {
13521
13797
  description: "Auto-open the SPA in the user's default browser after listen. --no-open opts out."
13522
13798
  });
13523
- devCors = Option19.Boolean("--dev-cors", false, {
13799
+ devCors = Option20.Boolean("--dev-cors", false, {
13524
13800
  description: "Enable permissive CORS for the Angular dev-server proxy workflow."
13525
13801
  });
13526
13802
  // `--ui-dist` is intentionally undocumented in the Usage block above
13527
13803
  // (the demo build pipeline + tests rely on it; everyday users never
13528
13804
  // need it). Clipanion still exposes it on the parser; the Usage
13529
13805
  // omission is the "hidden" contract per the 14.1 brief.
13530
- uiDist = Option19.String("--ui-dist", { required: false, hidden: true });
13531
- noWatcher = Option19.Boolean("--no-watcher", false, {
13806
+ uiDist = Option20.String("--ui-dist", { required: false, hidden: true });
13807
+ noWatcher = Option20.Boolean("--no-watcher", false, {
13532
13808
  description: "Disable the chokidar-fed scan-and-broadcast loop. Use only for CI / read-only deployments."
13533
13809
  });
13534
13810
  // `--watcher-debounce-ms` is undocumented sugar for advanced users
13535
13811
  // who want to tighten / relax the watcher's batching window without
13536
13812
  // editing settings.json. Hidden flag — the Usage block omits it.
13537
- watcherDebounceMs = Option19.String("--watcher-debounce-ms", { required: false, hidden: true });
13813
+ watcherDebounceMs = Option20.String("--watcher-debounce-ms", { required: false, hidden: true });
13538
13814
  // Long-running daemon — `done in <…>` after a graceful shutdown is
13539
13815
  // noise. Mirrors `sm watch`'s opt-out.
13540
13816
  emitElapsed = false;
@@ -13562,7 +13838,7 @@ var ServeCommand = class extends SmCommand {
13562
13838
  return ExitCode.Error;
13563
13839
  }
13564
13840
  const dbPath = resolveDbPath({ global: this.global, db: this.db, ...runtimeCtx });
13565
- if (this.db !== void 0 && !existsSync18(dbPath)) {
13841
+ if (this.db !== void 0 && !existsSync19(dbPath)) {
13566
13842
  this.context.stderr.write(
13567
13843
  tx(SERVE_TEXTS.dbNotFound, { path: sanitizeForTerminal(dbPath) })
13568
13844
  );
@@ -13743,7 +14019,7 @@ function tryOpenBrowser(url, stderr) {
13743
14019
  }
13744
14020
 
13745
14021
  // cli/commands/show.ts
13746
- import { Command as Command20, Option as Option20 } from "clipanion";
14022
+ import { Command as Command21, Option as Option21 } from "clipanion";
13747
14023
 
13748
14024
  // cli/i18n/show.texts.ts
13749
14025
  var SHOW_TEXTS = {
@@ -13783,7 +14059,7 @@ var SHOW_TEXTS = {
13783
14059
  // cli/commands/show.ts
13784
14060
  var ShowCommand = class extends SmCommand {
13785
14061
  static paths = [["show"]];
13786
- static usage = Command20.Usage({
14062
+ static usage = Command21.Usage({
13787
14063
  category: "Browse",
13788
14064
  description: "Node detail: weight, frontmatter, links, issues.",
13789
14065
  details: `
@@ -13799,7 +14075,7 @@ var ShowCommand = class extends SmCommand {
13799
14075
  ["Machine-readable detail", "$0 show .claude/agents/architect.md --json"]
13800
14076
  ]
13801
14077
  });
13802
- nodePath = Option20.String({ required: true });
14078
+ nodePath = Option21.String({ required: true });
13803
14079
  async run() {
13804
14080
  const dbPath = resolveDbPath({ global: this.global, db: this.db, ...defaultRuntimeContext() });
13805
14081
  if (!assertDbExists(dbPath, this.context.stderr)) return ExitCode.NotFound;
@@ -13948,7 +14224,7 @@ function rankConfidenceForGrouping(c) {
13948
14224
  }
13949
14225
 
13950
14226
  // cli/commands/stubs.ts
13951
- import { Command as Command21, Option as Option21 } from "clipanion";
14227
+ import { Command as Command22, Option as Option22 } from "clipanion";
13952
14228
 
13953
14229
  // cli/i18n/stubs.texts.ts
13954
14230
  var STUBS_TEXTS = {
@@ -13963,9 +14239,9 @@ function notImplemented(cmd, verb) {
13963
14239
  cmd.context.stderr.write(tx(STUBS_TEXTS.notImplemented, { verb }));
13964
14240
  return ExitCode.Error;
13965
14241
  }
13966
- var DoctorCommand = class extends Command21 {
14242
+ var DoctorCommand = class extends Command22 {
13967
14243
  static paths = [["doctor"]];
13968
- static usage = Command21.Usage({
14244
+ static usage = Command22.Usage({
13969
14245
  category: "Setup",
13970
14246
  description: planned("Diagnostic report: DB integrity, pending migrations, orphan rows, plugin status, runner availability.")
13971
14247
  });
@@ -13973,23 +14249,23 @@ var DoctorCommand = class extends Command21 {
13973
14249
  return notImplemented(this, "doctor");
13974
14250
  }
13975
14251
  };
13976
- var FindingsCommand = class extends Command21 {
14252
+ var FindingsCommand = class extends Command22 {
13977
14253
  static paths = [["findings"]];
13978
- static usage = Command21.Usage({
14254
+ static usage = Command22.Usage({
13979
14255
  category: "Browse",
13980
14256
  description: planned("Probabilistic findings: injection, stale summaries, low confidence.")
13981
14257
  });
13982
- kind = Option21.String("--kind", { required: false });
13983
- since = Option21.String("--since", { required: false });
13984
- threshold = Option21.String("--threshold", { required: false });
13985
- json = Option21.Boolean("--json", false);
14258
+ kind = Option22.String("--kind", { required: false });
14259
+ since = Option22.String("--since", { required: false });
14260
+ threshold = Option22.String("--threshold", { required: false });
14261
+ json = Option22.Boolean("--json", false);
13986
14262
  async execute() {
13987
14263
  return notImplemented(this, "findings");
13988
14264
  }
13989
14265
  };
13990
- var ActionsListCommand = class extends Command21 {
14266
+ var ActionsListCommand = class extends Command22 {
13991
14267
  static paths = [["actions", "list"]];
13992
- static usage = Command21.Usage({
14268
+ static usage = Command22.Usage({
13993
14269
  category: "Jobs",
13994
14270
  description: planned("Registered action types (manifest view).")
13995
14271
  });
@@ -13997,121 +14273,121 @@ var ActionsListCommand = class extends Command21 {
13997
14273
  return notImplemented(this, "actions list");
13998
14274
  }
13999
14275
  };
14000
- var ActionsShowCommand = class extends Command21 {
14276
+ var ActionsShowCommand = class extends Command22 {
14001
14277
  static paths = [["actions", "show"]];
14002
- static usage = Command21.Usage({
14278
+ static usage = Command22.Usage({
14003
14279
  category: "Jobs",
14004
14280
  description: planned("Full action manifest, including preconditions and expected duration.")
14005
14281
  });
14006
- id = Option21.String({ required: true });
14282
+ id = Option22.String({ required: true });
14007
14283
  async execute() {
14008
14284
  return notImplemented(this, "actions show");
14009
14285
  }
14010
14286
  };
14011
- var JobSubmitCommand = class extends Command21 {
14287
+ var JobSubmitCommand = class extends Command22 {
14012
14288
  static paths = [["job", "submit"]];
14013
- static usage = Command21.Usage({
14289
+ static usage = Command22.Usage({
14014
14290
  category: "Jobs",
14015
14291
  description: planned("Enqueue a single job or fan out to every matching node (--all).")
14016
14292
  });
14017
- action = Option21.String({ required: true });
14018
- node = Option21.String("-n", { required: false });
14019
- all = Option21.Boolean("--all", false);
14020
- run = Option21.Boolean("--run", false);
14021
- force = Option21.Boolean("--force", false);
14022
- ttl = Option21.String("--ttl", { required: false });
14023
- priority = Option21.String("--priority", { required: false });
14293
+ action = Option22.String({ required: true });
14294
+ node = Option22.String("-n", { required: false });
14295
+ all = Option22.Boolean("--all", false);
14296
+ run = Option22.Boolean("--run", false);
14297
+ force = Option22.Boolean("--force", false);
14298
+ ttl = Option22.String("--ttl", { required: false });
14299
+ priority = Option22.String("--priority", { required: false });
14024
14300
  async execute() {
14025
14301
  return notImplemented(this, "job submit");
14026
14302
  }
14027
14303
  };
14028
- var JobListCommand = class extends Command21 {
14304
+ var JobListCommand = class extends Command22 {
14029
14305
  static paths = [["job", "list"]];
14030
- static usage = Command21.Usage({ category: "Jobs", description: planned("List jobs.") });
14031
- status = Option21.String("--status", { required: false });
14032
- action = Option21.String("--action", { required: false });
14033
- node = Option21.String("--node", { required: false });
14306
+ static usage = Command22.Usage({ category: "Jobs", description: planned("List jobs.") });
14307
+ status = Option22.String("--status", { required: false });
14308
+ action = Option22.String("--action", { required: false });
14309
+ node = Option22.String("--node", { required: false });
14034
14310
  async execute() {
14035
14311
  return notImplemented(this, "job list");
14036
14312
  }
14037
14313
  };
14038
- var JobShowCommand = class extends Command21 {
14314
+ var JobShowCommand = class extends Command22 {
14039
14315
  static paths = [["job", "show"]];
14040
- static usage = Command21.Usage({ category: "Jobs", description: planned("Job detail: state, claim time, TTL, runner, content hash.") });
14041
- id = Option21.String({ required: true });
14316
+ static usage = Command22.Usage({ category: "Jobs", description: planned("Job detail: state, claim time, TTL, runner, content hash.") });
14317
+ id = Option22.String({ required: true });
14042
14318
  async execute() {
14043
14319
  return notImplemented(this, "job show");
14044
14320
  }
14045
14321
  };
14046
- var JobPreviewCommand = class extends Command21 {
14322
+ var JobPreviewCommand = class extends Command22 {
14047
14323
  static paths = [["job", "preview"]];
14048
- static usage = Command21.Usage({ category: "Jobs", description: planned("Render the job MD file without executing.") });
14049
- id = Option21.String({ required: true });
14324
+ static usage = Command22.Usage({ category: "Jobs", description: planned("Render the job MD file without executing.") });
14325
+ id = Option22.String({ required: true });
14050
14326
  async execute() {
14051
14327
  return notImplemented(this, "job preview");
14052
14328
  }
14053
14329
  };
14054
- var JobClaimCommand = class extends Command21 {
14330
+ var JobClaimCommand = class extends Command22 {
14055
14331
  static paths = [["job", "claim"]];
14056
- static usage = Command21.Usage({
14332
+ static usage = Command22.Usage({
14057
14333
  category: "Jobs",
14058
14334
  description: planned("Atomic primitive: return next queued job id, mark it running.")
14059
14335
  });
14060
- filter = Option21.String("--filter", { required: false });
14336
+ filter = Option22.String("--filter", { required: false });
14061
14337
  async execute() {
14062
14338
  return notImplemented(this, "job claim");
14063
14339
  }
14064
14340
  };
14065
- var JobRunCommand = class extends Command21 {
14341
+ var JobRunCommand = class extends Command22 {
14066
14342
  static paths = [["job", "run"]];
14067
- static usage = Command21.Usage({
14343
+ static usage = Command22.Usage({
14068
14344
  category: "Jobs",
14069
14345
  description: planned("Full CLI-runner loop: claim + spawn + record.")
14070
14346
  });
14071
- all = Option21.Boolean("--all", false);
14072
- max = Option21.String("--max", { required: false });
14347
+ all = Option22.Boolean("--all", false);
14348
+ max = Option22.String("--max", { required: false });
14073
14349
  async execute() {
14074
14350
  return notImplemented(this, "job run");
14075
14351
  }
14076
14352
  };
14077
- var JobStatusCommand = class extends Command21 {
14353
+ var JobStatusCommand = class extends Command22 {
14078
14354
  static paths = [["job", "status"]];
14079
- static usage = Command21.Usage({
14355
+ static usage = Command22.Usage({
14080
14356
  category: "Jobs",
14081
14357
  description: planned("Counts (per status) or single-job status.")
14082
14358
  });
14083
- id = Option21.String({ required: false });
14359
+ id = Option22.String({ required: false });
14084
14360
  async execute() {
14085
14361
  return notImplemented(this, "job status");
14086
14362
  }
14087
14363
  };
14088
- var JobCancelCommand = class extends Command21 {
14364
+ var JobCancelCommand = class extends Command22 {
14089
14365
  static paths = [["job", "cancel"]];
14090
- static usage = Command21.Usage({
14366
+ static usage = Command22.Usage({
14091
14367
  category: "Jobs",
14092
14368
  description: planned("Force a running job to failed with reason user-cancelled.")
14093
14369
  });
14094
- id = Option21.String({ required: false });
14095
- all = Option21.Boolean("--all", false);
14370
+ id = Option22.String({ required: false });
14371
+ all = Option22.Boolean("--all", false);
14096
14372
  async execute() {
14097
14373
  return notImplemented(this, "job cancel");
14098
14374
  }
14099
14375
  };
14100
- var RecordCommand = class extends Command21 {
14376
+ var RecordCommand = class extends Command22 {
14101
14377
  static paths = [["record"]];
14102
- static usage = Command21.Usage({
14378
+ static usage = Command22.Usage({
14103
14379
  category: "Jobs",
14104
14380
  description: planned("Close a running job with success or failure. Nonce is the sole credential.")
14105
14381
  });
14106
- id = Option21.String("--id", { required: true });
14107
- nonce = Option21.String("--nonce", { required: true });
14108
- status = Option21.String("--status", { required: true });
14109
- report = Option21.String("--report", { required: false });
14110
- tokensIn = Option21.String("--tokens-in", { required: false });
14111
- tokensOut = Option21.String("--tokens-out", { required: false });
14112
- durationMs = Option21.String("--duration-ms", { required: false });
14113
- model = Option21.String("--model", { required: false });
14114
- error = Option21.String("--error", { required: false });
14382
+ id = Option22.String("--id", { required: true });
14383
+ nonce = Option22.String("--nonce", { required: true });
14384
+ status = Option22.String("--status", { required: true });
14385
+ report = Option22.String("--report", { required: false });
14386
+ tokensIn = Option22.String("--tokens-in", { required: false });
14387
+ tokensOut = Option22.String("--tokens-out", { required: false });
14388
+ durationMs = Option22.String("--duration-ms", { required: false });
14389
+ model = Option22.String("--model", { required: false });
14390
+ error = Option22.String("--error", { required: false });
14115
14391
  async execute() {
14116
14392
  return notImplemented(this, "record");
14117
14393
  }
@@ -14133,7 +14409,7 @@ var STUB_COMMANDS = [
14133
14409
  ];
14134
14410
 
14135
14411
  // cli/commands/version.ts
14136
- import { Command as Command22 } from "clipanion";
14412
+ import { Command as Command23 } from "clipanion";
14137
14413
 
14138
14414
  // cli/i18n/version.texts.ts
14139
14415
  var VERSION_TEXTS = {
@@ -14145,7 +14421,7 @@ var VERSION_TEXTS = {
14145
14421
  // cli/commands/version.ts
14146
14422
  var VersionCommand = class extends SmCommand {
14147
14423
  static paths = [["version"]];
14148
- static usage = Command22.Usage({
14424
+ static usage = Command23.Usage({
14149
14425
  category: "Introspection",
14150
14426
  description: "Print the CLI / kernel / spec / runtime / db-schema version matrix."
14151
14427
  });
@@ -14215,6 +14491,7 @@ cli.register(Builtins.VersionCommand);
14215
14491
  cli.register(RootHelpCommand);
14216
14492
  cli.register(HelpCommand);
14217
14493
  cli.register(InitCommand);
14494
+ cli.register(GuideCommand);
14218
14495
  cli.register(ScanCommand);
14219
14496
  cli.register(ScanCompareCommand);
14220
14497
  cli.register(ServeCommand);
@@ -14272,7 +14549,7 @@ var exitCode = await cli.run(routedArgs, {
14272
14549
  process.exit(exitCode);
14273
14550
  function resolveBareDefault() {
14274
14551
  const ctx = defaultRuntimeContext();
14275
- if (existsSync19(defaultProjectDbPath(ctx))) {
14552
+ if (existsSync20(defaultProjectDbPath(ctx))) {
14276
14553
  return ["serve"];
14277
14554
  }
14278
14555
  process.stderr.write(tx(ENTRY_TEXTS.bareNoProject, { cwd: ctx.cwd }));