@skill-map/cli 0.54.0 → 0.56.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.
Files changed (41) hide show
  1. package/dist/cli/tutorial/sm-tutorial/SKILL.md +22 -24
  2. package/dist/cli/tutorial/sm-tutorial/references/_core.md +8 -7
  3. package/dist/cli/tutorial/sm-tutorial/references/_manifest.yml +21 -42
  4. package/dist/cli/tutorial/sm-tutorial/references/fixtures.md +15 -7
  5. package/dist/cli/tutorial/sm-tutorial/references/part-authoring.md +2 -2
  6. package/dist/cli/tutorial/sm-tutorial/references/part-cli.md +1 -1
  7. package/dist/cli/tutorial/sm-tutorial/references/part-connect-harness.md +9 -10
  8. package/dist/cli/tutorial/sm-tutorial/references/part-daily-loop.md +563 -0
  9. package/dist/cli/tutorial/sm-tutorial/references/part-mcp.md +5 -1
  10. package/dist/cli/tutorial/sm-tutorial/references/part-plugins.md +7 -7
  11. package/dist/cli/tutorial/sm-tutorial/references/part-project-kickoff.md +24 -12
  12. package/dist/cli/tutorial/sm-tutorial/references/part-settings.md +2 -2
  13. package/dist/cli.js +1785 -1102
  14. package/dist/index.js +148 -14
  15. package/dist/kernel/index.d.ts +229 -97
  16. package/dist/kernel/index.js +148 -14
  17. package/dist/migrations/001_initial.sql +5 -0
  18. package/dist/ui/chunk-4ITL7E6U.js +1 -0
  19. package/dist/ui/chunk-DWBJCNC7.js +2 -0
  20. package/dist/ui/{chunk-CXTU4HQV.js → chunk-GHOVZAAV.js} +1 -1
  21. package/dist/ui/{chunk-GBKHMJ4B.js → chunk-H6O2DYVT.js} +13 -13
  22. package/dist/ui/chunk-HDKR6XHG.js +917 -0
  23. package/dist/ui/{chunk-GEI6INVH.js → chunk-JA4Z74I3.js} +1 -1
  24. package/dist/ui/chunk-RS3ANRT5.js +1 -0
  25. package/dist/ui/chunk-VUNP5KNI.js +3 -0
  26. package/dist/ui/chunk-W3Z3CZL4.js +1844 -0
  27. package/dist/ui/chunk-YHJL5LP3.js +913 -0
  28. package/dist/ui/index.html +2 -2
  29. package/dist/ui/{main-HP3MOLI2.js → main-PL3BEVQI.js} +3 -3
  30. package/dist/ui/{styles-4SNVM34O.css → styles-RHEEXRHQ.css} +1 -1
  31. package/migrations/001_initial.sql +5 -0
  32. package/package.json +2 -2
  33. package/dist/cli/tutorial/sm-tutorial/references/part-live-site.md +0 -155
  34. package/dist/cli/tutorial/sm-tutorial/references/part-maintain.md +0 -284
  35. package/dist/cli/tutorial/sm-tutorial/references/part-run-harness.md +0 -181
  36. package/dist/ui/chunk-4CXAL43H.js +0 -1
  37. package/dist/ui/chunk-BUNPMGDX.js +0 -2205
  38. package/dist/ui/chunk-DSNBKMYU.js +0 -2
  39. package/dist/ui/chunk-JXRIGHET.js +0 -552
  40. package/dist/ui/chunk-MVRQGDZJ.js +0 -123
  41. package/dist/ui/chunk-WFLPMCK4.js +0 -392
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // kernel/i18n/registry.texts.ts
2
2
 
3
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f476faaa-267b-52a2-84d7-7b8859286768")}catch(e){}}();
3
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="2dc805c3-48a5-5cde-8fdd-5ba75f99be02")}catch(e){}}();
4
4
  var REGISTRY_TEXTS = {
5
5
  duplicateExtension: "Extension already registered: {{kind}}:{{qualifiedId}}",
6
6
  unknownKind: "Unknown extension kind: {{kind}}",
@@ -102,7 +102,7 @@ import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
102
102
  // package.json
103
103
  var package_default = {
104
104
  name: "@skill-map/cli",
105
- version: "0.54.0",
105
+ version: "0.56.0",
106
106
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
107
107
  license: "MIT",
108
108
  type: "module",
@@ -745,6 +745,10 @@ function matchesFilter(hook, event) {
745
745
  function buildHookContext(_hook, trigger, event) {
746
746
  const data = event.data ?? {};
747
747
  const ctx = {
748
+ // `settings` is always populated (possibly empty) so hooks can read
749
+ // `ctx.settings.<id>` without a presence check. The composer
750
+ // populated `resolvedSettings` on each composed hook.
751
+ settings: _hook.resolvedSettings ?? {},
748
752
  event: {
749
753
  type: trigger,
750
754
  timestamp: event.timestamp,
@@ -1190,8 +1194,83 @@ function isExternalUrlLink(link) {
1190
1194
  return EXTERNAL_URL_SCHEME_RE.test(link.target);
1191
1195
  }
1192
1196
 
1197
+ // kernel/orchestrator/action-projections.ts
1198
+ function runActionProjections(actions, nodes, links, emitter) {
1199
+ const contributions = [];
1200
+ const contributionErrors = [];
1201
+ const validators = loadSchemaValidators();
1202
+ for (const action of actions) {
1203
+ if (typeof action.project !== "function") continue;
1204
+ const qualifiedId = qualifiedExtensionId(action.pluginId, action.id);
1205
+ const declaredContributions = readDeclaredContributionRefs(action);
1206
+ const emitContribution = (nodePath, ref, payload) => {
1207
+ const declared = typeof ref === "object" && ref !== null ? declaredContributions.get(ref) : void 0;
1208
+ if (!declared) {
1209
+ const message = tx(ORCHESTRATOR_TEXTS.extensionErrorContributionUndeclaredRef, {
1210
+ extractorId: qualifiedId,
1211
+ nodePath
1212
+ });
1213
+ emitExtensionError(emitter, qualifiedId, nodePath, {
1214
+ phase: "emitContribution",
1215
+ reason: "undeclared-contribution-ref",
1216
+ message
1217
+ });
1218
+ contributionErrors.push({
1219
+ pluginId: action.pluginId,
1220
+ extensionId: action.id,
1221
+ nodePath,
1222
+ reason: "undeclared-contribution-ref",
1223
+ message,
1224
+ emittedAt: Date.now()
1225
+ });
1226
+ return;
1227
+ }
1228
+ const result = validators.validateContributionPayload(declared.slot, payload);
1229
+ if (!result.ok) {
1230
+ const message = tx(ORCHESTRATOR_TEXTS.extensionErrorContributionPayloadInvalid, {
1231
+ extractorId: qualifiedId,
1232
+ contributionId: declared.id,
1233
+ nodePath,
1234
+ slot: declared.slot,
1235
+ errors: result.errors
1236
+ });
1237
+ emitExtensionError(emitter, qualifiedId, nodePath, {
1238
+ phase: "emitContribution",
1239
+ contributionId: declared.id,
1240
+ slot: declared.slot,
1241
+ reason: result.errors,
1242
+ message
1243
+ });
1244
+ contributionErrors.push({
1245
+ pluginId: action.pluginId,
1246
+ extensionId: action.id,
1247
+ nodePath,
1248
+ reason: result.errors,
1249
+ message,
1250
+ contributionId: declared.id,
1251
+ slot: declared.slot,
1252
+ emittedAt: Date.now()
1253
+ });
1254
+ return;
1255
+ }
1256
+ contributions.push({
1257
+ pluginId: action.pluginId,
1258
+ extensionId: action.id,
1259
+ nodePath,
1260
+ contributionId: declared.id,
1261
+ slot: declared.slot,
1262
+ payload,
1263
+ emittedAt: Date.now()
1264
+ });
1265
+ };
1266
+ const ctx = { nodes, links, emitContribution };
1267
+ action.project(ctx);
1268
+ }
1269
+ return { contributions, contributionErrors };
1270
+ }
1271
+
1193
1272
  // kernel/orchestrator/analyzers.ts
1194
- async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions, orphanJobFiles, referenceablePaths, cwd, registeredActionIds, emitter, hookDispatcher, reservedNodePaths, signals, seedIssues = []) {
1273
+ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions, orphanJobFiles, referenceablePaths, cwd, registeredActionIds, emitter, hookDispatcher, reservedNodePaths, brokenLinks, signals, seedIssues = []) {
1195
1274
  const issues = [...seedIssues];
1196
1275
  const contributions = [];
1197
1276
  const contributionErrors = [];
@@ -1268,6 +1347,10 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
1268
1347
  const emitted = await analyzer.evaluate({
1269
1348
  nodes,
1270
1349
  links: internalLinks,
1350
+ // `settings` is always populated (possibly empty) so analyzers can
1351
+ // read `ctx.settings.<id>` without a presence check. The composer
1352
+ // populated `resolvedSettings` on each composed analyzer.
1353
+ settings: analyzer.resolvedSettings ?? {},
1271
1354
  orphanSidecars: analyzerOrphans,
1272
1355
  sidecarRoots,
1273
1356
  annotationContributions,
@@ -1281,6 +1364,7 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
1281
1364
  ...referenceablePaths ? { referenceablePaths } : {},
1282
1365
  ...cwd ? { cwd } : {},
1283
1366
  ...reservedNodePaths ? { reservedNodePaths } : {},
1367
+ ...brokenLinks ? { brokenLinks } : {},
1284
1368
  ...signals && signals.length > 0 ? { signals } : {},
1285
1369
  emitContribution
1286
1370
  });
@@ -1560,16 +1644,34 @@ function readDirname(node) {
1560
1644
 
1561
1645
  // kernel/orchestrator/lift-resolved-link-confidence.ts
1562
1646
  var RESERVED_TARGET_CONFIDENCE = 0.1;
1647
+ var BROKEN_TARGET_CONFIDENCE = 0.5;
1563
1648
  function liftResolvedLinkConfidence(links, nodes, ctx) {
1564
1649
  if (!links.some((l) => l.confidence < 1)) return;
1565
1650
  const indexes = buildIndexes(nodes, ctx);
1566
1651
  for (const link of links) {
1567
- if (link.confidence >= 1) continue;
1568
- const resolution = resolve5(link, indexes, ctx);
1569
- if (resolution === "none") continue;
1570
- link.confidence = ctx.reservedNodePaths.has(resolution) ? RESERVED_TARGET_CONFIDENCE : 1;
1571
- link.resolvedTarget = resolution;
1652
+ if (link.confidence < 1) applyResolution(link, indexes, ctx);
1653
+ }
1654
+ }
1655
+ function collectBrokenLinks(links, nodes, ctx) {
1656
+ const broken = /* @__PURE__ */ new Set();
1657
+ if (links.length === 0) return broken;
1658
+ const indexes = buildIndexes(nodes, ctx);
1659
+ for (const link of links) {
1660
+ if (isGenuinelyBroken(link, indexes)) broken.add(link);
1572
1661
  }
1662
+ return broken;
1663
+ }
1664
+ function applyResolution(link, indexes, ctx) {
1665
+ const resolution = resolve5(link, indexes, ctx);
1666
+ if (resolution === "none") {
1667
+ if (isGenuinelyBroken(link, indexes)) {
1668
+ link.confidence = Math.min(link.confidence, BROKEN_TARGET_CONFIDENCE);
1669
+ }
1670
+ return;
1671
+ }
1672
+ link.resolvedTarget = resolution;
1673
+ if (indexes.nodeByPath.get(resolution)?.virtual) return;
1674
+ link.confidence = ctx.reservedNodePaths.has(resolution) ? RESERVED_TARGET_CONFIDENCE : 1;
1573
1675
  }
1574
1676
  function buildIndexes(nodes, ctx) {
1575
1677
  const byPath2 = /* @__PURE__ */ new Set();
@@ -1586,6 +1688,12 @@ function resolve5(link, indexes, ctx) {
1586
1688
  if (indexes.byPath.has(link.target)) return link.target;
1587
1689
  return resolveByName(link, indexes, ctx);
1588
1690
  }
1691
+ function isGenuinelyBroken(link, indexes) {
1692
+ if (indexes.byPath.has(link.target)) return false;
1693
+ const stripped = stripTriggerSigil(link.trigger?.normalizedTrigger);
1694
+ if (stripped !== null && indexes.byName.has(stripped)) return false;
1695
+ return true;
1696
+ }
1589
1697
  function resolveByName(link, indexes, ctx) {
1590
1698
  const stripped = stripTriggerSigil(link.trigger?.normalizedTrigger);
1591
1699
  if (stripped === null) return "none";
@@ -2146,11 +2254,11 @@ async function* walkContent(roots, options) {
2146
2254
  const extensions = options.extensions;
2147
2255
  const sizeLimit = buildSizeLimit(options);
2148
2256
  for (const root of roots) {
2149
- for await (const file of walkRoot(root, root, filter, extensions, sizeLimit)) {
2150
- const relPath = relative2(root, file).split(sep).join("/");
2257
+ for await (const entry of walkRoot(root, root, filter, extensions, sizeLimit)) {
2258
+ const relPath = relative2(root, entry.full).split(sep).join("/");
2151
2259
  let raw;
2152
2260
  try {
2153
- raw = await readFile(file, "utf8");
2261
+ raw = await readFile(entry.full, "utf8");
2154
2262
  } catch {
2155
2263
  continue;
2156
2264
  }
@@ -2160,6 +2268,9 @@ async function* walkContent(roots, options) {
2160
2268
  body: parsed.body,
2161
2269
  frontmatterRaw: parsed.frontmatterRaw,
2162
2270
  frontmatter: parsed.frontmatter,
2271
+ // File mtime from the TOCTOU `lstat` (zero extra syscalls).
2272
+ // Threaded onto the persisted `Node` as `modifiedAtMs`.
2273
+ modifiedAtMs: entry.modifiedAtMs,
2163
2274
  // Audit L1: forward parser diagnostics (e.g. malformed YAML)
2164
2275
  // through the IRawNode surface so the orchestrator can
2165
2276
  // convert them into warn-level kernel `Issue` rows. Omitted
@@ -2200,7 +2311,7 @@ async function* walkRoot(root, current, filter, extensions, sizeLimit) {
2200
2311
  sizeLimit.onOversizedFile?.({ path: rel, bytes: s.size });
2201
2312
  continue;
2202
2313
  }
2203
- yield full;
2314
+ yield { full, modifiedAtMs: Math.round(s.mtimeMs) };
2204
2315
  } catch {
2205
2316
  }
2206
2317
  }
@@ -2491,6 +2602,7 @@ function buildNode(args) {
2491
2602
  externalRefsCount: 0,
2492
2603
  frontmatter: args.frontmatter
2493
2604
  };
2605
+ if (args.modifiedAtMs !== void 0) node.modifiedAtMs = args.modifiedAtMs;
2494
2606
  if (args.encoder) {
2495
2607
  node.tokens = countTokens(args.encoder, args.frontmatterRaw, args.body);
2496
2608
  }
@@ -2606,7 +2718,10 @@ function buildFreshNodeAndValidateFrontmatter(opts) {
2606
2718
  frontmatter: opts.raw.frontmatter,
2607
2719
  bodyHash: opts.bodyHash,
2608
2720
  frontmatterHash: opts.frontmatterHash,
2609
- encoder: opts.encoder
2721
+ encoder: opts.encoder,
2722
+ // Thread the walker's mtime through; `buildNode` only attaches it
2723
+ // when present, so virtual / walk()-without-stat sources stay absent.
2724
+ modifiedAtMs: opts.raw.modifiedAtMs
2610
2725
  });
2611
2726
  const frontmatterIssues = [];
2612
2727
  if (opts.raw.parseIssues && opts.raw.parseIssues.length > 0) {
@@ -3027,6 +3142,7 @@ async function runScanInternal(_kernel, options) {
3027
3142
  walked.signals = resolved.resolvedSignals;
3028
3143
  const postWalkCtx = buildPostWalkTransformCtx(exts.providers, walked.nodes, activeProviderId);
3029
3144
  walked.internalLinks = applyPostWalkTransforms(walked.internalLinks, walked.nodes, postWalkCtx);
3145
+ const brokenLinks = collectBrokenLinks(walked.internalLinks, walked.nodes, postWalkCtx);
3030
3146
  recomputeLinkCounts(walked.nodes, walked.internalLinks);
3031
3147
  recomputeExternalRefsCount(walked.nodes, walked.externalLinks, walked.cachedPaths);
3032
3148
  await dispatchExtractorCompleted(exts.extractors, emitter, hookDispatcher);
@@ -3048,6 +3164,7 @@ async function runScanInternal(_kernel, options) {
3048
3164
  emitter,
3049
3165
  hookDispatcher,
3050
3166
  postWalkCtx.reservedNodePaths,
3167
+ brokenLinks,
3051
3168
  walked.signals,
3052
3169
  // Seed the accumulator with orchestrator-emitted frontmatter
3053
3170
  // issues so the aggregate phase (`core/issue-counter`) counts
@@ -3056,6 +3173,13 @@ async function runScanInternal(_kernel, options) {
3056
3173
  walked.frontmatterIssues
3057
3174
  );
3058
3175
  mergeAnalyzerEmissions(walked, analyzerResult, exts.analyzers);
3176
+ const projectionResult = runActionProjections(
3177
+ exts.actions ?? [],
3178
+ walked.nodes,
3179
+ walked.internalLinks,
3180
+ emitter
3181
+ );
3182
+ mergeActionProjections(walked, projectionResult, exts.actions);
3059
3183
  const issues = analyzerResult.issues;
3060
3184
  const silenced = options.ignoreFilter ? (path) => options.ignoreFilter.ignores(path) : void 0;
3061
3185
  const renameOps = prior ? detectRenamesAndOrphans(prior, walked.nodes, issues, silenced) : [];
@@ -3164,6 +3288,16 @@ function mergeAnalyzerEmissions(walked, analyzerResult, analyzers) {
3164
3288
  }
3165
3289
  }
3166
3290
  }
3291
+ function mergeActionProjections(walked, projectionResult, actions) {
3292
+ for (const c of projectionResult.contributions) walked.contributions.push(c);
3293
+ for (const e of projectionResult.contributionErrors) walked.contributionErrors.push(e);
3294
+ for (const action of actions ?? []) {
3295
+ if (action.ui === void 0 || typeof action.project !== "function") continue;
3296
+ for (const node of walked.nodes) {
3297
+ walked.freshlyRunTuples.add(`${action.pluginId}\0${action.id}\0${node.path}`);
3298
+ }
3299
+ }
3300
+ }
3167
3301
  function buildScanStats(walked, issues, start) {
3168
3302
  return {
3169
3303
  // `filesSkipped` is "files walked but not classified by any
@@ -3632,4 +3766,4 @@ export {
3632
3766
  runScanWithRenames
3633
3767
  };
3634
3768
  //# sourceMappingURL=index.js.map
3635
- //# debugId=f476faaa-267b-52a2-84d7-7b8859286768
3769
+ //# debugId=2dc805c3-48a5-5cde-8fdd-5ba75f99be02