@skill-map/cli 0.69.0 → 0.71.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 (45) hide show
  1. package/dist/cli/tutorial/sm-tutorial/SKILL.md +8 -3
  2. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/agents-hub/providers/codex/en/agents-hub.md +2 -0
  3. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/agents-hub/providers/codex/es/agents-hub.md +2 -0
  4. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/codex/en/todo-bullet-agent.md +1 -0
  5. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/codex/en/todo-bullet-command.md +1 -0
  6. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/codex/en/todo-bullet-skill.md +1 -0
  7. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/codex/es/todo-bullet-agent.md +1 -0
  8. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/codex/es/todo-bullet-command.md +1 -0
  9. package/dist/cli/tutorial/sm-tutorial/fixtures-data/edits/todo-connectors/providers/codex/es/todo-bullet-skill.md +1 -0
  10. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/harness/providers/codex/en/__PROVIDER__/skills/publish/SKILL.md +2 -2
  11. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/harness/providers/codex/es/__PROVIDER__/skills/publish/SKILL.md +2 -2
  12. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/master/providers/codex/en/.codex/agents/master-agent.toml +1 -1
  13. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/master/providers/codex/es/.codex/agents/master-agent.toml +1 -1
  14. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/providers/codex/en/.codex/agents/content-editor.toml +1 -1
  15. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/providers/codex/es/.codex/agents/content-editor.toml +1 -1
  16. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/providers/codex/en/.codex/agents/demo-agent.toml +1 -1
  17. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/prologue/providers/codex/es/.codex/agents/demo-agent.toml +1 -1
  18. package/dist/cli/tutorial/sm-tutorial/references/_core.md +73 -47
  19. package/dist/cli/tutorial/sm-tutorial/references/_manifest.yml +2 -2
  20. package/dist/cli/tutorial/sm-tutorial/references/part-authoring.md +0 -7
  21. package/dist/cli/tutorial/sm-tutorial/references/part-basic-daily.md +10 -26
  22. package/dist/cli/tutorial/sm-tutorial/references/part-basic-fundamentals.md +10 -18
  23. package/dist/cli/tutorial/sm-tutorial/references/part-basic-kickoff.md +12 -31
  24. package/dist/cli/tutorial/sm-tutorial/references/part-cli.md +0 -4
  25. package/dist/cli/tutorial/sm-tutorial/references/part-daily-loop.md +97 -43
  26. package/dist/cli/tutorial/sm-tutorial/references/part-fundamentals.md +11 -14
  27. package/dist/cli/tutorial/sm-tutorial/references/part-mcp.md +4 -14
  28. package/dist/cli/tutorial/sm-tutorial/references/part-plugins.md +0 -8
  29. package/dist/cli/tutorial/sm-tutorial/references/part-project-kickoff.md +18 -36
  30. package/dist/cli/tutorial/sm-tutorial/references/part-settings.md +2 -10
  31. package/dist/cli/tutorial/sm-tutorial/scripts/lib/paths.js +6 -5
  32. package/dist/cli.js +441 -269
  33. package/dist/index.js +115 -18
  34. package/dist/kernel/index.d.ts +27 -0
  35. package/dist/kernel/index.js +115 -18
  36. package/dist/ui/chunk-CK4C2IIP.js +3 -0
  37. package/dist/ui/{chunk-RRRXQNG6.js → chunk-EVNCL7FV.js} +21 -21
  38. package/dist/ui/{chunk-E7GLGHVY.js → chunk-GUGB4JY5.js} +1 -1
  39. package/dist/ui/{chunk-SXSNTF26.js → chunk-RSPEJBPT.js} +1 -1
  40. package/dist/ui/chunk-SQCXHF3J.js +2 -0
  41. package/dist/ui/index.html +1 -1
  42. package/dist/ui/{main-23NGLEUB.js → main-GY4PAVQW.js} +3 -3
  43. package/package.json +2 -2
  44. package/dist/ui/chunk-RLRSNHYG.js +0 -3
  45. package/dist/ui/chunk-SI4MGFOW.js +0 -2
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]="8412e979-4214-5385-b673-277f1b8e6e02")}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]="44976d76-8c48-5b39-862d-641f0e3be027")}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.69.0",
105
+ version: "0.71.0",
106
106
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
107
107
  license: "MIT",
108
108
  type: "module",
@@ -789,6 +789,8 @@ var ORCHESTRATOR_TEXTS = {
789
789
  frontmatterMalformedPasteWithIndent: "Frontmatter fence in {{path}} appears indented; YAML frontmatter MUST start with `---` at column 0. The file was scanned as body-only; the metadata block was silently lost. Move the `---` lines to the start of the line.",
790
790
  frontmatterMalformedByteOrderMark: "Frontmatter fence in {{path}} is preceded by a UTF-8 byte-order mark (BOM); the file was scanned as body-only. Re-save the file as UTF-8 without BOM. The metadata block was silently lost.",
791
791
  frontmatterMalformedMissingClose: "Frontmatter in {{path}} opens with `---` but never closes (no matching `---` line at column 0 was found). The file was scanned as body-only and every metadata field was silently lost. Add a closing `---` line below the metadata block.",
792
+ bodyBacktickUnclosedFence: "Body of {{path}} has an unclosed fenced code block opened at body line {{line}} (no matching closing ``` or ~~~). The code-strip policy then reads the rest of the file as code, so prose extractors stop emitting edges past it. Close the fence.",
793
+ bodyBacktickUnclosedInline: "Body of {{path}} has an unclosed inline backtick at body line {{line}} (the backtick run has no equal-length closer). Close the inline span with a matching backtick run, or escape a literal backtick with a backslash.",
792
794
  extensionErrorLinkKindNotDeclared: 'Extractor "{{extractorId}}" emitted a link of kind "{{linkKind}}" outside its declared `emitsLinkKinds` set [{{declaredKinds}}]. Link dropped.',
793
795
  extensionErrorIssueInvalidSeverity: `Rule "{{analyzerId}}" emitted an issue with invalid severity {{severity}} (allowed: 'error' | 'warn' | 'info'). Issue dropped.`,
794
796
  extensionErrorContributionUndeclaredRef: 'Extension "{{extractorId}}" emitted a view contribution on {{nodePath}} whose object is not one declared in its `ui` map (pass the declared const by reference, do not spread or inline it). Contribution dropped.',
@@ -814,14 +816,16 @@ function installedDefaultEnabled(stability) {
814
816
  // kernel/scan/detect-providers.ts
815
817
  function detectProvidersFromFilesystem(cwd, providers) {
816
818
  const seen = /* @__PURE__ */ new Set();
817
- const out = [];
819
+ const matched = [];
818
820
  for (const provider of providers) {
819
821
  if (seen.has(provider.id)) continue;
820
822
  if (!isDetectableUnderCwd(cwd, provider)) continue;
821
823
  seen.add(provider.id);
822
- out.push(provider.id);
824
+ matched.push(provider);
823
825
  }
824
- return out;
826
+ const hasVendor = matched.some((p) => p.detect?.fallback !== true);
827
+ const kept = hasVendor ? matched.filter((p) => p.detect?.fallback !== true) : matched;
828
+ return kept.map((p) => p.id);
825
829
  }
826
830
  function isDetectableUnderCwd(cwd, provider) {
827
831
  if (!installedDefaultEnabled(provider.stability)) return false;
@@ -1506,6 +1510,11 @@ var FRONTMATTER_ISSUE_ANALYZERS = /* @__PURE__ */ new Set([
1506
1510
  "frontmatter-malformed",
1507
1511
  "frontmatter-parse-error"
1508
1512
  ]);
1513
+ var BACKTICK_ISSUE_ID = "backtick-unbalanced";
1514
+ var CACHED_KERNEL_ISSUE_ANALYZERS = /* @__PURE__ */ new Set([
1515
+ ...FRONTMATTER_ISSUE_ANALYZERS,
1516
+ BACKTICK_ISSUE_ID
1517
+ ]);
1509
1518
 
1510
1519
  // kernel/orchestrator/cache.ts
1511
1520
  function indexPriorSnapshot(prior) {
@@ -1535,7 +1544,7 @@ function indexPriorLinks(links, byOriginating) {
1535
1544
  }
1536
1545
  function indexPriorFrontmatterIssues(issues, byNode) {
1537
1546
  for (const issue of issues) {
1538
- if (!FRONTMATTER_ISSUE_ANALYZERS.has(issue.analyzerId)) continue;
1547
+ if (!CACHED_KERNEL_ISSUE_ANALYZERS.has(issue.analyzerId)) continue;
1539
1548
  if (issue.nodeIds.length !== 1) continue;
1540
1549
  const path = issue.nodeIds[0];
1541
1550
  const list = byNode.get(path);
@@ -1817,7 +1826,7 @@ function lookupAllowedKinds(link, _indexes, ctx) {
1817
1826
  }
1818
1827
  function stripTriggerSigil(normalized) {
1819
1828
  if (!normalized) return null;
1820
- const trimmed = normalized.replace(/^[/@]/, "").trim();
1829
+ const trimmed = normalized.replace(/^[/@$]/, "").trim();
1821
1830
  return trimmed.length === 0 ? null : trimmed;
1822
1831
  }
1823
1832
  function indexNode(node, ctx, byName) {
@@ -2801,6 +2810,83 @@ function malformedMessage(hint, path) {
2801
2810
  }
2802
2811
  }
2803
2812
 
2813
+ // kernel/util/strip-code-blocks.ts
2814
+ var FENCE_RE = /^(?<indent> {0,3})(?<fence>`{3,}|~{3,})/;
2815
+ function findBacktickImbalance(body) {
2816
+ if (!body) return null;
2817
+ const { stripped, openFenceLine } = scanFences(body);
2818
+ if (openFenceLine > 0) {
2819
+ return { kind: "fence", line: openFenceLine, sourceLine: sourceLineAt(body, openFenceLine) };
2820
+ }
2821
+ const survivor = stripInline(maskEscapes(stripped)).indexOf("`");
2822
+ if (survivor < 0) return null;
2823
+ const line = lineOfIndex(stripped, survivor);
2824
+ return { kind: "inline", line, sourceLine: sourceLineAt(body, line) };
2825
+ }
2826
+ function maskEscapes(text) {
2827
+ return text.replace(/\\[^\n]/g, " ");
2828
+ }
2829
+ function lineOfIndex(text, idx) {
2830
+ let line = 1;
2831
+ for (let i = 0; i < idx; i++) if (text[i] === "\n") line++;
2832
+ return line;
2833
+ }
2834
+ function sourceLineAt(text, line) {
2835
+ return text.split("\n")[line - 1]?.trim() ?? "";
2836
+ }
2837
+ function scanFences(input) {
2838
+ const out = [];
2839
+ const lines = input.split("\n");
2840
+ let openFence = null;
2841
+ let openFenceLine = 0;
2842
+ for (let i = 0; i < lines.length; i++) {
2843
+ const line = lines[i];
2844
+ if (openFence) {
2845
+ if (matchClosingFence(line, openFence)) openFence = null;
2846
+ out.push(blank(line));
2847
+ continue;
2848
+ }
2849
+ const open = FENCE_RE.exec(line);
2850
+ if (open?.groups) {
2851
+ openFence = open.groups["fence"];
2852
+ openFenceLine = i + 1;
2853
+ out.push(blank(line));
2854
+ continue;
2855
+ }
2856
+ out.push(line);
2857
+ }
2858
+ return { stripped: out.join("\n"), openFenceLine: openFence ? openFenceLine : 0 };
2859
+ }
2860
+ function matchClosingFence(line, openFence) {
2861
+ const m = FENCE_RE.exec(line);
2862
+ if (!m?.groups) return false;
2863
+ const fence = m.groups["fence"];
2864
+ return fence[0] === openFence[0] && fence.length >= openFence.length;
2865
+ }
2866
+ function stripInline(input) {
2867
+ return input.replace(/(`+)([\s\S]*?)\1/g, (_full, ticks, body) => {
2868
+ return ticks.replace(/`/g, " ") + blank(body) + ticks.replace(/`/g, " ");
2869
+ });
2870
+ }
2871
+ function blank(s) {
2872
+ return s.replace(/[^\s]/g, " ");
2873
+ }
2874
+
2875
+ // kernel/orchestrator/body-syntax.ts
2876
+ function detectUnclosedBacktick(body, path, strict) {
2877
+ const imbalance = findBacktickImbalance(body);
2878
+ if (!imbalance) return null;
2879
+ const template = imbalance.kind === "fence" ? ORCHESTRATOR_TEXTS.bodyBacktickUnclosedFence : ORCHESTRATOR_TEXTS.bodyBacktickUnclosedInline;
2880
+ return {
2881
+ analyzerId: BACKTICK_ISSUE_ID,
2882
+ severity: strict ? "error" : "warn",
2883
+ nodeIds: [path],
2884
+ message: tx(template, { path, line: imbalance.line }),
2885
+ detail: imbalance.sourceLine,
2886
+ data: { kind: imbalance.kind, line: imbalance.line }
2887
+ };
2888
+ }
2889
+
2804
2890
  // kernel/orchestrator/node-build.ts
2805
2891
  function buildNode(args) {
2806
2892
  const bytesFrontmatter = Buffer.byteLength(args.frontmatterRaw, "utf8");
@@ -2927,6 +3013,9 @@ function relativePathFromRoots(absolutePath, roots) {
2927
3013
  }
2928
3014
  return absolutePath;
2929
3015
  }
3016
+ function pushIssue(list, issue) {
3017
+ if (issue) list.push(issue);
3018
+ }
2930
3019
  function buildFreshNodeAndValidateFrontmatter(opts) {
2931
3020
  const node = buildNode({
2932
3021
  path: opts.raw.path,
@@ -2954,19 +3043,27 @@ function buildFreshNodeAndValidateFrontmatter(opts) {
2954
3043
  }
2955
3044
  }
2956
3045
  if (opts.raw.frontmatterRaw.length > 0) {
2957
- const fmIssue = validateFrontmatter(
2958
- opts.providerFrontmatter,
2959
- opts.provider,
2960
- opts.kind,
2961
- opts.raw.frontmatter,
2962
- opts.raw.path,
2963
- opts.strict
3046
+ pushIssue(
3047
+ frontmatterIssues,
3048
+ validateFrontmatter(
3049
+ opts.providerFrontmatter,
3050
+ opts.provider,
3051
+ opts.kind,
3052
+ opts.raw.frontmatter,
3053
+ opts.raw.path,
3054
+ opts.strict
3055
+ )
2964
3056
  );
2965
- if (fmIssue) frontmatterIssues.push(fmIssue);
2966
3057
  } else {
2967
- const malformed = detectMalformedFrontmatter(opts.raw.body, opts.raw.path, opts.strict);
2968
- if (malformed) frontmatterIssues.push(malformed);
3058
+ pushIssue(
3059
+ frontmatterIssues,
3060
+ detectMalformedFrontmatter(opts.raw.body, opts.raw.path, opts.strict)
3061
+ );
2969
3062
  }
3063
+ pushIssue(
3064
+ frontmatterIssues,
3065
+ detectUnclosedBacktick(opts.raw.body, opts.raw.path, opts.strict)
3066
+ );
2970
3067
  return { node, frontmatterIssues };
2971
3068
  }
2972
3069
  function mergeNodeWithEnrichments(node, enrichments, opts = {}) {
@@ -4164,4 +4261,4 @@ export {
4164
4261
  runScanWithRenames
4165
4262
  };
4166
4263
  //# sourceMappingURL=index.js.map
4167
- //# debugId=8412e979-4214-5385-b673-277f1b8e6e02
4264
+ //# debugId=44976d76-8c48-5b39-862d-641f0e3be027
@@ -2582,6 +2582,21 @@ interface IProviderUi {
2582
2582
  * topbar lens chip; only the per-card badge is suppressed.
2583
2583
  */
2584
2584
  hideChip?: boolean;
2585
+ /**
2586
+ * Single glyph this lens's runtime uses to invoke a skill / command,
2587
+ * surfaced as the `invokes` edge-kind glyph (and its tooltip example)
2588
+ * in the link-kind palette so the operator recognises the source
2589
+ * syntax instantly. `/` for the slash-invoking lenses (`claude`
2590
+ * commands + skills, `antigravity` skills + workflows), `$` for
2591
+ * `codex` (skills are `$skill`; `/` is reserved for Codex's own
2592
+ * built-in commands). Omitted for lenses with no `/`/`$` invocation
2593
+ * channel (the open-standard `agent-skills`, where skills activate by
2594
+ * `description`, and the non-lens `markdown` base): under those no
2595
+ * `invokes` edge arises, so the palette never paints the glyph.
2596
+ * Projected into `providerRegistry` and joined client-side against
2597
+ * the active lens.
2598
+ */
2599
+ invocationSigil?: string;
2585
2600
  }
2586
2601
  /**
2587
2602
  * Auto-detection markers for the active-provider lens. The lens resolver
@@ -2598,6 +2613,18 @@ interface IProviderDetect {
2598
2613
  * A directory or a file both count; existence is the only test.
2599
2614
  */
2600
2615
  markers: string[];
2616
+ /**
2617
+ * When `true`, this Provider is the open-standard FALLBACK lens: its
2618
+ * markers produce a detection candidate ONLY when no non-fallback
2619
+ * (vendor) Provider matched under the same scope. Reserved for
2620
+ * `agent-skills`, whose `.agents/` marker is the shared skill home that
2621
+ * vendor lenses (`codex`, `antigravity`) also populate; without this flag
2622
+ * a `.codex/` + `.agents/` project would falsely read as an ambiguous
2623
+ * `codex` vs `agent-skills` pair. Vendor Providers omit it (default
2624
+ * `false`) so two vendor markers still surface a real ambiguous prompt.
2625
+ * Mirrors `provider.schema.json#/properties/detect/properties/fallback`.
2626
+ */
2627
+ fallback?: boolean;
2601
2628
  }
2602
2629
  /**
2603
2630
  * Authoring targets for verbs that MATERIALISE files into this
@@ -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]="c7e7d754-73eb-5f99-8e19-732a9f84a4c8")}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]="7667ff07-bdd8-541a-b813-e01e201964f0")}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.69.0",
105
+ version: "0.71.0",
106
106
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
107
107
  license: "MIT",
108
108
  type: "module",
@@ -789,6 +789,8 @@ var ORCHESTRATOR_TEXTS = {
789
789
  frontmatterMalformedPasteWithIndent: "Frontmatter fence in {{path}} appears indented; YAML frontmatter MUST start with `---` at column 0. The file was scanned as body-only; the metadata block was silently lost. Move the `---` lines to the start of the line.",
790
790
  frontmatterMalformedByteOrderMark: "Frontmatter fence in {{path}} is preceded by a UTF-8 byte-order mark (BOM); the file was scanned as body-only. Re-save the file as UTF-8 without BOM. The metadata block was silently lost.",
791
791
  frontmatterMalformedMissingClose: "Frontmatter in {{path}} opens with `---` but never closes (no matching `---` line at column 0 was found). The file was scanned as body-only and every metadata field was silently lost. Add a closing `---` line below the metadata block.",
792
+ bodyBacktickUnclosedFence: "Body of {{path}} has an unclosed fenced code block opened at body line {{line}} (no matching closing ``` or ~~~). The code-strip policy then reads the rest of the file as code, so prose extractors stop emitting edges past it. Close the fence.",
793
+ bodyBacktickUnclosedInline: "Body of {{path}} has an unclosed inline backtick at body line {{line}} (the backtick run has no equal-length closer). Close the inline span with a matching backtick run, or escape a literal backtick with a backslash.",
792
794
  extensionErrorLinkKindNotDeclared: 'Extractor "{{extractorId}}" emitted a link of kind "{{linkKind}}" outside its declared `emitsLinkKinds` set [{{declaredKinds}}]. Link dropped.',
793
795
  extensionErrorIssueInvalidSeverity: `Rule "{{analyzerId}}" emitted an issue with invalid severity {{severity}} (allowed: 'error' | 'warn' | 'info'). Issue dropped.`,
794
796
  extensionErrorContributionUndeclaredRef: 'Extension "{{extractorId}}" emitted a view contribution on {{nodePath}} whose object is not one declared in its `ui` map (pass the declared const by reference, do not spread or inline it). Contribution dropped.',
@@ -814,14 +816,16 @@ function installedDefaultEnabled(stability) {
814
816
  // kernel/scan/detect-providers.ts
815
817
  function detectProvidersFromFilesystem(cwd, providers) {
816
818
  const seen = /* @__PURE__ */ new Set();
817
- const out = [];
819
+ const matched = [];
818
820
  for (const provider of providers) {
819
821
  if (seen.has(provider.id)) continue;
820
822
  if (!isDetectableUnderCwd(cwd, provider)) continue;
821
823
  seen.add(provider.id);
822
- out.push(provider.id);
824
+ matched.push(provider);
823
825
  }
824
- return out;
826
+ const hasVendor = matched.some((p) => p.detect?.fallback !== true);
827
+ const kept = hasVendor ? matched.filter((p) => p.detect?.fallback !== true) : matched;
828
+ return kept.map((p) => p.id);
825
829
  }
826
830
  function isDetectableUnderCwd(cwd, provider) {
827
831
  if (!installedDefaultEnabled(provider.stability)) return false;
@@ -1506,6 +1510,11 @@ var FRONTMATTER_ISSUE_ANALYZERS = /* @__PURE__ */ new Set([
1506
1510
  "frontmatter-malformed",
1507
1511
  "frontmatter-parse-error"
1508
1512
  ]);
1513
+ var BACKTICK_ISSUE_ID = "backtick-unbalanced";
1514
+ var CACHED_KERNEL_ISSUE_ANALYZERS = /* @__PURE__ */ new Set([
1515
+ ...FRONTMATTER_ISSUE_ANALYZERS,
1516
+ BACKTICK_ISSUE_ID
1517
+ ]);
1509
1518
 
1510
1519
  // kernel/orchestrator/cache.ts
1511
1520
  function indexPriorSnapshot(prior) {
@@ -1535,7 +1544,7 @@ function indexPriorLinks(links, byOriginating) {
1535
1544
  }
1536
1545
  function indexPriorFrontmatterIssues(issues, byNode) {
1537
1546
  for (const issue of issues) {
1538
- if (!FRONTMATTER_ISSUE_ANALYZERS.has(issue.analyzerId)) continue;
1547
+ if (!CACHED_KERNEL_ISSUE_ANALYZERS.has(issue.analyzerId)) continue;
1539
1548
  if (issue.nodeIds.length !== 1) continue;
1540
1549
  const path = issue.nodeIds[0];
1541
1550
  const list = byNode.get(path);
@@ -1817,7 +1826,7 @@ function lookupAllowedKinds(link, _indexes, ctx) {
1817
1826
  }
1818
1827
  function stripTriggerSigil(normalized) {
1819
1828
  if (!normalized) return null;
1820
- const trimmed = normalized.replace(/^[/@]/, "").trim();
1829
+ const trimmed = normalized.replace(/^[/@$]/, "").trim();
1821
1830
  return trimmed.length === 0 ? null : trimmed;
1822
1831
  }
1823
1832
  function indexNode(node, ctx, byName) {
@@ -2801,6 +2810,83 @@ function malformedMessage(hint, path) {
2801
2810
  }
2802
2811
  }
2803
2812
 
2813
+ // kernel/util/strip-code-blocks.ts
2814
+ var FENCE_RE = /^(?<indent> {0,3})(?<fence>`{3,}|~{3,})/;
2815
+ function findBacktickImbalance(body) {
2816
+ if (!body) return null;
2817
+ const { stripped, openFenceLine } = scanFences(body);
2818
+ if (openFenceLine > 0) {
2819
+ return { kind: "fence", line: openFenceLine, sourceLine: sourceLineAt(body, openFenceLine) };
2820
+ }
2821
+ const survivor = stripInline(maskEscapes(stripped)).indexOf("`");
2822
+ if (survivor < 0) return null;
2823
+ const line = lineOfIndex(stripped, survivor);
2824
+ return { kind: "inline", line, sourceLine: sourceLineAt(body, line) };
2825
+ }
2826
+ function maskEscapes(text) {
2827
+ return text.replace(/\\[^\n]/g, " ");
2828
+ }
2829
+ function lineOfIndex(text, idx) {
2830
+ let line = 1;
2831
+ for (let i = 0; i < idx; i++) if (text[i] === "\n") line++;
2832
+ return line;
2833
+ }
2834
+ function sourceLineAt(text, line) {
2835
+ return text.split("\n")[line - 1]?.trim() ?? "";
2836
+ }
2837
+ function scanFences(input) {
2838
+ const out = [];
2839
+ const lines = input.split("\n");
2840
+ let openFence = null;
2841
+ let openFenceLine = 0;
2842
+ for (let i = 0; i < lines.length; i++) {
2843
+ const line = lines[i];
2844
+ if (openFence) {
2845
+ if (matchClosingFence(line, openFence)) openFence = null;
2846
+ out.push(blank(line));
2847
+ continue;
2848
+ }
2849
+ const open = FENCE_RE.exec(line);
2850
+ if (open?.groups) {
2851
+ openFence = open.groups["fence"];
2852
+ openFenceLine = i + 1;
2853
+ out.push(blank(line));
2854
+ continue;
2855
+ }
2856
+ out.push(line);
2857
+ }
2858
+ return { stripped: out.join("\n"), openFenceLine: openFence ? openFenceLine : 0 };
2859
+ }
2860
+ function matchClosingFence(line, openFence) {
2861
+ const m = FENCE_RE.exec(line);
2862
+ if (!m?.groups) return false;
2863
+ const fence = m.groups["fence"];
2864
+ return fence[0] === openFence[0] && fence.length >= openFence.length;
2865
+ }
2866
+ function stripInline(input) {
2867
+ return input.replace(/(`+)([\s\S]*?)\1/g, (_full, ticks, body) => {
2868
+ return ticks.replace(/`/g, " ") + blank(body) + ticks.replace(/`/g, " ");
2869
+ });
2870
+ }
2871
+ function blank(s) {
2872
+ return s.replace(/[^\s]/g, " ");
2873
+ }
2874
+
2875
+ // kernel/orchestrator/body-syntax.ts
2876
+ function detectUnclosedBacktick(body, path, strict) {
2877
+ const imbalance = findBacktickImbalance(body);
2878
+ if (!imbalance) return null;
2879
+ const template = imbalance.kind === "fence" ? ORCHESTRATOR_TEXTS.bodyBacktickUnclosedFence : ORCHESTRATOR_TEXTS.bodyBacktickUnclosedInline;
2880
+ return {
2881
+ analyzerId: BACKTICK_ISSUE_ID,
2882
+ severity: strict ? "error" : "warn",
2883
+ nodeIds: [path],
2884
+ message: tx(template, { path, line: imbalance.line }),
2885
+ detail: imbalance.sourceLine,
2886
+ data: { kind: imbalance.kind, line: imbalance.line }
2887
+ };
2888
+ }
2889
+
2804
2890
  // kernel/orchestrator/node-build.ts
2805
2891
  function buildNode(args) {
2806
2892
  const bytesFrontmatter = Buffer.byteLength(args.frontmatterRaw, "utf8");
@@ -2927,6 +3013,9 @@ function relativePathFromRoots(absolutePath, roots) {
2927
3013
  }
2928
3014
  return absolutePath;
2929
3015
  }
3016
+ function pushIssue(list, issue) {
3017
+ if (issue) list.push(issue);
3018
+ }
2930
3019
  function buildFreshNodeAndValidateFrontmatter(opts) {
2931
3020
  const node = buildNode({
2932
3021
  path: opts.raw.path,
@@ -2954,19 +3043,27 @@ function buildFreshNodeAndValidateFrontmatter(opts) {
2954
3043
  }
2955
3044
  }
2956
3045
  if (opts.raw.frontmatterRaw.length > 0) {
2957
- const fmIssue = validateFrontmatter(
2958
- opts.providerFrontmatter,
2959
- opts.provider,
2960
- opts.kind,
2961
- opts.raw.frontmatter,
2962
- opts.raw.path,
2963
- opts.strict
3046
+ pushIssue(
3047
+ frontmatterIssues,
3048
+ validateFrontmatter(
3049
+ opts.providerFrontmatter,
3050
+ opts.provider,
3051
+ opts.kind,
3052
+ opts.raw.frontmatter,
3053
+ opts.raw.path,
3054
+ opts.strict
3055
+ )
2964
3056
  );
2965
- if (fmIssue) frontmatterIssues.push(fmIssue);
2966
3057
  } else {
2967
- const malformed = detectMalformedFrontmatter(opts.raw.body, opts.raw.path, opts.strict);
2968
- if (malformed) frontmatterIssues.push(malformed);
3058
+ pushIssue(
3059
+ frontmatterIssues,
3060
+ detectMalformedFrontmatter(opts.raw.body, opts.raw.path, opts.strict)
3061
+ );
2969
3062
  }
3063
+ pushIssue(
3064
+ frontmatterIssues,
3065
+ detectUnclosedBacktick(opts.raw.body, opts.raw.path, opts.strict)
3066
+ );
2970
3067
  return { node, frontmatterIssues };
2971
3068
  }
2972
3069
  function mergeNodeWithEnrichments(node, enrichments, opts = {}) {
@@ -4164,4 +4261,4 @@ export {
4164
4261
  runScanWithRenames
4165
4262
  };
4166
4263
  //# sourceMappingURL=index.js.map
4167
- //# debugId=c7e7d754-73eb-5f99-8e19-732a9f84a4c8
4264
+ //# debugId=7667ff07-bdd8-541a-b813-e01e201964f0