@skill-map/cli 0.69.0 → 0.70.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 (38) 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 +46 -27
  19. package/dist/cli/tutorial/sm-tutorial/references/part-basic-daily.md +18 -13
  20. package/dist/cli/tutorial/sm-tutorial/references/part-basic-fundamentals.md +6 -7
  21. package/dist/cli/tutorial/sm-tutorial/references/part-daily-loop.md +97 -26
  22. package/dist/cli/tutorial/sm-tutorial/references/part-fundamentals.md +11 -10
  23. package/dist/cli/tutorial/sm-tutorial/references/part-project-kickoff.md +13 -11
  24. package/dist/cli/tutorial/sm-tutorial/scripts/lib/paths.js +6 -5
  25. package/dist/cli.js +267 -128
  26. package/dist/index.js +9 -7
  27. package/dist/kernel/index.d.ts +27 -0
  28. package/dist/kernel/index.js +9 -7
  29. package/dist/ui/{chunk-RRRXQNG6.js → chunk-EVNCL7FV.js} +21 -21
  30. package/dist/ui/{chunk-E7GLGHVY.js → chunk-GUGB4JY5.js} +1 -1
  31. package/dist/ui/chunk-RJUHQQOF.js +3 -0
  32. package/dist/ui/{chunk-SXSNTF26.js → chunk-RSPEJBPT.js} +1 -1
  33. package/dist/ui/chunk-SQCXHF3J.js +2 -0
  34. package/dist/ui/index.html +1 -1
  35. package/dist/ui/{main-23NGLEUB.js → main-K4O6LCIJ.js} +3 -3
  36. package/package.json +2 -2
  37. package/dist/ui/chunk-RLRSNHYG.js +0 -3
  38. package/dist/ui/chunk-SI4MGFOW.js +0 -2
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // cli/entry.ts
2
2
 
3
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="75d0c2c2-d08f-5d7c-aa62-3e1d53dd4ef1")}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]="ca8113af-a7ac-5cd7-b45c-03a5974bca86")}catch(e){}}();
4
4
  import { existsSync as existsSync34 } from "fs";
5
5
  import { Builtins, Cli as Cli2 } from "clipanion";
6
6
 
@@ -250,7 +250,7 @@ function bucketByKind(kind, instance, bag) {
250
250
  // package.json
251
251
  var package_default = {
252
252
  name: "@skill-map/cli",
253
- version: "0.69.0",
253
+ version: "0.70.0",
254
254
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
255
255
  license: "MIT",
256
256
  type: "module",
@@ -633,7 +633,10 @@ var claudeProvider = {
633
633
  presentation: {
634
634
  label: "Anthropic's Claude",
635
635
  color: "#cc785c",
636
- colorDark: "#e89270"
636
+ colorDark: "#e89270",
637
+ // Claude Code invokes both commands and skills with `/`; the palette
638
+ // paints this as the `invokes` edge glyph under the claude lens.
639
+ invocationSigil: "/"
637
640
  },
638
641
  // Auto-detect marker: a `.claude/` directory under the scope root marks
639
642
  // a Claude Code project. Provider-owned (replaces the old central
@@ -913,13 +916,11 @@ var atDirectiveExtractor = {
913
916
  kind: "extractor",
914
917
  description: "Detects `@<token>` directives in a node's body using Claude Code rules, choosing the link kind by token shape. Example: a bare handle `@team` becomes a `mentions` link, while a file-flavoured token `@docs/api.md` becomes a `references` link.",
915
918
  scope: "body",
916
- // Authorised under the claude AND codex lenses: OpenAI Codex sub-agents
917
- // reference each other with the same `@<name>` mention grammar, and their
918
- // prompt (the TOML `developer_instructions` body, fed in via the codex
919
- // provider's `read.bodyField`) carries the same `@` tokens. The gate is the active
920
- // lens, not the node's provider, so under `codex` this parses `@` across
921
- // the project's markdown surface just as it does under `claude`.
922
- precondition: { provider: ["claude", "codex"] },
919
+ // Claude-only. This is Claude's `@<name>` grammar, where a bare handle is
920
+ // an agent / entity MENTION. OpenAI Codex's `@` is a file-path picker, not
921
+ // a mention grammar, so codex is NOT gated here; the codex-owned `at-file`
922
+ // extractor covers `@`-as-file-reference under the codex lens.
923
+ precondition: { provider: ["claude"] },
923
924
  // eslint-disable-next-line complexity
924
925
  extract(ctx) {
925
926
  const seenMentions = /* @__PURE__ */ new Set();
@@ -1007,16 +1008,15 @@ var slashCommandExtractor = {
1007
1008
  kind: "extractor",
1008
1009
  description: "Turns `/command` invocations in a node's body into arrows that point at the resolved slash command or skill, using Claude Code routing rules. Example: `/deploy` in the body draws an arrow to the `deploy` command.",
1009
1010
  scope: "body",
1010
- // Also authorised under the codex and antigravity lenses, which share the
1011
- // `/command` grammar. Under codex a sub-agent's prompt body (the TOML
1012
- // `developer_instructions` field) has its `/command` tokens parsed for
1013
- // pipeline parity; codex resolves them to its open-standard skills
1014
- // (`invokes: ['skill']`). Under antigravity a workflow / skill / AGENTS.md
1015
- // body's `/name` tokens resolve to BOTH skills and workflows
1016
- // (`invokes: ['skill', 'workflow']`), since Antigravity invokes either by
1017
- // the same slash. A lens that declares no `invokes` resolution leaves the
1018
- // signals unresolved (no spurious edges).
1019
- precondition: { provider: ["claude", "codex", "antigravity"] },
1011
+ // Also authorised under the antigravity lens, which shares the `/command`
1012
+ // grammar: a workflow / skill / AGENTS.md body's `/name` tokens resolve to
1013
+ // BOTH skills and workflows (`invokes: ['skill', 'workflow']`), since
1014
+ // Antigravity invokes either by the same slash. NOT gated under codex:
1015
+ // OpenAI Codex reserves `/` for its OWN built-in commands (`/model`,
1016
+ // `/init`, ...) and invokes user skills with `$` instead (parsed by the
1017
+ // codex `dollar-skill` extractor). A lens that declares no `invokes`
1018
+ // resolution leaves the signals unresolved (no spurious edges).
1019
+ precondition: { provider: ["claude", "antigravity"] },
1020
1020
  extract(ctx) {
1021
1021
  const seen = /* @__PURE__ */ new Set();
1022
1022
  const body = stripCodeAndHtml(ctx.body);
@@ -1241,26 +1241,32 @@ var agentSkillsProvider = {
1241
1241
  stability: "stable",
1242
1242
  // Auto-detect marker: a `.agents/` directory marks an open-standard
1243
1243
  // project. This is also the marker a Google/Antigravity project carries
1244
- // (Antigravity adopted the open standard). The marker only produces an
1245
- // auto-detect candidate once this experimental provider is enabled.
1246
- // Provider-owned.
1247
- detect: { markers: [".agents"] },
1244
+ // (Antigravity adopted the open standard) and the shared skill home a
1245
+ // Codex project populates under `.agents/skills/`. `fallback: true` makes
1246
+ // this candidate yield to any vendor marker present alongside `.agents/`:
1247
+ // a `.codex/` + `.agents/` project resolves `codex` outright, never an
1248
+ // ambiguous `codex` vs `agent-skills` prompt. The `.agents/` marker only
1249
+ // wins when no vendor marker is present. Provider-owned.
1250
+ detect: { markers: [".agents"], fallback: true },
1248
1251
  // Authoring target for `sm tutorial`: the open standard discovers skills
1249
1252
  // under `.agents/skills/<name>/SKILL.md`. `aka` lists Antigravity, which
1250
1253
  // shares this territory AND the BASIC tutorial track (skill + markdown,
1251
1254
  // references), so a tester on Antigravity scaffolds here. OpenAI Codex
1252
1255
  // also reads `.agents/skills/`, but Codex is a RICH-track lens (it has the
1253
- // `agent` kind, slash and `@`), so advertising it under this basic row
1256
+ // `agent` kind, plus `$`-skill invocation and `@`-file references), so
1257
+ // advertising it under this basic row
1254
1258
  // would hand it the wrong book; Codex is surfaced once a Codex rich
1255
1259
  // scaffold target lands. `aka` is display-only, `--for` matches the id.
1256
1260
  scaffold: { skillDir: ".agents/skills", aka: ["Google's Antigravity"] },
1257
1261
  read: COMMONS_READ,
1258
1262
  kinds: COMMONS_KINDS,
1259
1263
  resolution: COMMONS_RESOLUTION,
1260
- // Base reserved-name catalog (self-scope under the `agent-skills` lens). The
1261
- // shared export is inherited by every Provider that adopts the open
1262
- // standard (see `COMMONS_RESERVED_NAMES` above).
1263
- reservedNames: COMMONS_RESERVED_NAMES,
1264
+ // NO `reservedNames`: the neutral open standard has no `/`-invocation, a
1265
+ // skill activates by its `description` and connects via markdown links, so
1266
+ // a skill name cannot shadow a built-in `/` command. The shared
1267
+ // `COMMONS_RESERVED_NAMES` export above is for `/`-invoking vendors
1268
+ // (Antigravity) to spread, NOT applied here. See spec/architecture.md
1269
+ // §Provider · reservedNames.
1264
1270
  classify: classifyCommonsPath
1265
1271
  };
1266
1272
 
@@ -1308,7 +1314,10 @@ var antigravityProvider = {
1308
1314
  presentation: {
1309
1315
  label: "Google's Antigravity",
1310
1316
  color: "#7c3aed",
1311
- colorDark: "#a78bfa"
1317
+ colorDark: "#a78bfa",
1318
+ // Antigravity bolts `/`-invocation onto the open standard (skills and
1319
+ // workflows are `/<name>`), so the `invokes` edge glyph is the slash.
1320
+ invocationSigil: "/"
1312
1321
  },
1313
1322
  // Auto-detect marker: Antigravity's workflows live under `.agent/workflows/`
1314
1323
  // (SINGULAR `.agent`), its one vendor-specific on-disk territory. Skills
@@ -1461,7 +1470,11 @@ var codexProvider = {
1461
1470
  presentation: {
1462
1471
  label: "OpenAI's Codex",
1463
1472
  color: "#22c55e",
1464
- colorDark: "#4ade80"
1473
+ colorDark: "#4ade80",
1474
+ // Codex invokes skills with `$` (`$publish`); `/` is reserved for its
1475
+ // own built-in commands (`/model`, `/init`), so the `invokes` edge glyph
1476
+ // under the codex lens is the dollar, not the slash.
1477
+ invocationSigil: "$"
1465
1478
  },
1466
1479
  // Auto-detect marker: a `.codex/` directory marks a Codex CLI project.
1467
1480
  // `AGENTS.md` is intentionally NOT a marker: it is the open agents.md
@@ -1527,19 +1540,21 @@ var codexProvider = {
1527
1540
  // `frontmatter.name`.
1528
1541
  ...COMMONS_KINDS
1529
1542
  },
1530
- // Mentions resolve to agents (`@<name>`, the Codex sub-agent handle).
1531
- // Slash invocations resolve to skills (`invokes: ['skill']`, inherited
1532
- // from the open standard), so a `/skill-name` in an agent's prompt links
1533
- // to its `.agents/skills/` skill.
1543
+ // Skill invocations resolve to skills (`invokes: ['skill']`, inherited
1544
+ // from the open standard): a `$skill-name` in an agent's prompt (parsed by
1545
+ // the codex `dollar-skill` extractor) links to its `.agents/skills/` skill.
1546
+ // NO `mentions` entry: Codex's `@` is a file picker, parsed by the
1547
+ // codex-owned `at-file` extractor as a path-resolved `references` link, not
1548
+ // an agent-mention grammar.
1534
1549
  resolution: {
1535
- mentions: ["agent"],
1536
1550
  ...COMMONS_RESOLUTION
1537
1551
  },
1538
- // Open-standard reserved-name base (the universal cross-agent slash
1539
- // verbs an agent CLI ships built-in), inherited from `agent-skills` and
1540
- // applied under the codex lens via SELF scope: a user skill that shadows
1541
- // one is flagged by `core/name-reserved`.
1542
- reservedNames: COMMONS_RESERVED_NAMES,
1552
+ // NO `reservedNames`: Codex invokes skills via `$` (the `dollar-skill`
1553
+ // extractor), a namespace disjoint from its built-in `/` commands, so a
1554
+ // `$`-skill named `model` does NOT collide with `/model`. Reserved-skill
1555
+ // names only apply to lenses that invoke skills through the `/` command
1556
+ // channel (claude, antigravity). See spec/architecture.md §Provider ·
1557
+ // reservedNames.
1543
1558
  classify(path) {
1544
1559
  const lower = path.toLowerCase();
1545
1560
  if (lower.startsWith(".codex/agents/") && lower.endsWith(".toml")) return "agent";
@@ -1547,6 +1562,123 @@ var codexProvider = {
1547
1562
  }
1548
1563
  };
1549
1564
 
1565
+ // plugins/codex/extractors/at-file/index.ts
1566
+ import { posix as pathPosix2 } from "path";
1567
+ var ID4 = "at-file";
1568
+ var AT_RE2 = /(?:^|[^A-Za-z0-9_@])(@(?:\.{1,2}\/|\/)?[a-z0-9](?:[a-z0-9_\-./]*[a-z0-9_])?(?::[a-z0-9][a-z0-9_-]*)?)/gi;
1569
+ var FILE_EXT_RE2 = /\.(md|mdx|js|jsx|ts|tsx|json|yml|yaml|toml|txt|html|css|scss|less|py|rb|go|rs|java|c|cpp|h|hpp|sh|sql|svg|png|jpg|jpeg|gif|webp|pdf)$/i;
1570
+ var atFileExtractor = {
1571
+ id: ID4,
1572
+ pluginId: OPENAI_PLUGIN_ID,
1573
+ kind: "extractor",
1574
+ description: "Detects `@<file>` references in a node's body under the OpenAI Codex lens, where `@` is a file picker. A path- or extension-shaped token becomes a `references` link to that file; a bare `@handle` forms no edge. Example: `@builder.toml` in an agent's prompt draws an arrow to the `builder` agent file.",
1575
+ scope: "body",
1576
+ // Codex-only: under the codex lens `@` is a file-path picker, so only
1577
+ // file-shaped tokens form references. The claude `at-directive` (bare
1578
+ // `@handle` → agent mention) is NOT gated under codex.
1579
+ precondition: { provider: ["codex"] },
1580
+ extract(ctx) {
1581
+ const seenReferences = /* @__PURE__ */ new Set();
1582
+ const body = stripCodeAndHtml(ctx.body);
1583
+ const lineStarts = computeLineStarts(body);
1584
+ const sourceDir = pathPosix2.dirname(ctx.node.path);
1585
+ for (const match of body.matchAll(AT_RE2)) {
1586
+ const original = match[1];
1587
+ const bare = original.slice(1);
1588
+ const rationale = classifyAtToken(bare);
1589
+ if (rationale === null) continue;
1590
+ const target = resolveSourceRelative2(sourceDir, bare);
1591
+ const dedupKey = target.toLowerCase();
1592
+ if (seenReferences.has(dedupKey)) continue;
1593
+ seenReferences.add(dedupKey);
1594
+ const captureOffset = (match.index ?? 0) + match[0].indexOf(original);
1595
+ const line = lineFor(lineStarts, captureOffset);
1596
+ ctx.emitSignal({
1597
+ source: ctx.node.path,
1598
+ scope: "body",
1599
+ range: { start: captureOffset, end: captureOffset + original.length, line },
1600
+ raw: original,
1601
+ candidates: [
1602
+ {
1603
+ extractorId: ID4,
1604
+ kind: "references",
1605
+ target,
1606
+ // 0.85: strong file signal (path prefix or known extension),
1607
+ // one degree of inference (the runtime resolves the path).
1608
+ confidence: 0.85,
1609
+ rationale,
1610
+ trigger: {
1611
+ originalTrigger: original,
1612
+ normalizedTrigger: target
1613
+ }
1614
+ }
1615
+ ]
1616
+ });
1617
+ }
1618
+ }
1619
+ };
1620
+ function classifyAtToken(bare) {
1621
+ if (bare.startsWith("/")) return null;
1622
+ if (bare.startsWith("./") || bare.startsWith("../")) return "relative path prefix";
1623
+ if (FILE_EXT_RE2.test(bare)) return "known file extension";
1624
+ return null;
1625
+ }
1626
+ function resolveSourceRelative2(sourceDir, bare) {
1627
+ const joined = sourceDir === "." ? bare : `${sourceDir}/${bare}`;
1628
+ return pathPosix2.normalize(joined);
1629
+ }
1630
+
1631
+ // plugins/codex/extractors/dollar-skill/index.ts
1632
+ var ID5 = "dollar-skill";
1633
+ var DOLLAR_RE = /(?<![A-Za-z0-9_$])(\$[a-z][a-z0-9_-]*)/g;
1634
+ var dollarSkillExtractor = {
1635
+ id: ID5,
1636
+ pluginId: OPENAI_PLUGIN_ID,
1637
+ kind: "extractor",
1638
+ description: "Turns `$skill` invocations in a node's body into arrows that point at the resolved Codex skill, using OpenAI Codex routing rules. Example: `$check-links` in the body draws an arrow to the `check-links` skill.",
1639
+ scope: "body",
1640
+ // Codex-only: `$skill` is OpenAI Codex's explicit skill-invocation
1641
+ // grammar. The codex provider resolves it to its open-standard skills
1642
+ // (`invokes: ['skill']`). Other lenses do not parse `$`.
1643
+ precondition: { provider: ["codex"] },
1644
+ extract(ctx) {
1645
+ const seen = /* @__PURE__ */ new Set();
1646
+ const body = stripCodeAndHtml(ctx.body);
1647
+ const lineStarts = computeLineStarts(body);
1648
+ for (const match of body.matchAll(DOLLAR_RE)) {
1649
+ const original = match[1];
1650
+ const normalized = normalizeTrigger(original);
1651
+ if (seen.has(normalized)) continue;
1652
+ seen.add(normalized);
1653
+ const captureOffset = (match.index ?? 0) + match[0].indexOf(original);
1654
+ const line = lineFor(lineStarts, captureOffset);
1655
+ ctx.emitSignal({
1656
+ source: ctx.node.path,
1657
+ scope: "body",
1658
+ range: { start: captureOffset, end: captureOffset + original.length, line },
1659
+ raw: original,
1660
+ candidates: [
1661
+ {
1662
+ extractorId: ID5,
1663
+ kind: "invokes",
1664
+ target: original,
1665
+ // 0.8: clean `$skill` match after code-block strip. The
1666
+ // lowercase-letter guard filters currency / env-var noise, so a
1667
+ // hit is unambiguous syntax. Resolution against the live skill
1668
+ // catalog happens downstream.
1669
+ confidence: 0.8,
1670
+ rationale: "unambiguous $skill syntax post code-block strip",
1671
+ trigger: {
1672
+ originalTrigger: original,
1673
+ normalizedTrigger: normalized
1674
+ }
1675
+ }
1676
+ ]
1677
+ });
1678
+ }
1679
+ }
1680
+ };
1681
+
1550
1682
  // plugins/core/providers/core-markdown/schemas/markdown.schema.json
1551
1683
  var markdown_schema_default = {
1552
1684
  $schema: "https://json-schema.org/draft/2020-12/schema",
@@ -1634,11 +1766,11 @@ var coreMarkdownProvider = {
1634
1766
  };
1635
1767
 
1636
1768
  // plugins/core/extractors/backtick-path/index.ts
1637
- import { posix as pathPosix2 } from "path";
1638
- var ID4 = "backtick-path";
1769
+ import { posix as pathPosix3 } from "path";
1770
+ var ID6 = "backtick-path";
1639
1771
  var PATH_RE = /(?<![\w/:.-])(?:\.{1,2}\/)?[\w][\w.-]*(?:\/[\w.-]+)*\.md\b(?![\w/])/g;
1640
1772
  var backtickPathExtractor = {
1641
- id: ID4,
1773
+ id: ID6,
1642
1774
  pluginId: CORE_PLUGIN_ID,
1643
1775
  kind: "extractor",
1644
1776
  description: "Turns relative .md paths written inside code spans and fenced blocks into arrows between nodes in the graph. Example: a backticked `references/rules.md` path draws an arrow to that file.",
@@ -1647,7 +1779,7 @@ var backtickPathExtractor = {
1647
1779
  const seen = /* @__PURE__ */ new Set();
1648
1780
  const body = extractCodeRegions(ctx.body);
1649
1781
  const lineStarts = computeLineStarts(body);
1650
- const sourceDir = pathPosix2.dirname(ctx.node.path);
1782
+ const sourceDir = pathPosix3.dirname(ctx.node.path);
1651
1783
  for (const match of body.matchAll(PATH_RE)) {
1652
1784
  const original = match[0];
1653
1785
  const resolved = resolveTarget(sourceDir, original);
@@ -1663,7 +1795,7 @@ var backtickPathExtractor = {
1663
1795
  raw: original,
1664
1796
  candidates: [
1665
1797
  {
1666
- extractorId: ID4,
1798
+ extractorId: ID6,
1667
1799
  kind: "points",
1668
1800
  target: resolved,
1669
1801
  // 0.85: a strong file signal with one degree of inference,
@@ -1688,11 +1820,11 @@ function resolveTarget(sourceDir, raw) {
1688
1820
  if (trimmed.length === 0) return null;
1689
1821
  if (trimmed.startsWith("/")) return null;
1690
1822
  const joined = sourceDir === "." ? trimmed : `${sourceDir}/${trimmed}`;
1691
- return pathPosix2.normalize(joined);
1823
+ return pathPosix3.normalize(joined);
1692
1824
  }
1693
1825
 
1694
1826
  // plugins/core/extractors/external-url-counter/index.ts
1695
- var ID5 = "external-url-counter";
1827
+ var ID7 = "external-url-counter";
1696
1828
  var count2 = {
1697
1829
  slot: "card.footer.left",
1698
1830
  icon: "pi-link",
@@ -1712,7 +1844,7 @@ var settings = {
1712
1844
  var URL_RE = /https?:\/\/[^\s<>"'`)\]]+/g;
1713
1845
  var TRAILING_PUNCT = /[.,;:!?]+$/;
1714
1846
  var externalUrlCounterExtractor = {
1715
- id: ID5,
1847
+ id: ID7,
1716
1848
  pluginId: CORE_PLUGIN_ID,
1717
1849
  kind: "extractor",
1718
1850
  description: "Counts the distinct external URLs in a node's body and shows the count on the card. Example: a body linking `https://example.com` and `https://docs.rs` shows a count of 2.",
@@ -1764,7 +1896,7 @@ var externalUrlCounterExtractor = {
1764
1896
  raw: original,
1765
1897
  candidates: [
1766
1898
  {
1767
- extractorId: ID5,
1899
+ extractorId: ID7,
1768
1900
  kind: "references",
1769
1901
  target: normalized.href,
1770
1902
  confidence: 0.3,
@@ -1805,12 +1937,12 @@ function normalizeUrl(raw) {
1805
1937
  }
1806
1938
 
1807
1939
  // plugins/core/extractors/markdown-link/index.ts
1808
- import { posix as pathPosix3 } from "path";
1809
- var ID6 = "markdown-link";
1940
+ import { posix as pathPosix4 } from "path";
1941
+ var ID8 = "markdown-link";
1810
1942
  var LINK_RE = /(?<!!)\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
1811
1943
  var URL_SCHEME_RE = /^[a-z][a-z0-9+.-]*:/i;
1812
1944
  var markdownLinkExtractor = {
1813
- id: ID6,
1945
+ id: ID8,
1814
1946
  pluginId: CORE_PLUGIN_ID,
1815
1947
  kind: "extractor",
1816
1948
  description: "Turns markdown links (`[text](path)`) in a node's body into arrows between nodes in the graph. Example: `[the guide](docs/guide.md)` draws an arrow to `docs/guide.md`.",
@@ -1819,7 +1951,7 @@ var markdownLinkExtractor = {
1819
1951
  const seen = /* @__PURE__ */ new Set();
1820
1952
  const body = stripCodeAndHtml(ctx.body);
1821
1953
  const lineStarts = computeLineStarts(body);
1822
- const sourceDir = pathPosix3.dirname(ctx.node.path);
1954
+ const sourceDir = pathPosix4.dirname(ctx.node.path);
1823
1955
  for (const match of body.matchAll(LINK_RE)) {
1824
1956
  const original = match[2];
1825
1957
  const resolved = resolveTarget2(sourceDir, original);
@@ -1835,7 +1967,7 @@ var markdownLinkExtractor = {
1835
1967
  raw: match[0],
1836
1968
  candidates: [
1837
1969
  {
1838
- extractorId: ID6,
1970
+ extractorId: ID8,
1839
1971
  kind: "references",
1840
1972
  target: resolved,
1841
1973
  // 0.95: the `[text](path)` syntax is unambiguous (the spec's
@@ -1866,14 +1998,14 @@ function resolveTarget2(sourceDir, raw) {
1866
1998
  if (URL_SCHEME_RE.test(trimmed)) return null;
1867
1999
  if (trimmed.startsWith("/")) return null;
1868
2000
  const joined = sourceDir === "." ? trimmed : `${sourceDir}/${trimmed}`;
1869
- return pathPosix3.normalize(joined);
2001
+ return pathPosix4.normalize(joined);
1870
2002
  }
1871
2003
 
1872
2004
  // plugins/core/extractors/mcp-tools/index.ts
1873
- var ID7 = "mcp-tools";
2005
+ var ID9 = "mcp-tools";
1874
2006
  var MCP_PATTERN = /^mcp__([a-z0-9][a-z0-9_-]*)__[a-z0-9_-]+$/i;
1875
2007
  var mcpToolsExtractor = {
1876
- id: ID7,
2008
+ id: ID9,
1877
2009
  pluginId: CORE_PLUGIN_ID,
1878
2010
  kind: "extractor",
1879
2011
  description: "Turns `tools: [mcp__<server>__<tool>]` entries in a node's frontmatter into an MCP node per unique server and an arrow from the source to each one. Example: `tools: [mcp__github__create_pr]` adds an `mcp://github` node and an arrow to it.",
@@ -1904,7 +2036,7 @@ var mcpToolsExtractor = {
1904
2036
  raw: `mcp__${server}__*`,
1905
2037
  candidates: [
1906
2038
  {
1907
- extractorId: ID7,
2039
+ extractorId: ID9,
1908
2040
  kind: "references",
1909
2041
  target: mcpPath,
1910
2042
  confidence: 0.85,
@@ -1973,10 +2105,10 @@ var ANNOTATION_FIELD_UNKNOWN_TEXTS = {
1973
2105
  };
1974
2106
 
1975
2107
  // plugins/core/analyzers/annotation-field-unknown/index.ts
1976
- var ID8 = "annotation-field-unknown";
2108
+ var ID10 = "annotation-field-unknown";
1977
2109
  var RESERVED_ROOT_BLOCKS = /* @__PURE__ */ new Set(["identity", "annotations", "settings", "audit"]);
1978
2110
  var annotationFieldUnknownAnalyzer = {
1979
- id: ID8,
2111
+ id: ID10,
1980
2112
  pluginId: CORE_PLUGIN_ID,
1981
2113
  kind: "analyzer",
1982
2114
  description: "Flags typos or unrecognized keys in sidecars (`.sm`).",
@@ -2011,7 +2143,7 @@ var annotationFieldUnknownAnalyzer = {
2011
2143
  for (const key of Object.keys(annotations)) {
2012
2144
  if (!knownAnnotationKeys.has(key)) {
2013
2145
  issues.push({
2014
- analyzerId: ID8,
2146
+ analyzerId: ID10,
2015
2147
  severity: "warn",
2016
2148
  nodeIds: [node.path],
2017
2149
  message: formatFinding({
@@ -2037,7 +2169,7 @@ var annotationFieldUnknownAnalyzer = {
2037
2169
  if (validator(value)) continue;
2038
2170
  const errors = (validator.errors ?? []).map((e) => `${e.instancePath || "(root)"} ${e.message ?? e.keyword}`).join("; ");
2039
2171
  issues.push({
2040
- analyzerId: ID8,
2172
+ analyzerId: ID10,
2041
2173
  severity: "warn",
2042
2174
  nodeIds: [node.path],
2043
2175
  message: formatFinding({
@@ -2052,7 +2184,7 @@ var annotationFieldUnknownAnalyzer = {
2052
2184
  continue;
2053
2185
  }
2054
2186
  issues.push({
2055
- analyzerId: ID8,
2187
+ analyzerId: ID10,
2056
2188
  severity: "warn",
2057
2189
  nodeIds: [node.path],
2058
2190
  message: formatFinding({
@@ -2124,9 +2256,9 @@ var ANNOTATION_ORPHAN_TEXTS = {
2124
2256
  };
2125
2257
 
2126
2258
  // plugins/core/analyzers/annotation-orphan/index.ts
2127
- var ID9 = "annotation-orphan";
2259
+ var ID11 = "annotation-orphan";
2128
2260
  var annotationOrphanAnalyzer = {
2129
- id: ID9,
2261
+ id: ID11,
2130
2262
  pluginId: CORE_PLUGIN_ID,
2131
2263
  kind: "analyzer",
2132
2264
  description: "Flags sidecars (`.sm`) whose `.md` file no longer exists.",
@@ -2138,7 +2270,7 @@ var annotationOrphanAnalyzer = {
2138
2270
  for (const orphan of orphans) {
2139
2271
  const expectedMdRelative = orphan.relativePath.endsWith(".sm") ? `${orphan.relativePath.slice(0, -".sm".length)}.md` : `${orphan.relativePath}.md`;
2140
2272
  issues.push({
2141
- analyzerId: ID9,
2273
+ analyzerId: ID11,
2142
2274
  severity: "warn",
2143
2275
  nodeIds: [expectedMdRelative],
2144
2276
  message: formatFinding({
@@ -2181,7 +2313,7 @@ var ANNOTATION_STALE_TEXTS = {
2181
2313
  };
2182
2314
 
2183
2315
  // plugins/core/analyzers/annotation-stale/index.ts
2184
- var ID10 = "annotation-stale";
2316
+ var ID12 = "annotation-stale";
2185
2317
  var staleIcon = {
2186
2318
  slot: "card.footer.right",
2187
2319
  icon: "pi-clock",
@@ -2194,7 +2326,7 @@ var staleBadge = {
2194
2326
  priority: 20
2195
2327
  };
2196
2328
  var annotationStaleAnalyzer = {
2197
- id: ID10,
2329
+ id: ID12,
2198
2330
  pluginId: CORE_PLUGIN_ID,
2199
2331
  kind: "analyzer",
2200
2332
  description: "Marks sidecars (`.sm`) that are out of date with their `.md`.",
@@ -2214,7 +2346,7 @@ var annotationStaleAnalyzer = {
2214
2346
  const status = staleStatus(node.sidecar);
2215
2347
  if (status === null) continue;
2216
2348
  issues.push({
2217
- analyzerId: ID10,
2349
+ analyzerId: ID12,
2218
2350
  severity: "info",
2219
2351
  nodeIds: [node.path],
2220
2352
  message: formatFinding({ body: messageFor(status) }),
@@ -2260,9 +2392,9 @@ function tooltipFor(status) {
2260
2392
  }
2261
2393
 
2262
2394
  // plugins/core/analyzers/contribution-orphan/index.ts
2263
- var ID11 = "contribution-orphan";
2395
+ var ID13 = "contribution-orphan";
2264
2396
  var contributionOrphanAnalyzer = {
2265
- id: ID11,
2397
+ id: ID13,
2266
2398
  pluginId: CORE_PLUGIN_ID,
2267
2399
  kind: "analyzer",
2268
2400
  description: "Warns about plugin data referencing nodes renamed or deleted in the latest scan.",
@@ -2294,12 +2426,12 @@ var EXTRACTOR_COLLISION_TEXTS = {
2294
2426
  };
2295
2427
 
2296
2428
  // plugins/core/analyzers/extractor-collision/index.ts
2297
- var ID12 = "extractor-collision";
2429
+ var ID14 = "extractor-collision";
2298
2430
  function signalLines(signal) {
2299
2431
  return signal.range && typeof signal.range.line === "number" ? [signal.range.line] : void 0;
2300
2432
  }
2301
2433
  var extractorCollisionAnalyzer = {
2302
- id: ID12,
2434
+ id: ID14,
2303
2435
  pluginId: CORE_PLUGIN_ID,
2304
2436
  kind: "analyzer",
2305
2437
  description: "Reports when two extractors detect something at the same span of body text and the resolver drops one.",
@@ -2323,7 +2455,7 @@ function makeIssue(signal) {
2323
2455
  const loserRange = signal.range ? `${signal.range.start}-${signal.range.end}` : "unknown";
2324
2456
  const winnerRange = `${winner.range.start}-${winner.range.end}`;
2325
2457
  return {
2326
- analyzerId: ID12,
2458
+ analyzerId: ID14,
2327
2459
  severity: "warn",
2328
2460
  nodeIds: [signal.source],
2329
2461
  message: formatFinding({
@@ -2367,7 +2499,7 @@ var ISSUE_COUNTER_TEXTS = {
2367
2499
  };
2368
2500
 
2369
2501
  // plugins/core/analyzers/issue-counter/index.ts
2370
- var ID13 = "issue-counter";
2502
+ var ID15 = "issue-counter";
2371
2503
  var warnCount = {
2372
2504
  slot: "card.footer.right",
2373
2505
  icon: "pi-exclamation-triangle",
@@ -2403,7 +2535,7 @@ function emitTierChips(ctx, ref, severity, counts, singleTooltip, manyTooltip) {
2403
2535
  }
2404
2536
  }
2405
2537
  var issueCounterAnalyzer = {
2406
- id: ID13,
2538
+ id: ID15,
2407
2539
  pluginId: CORE_PLUGIN_ID,
2408
2540
  kind: "analyzer",
2409
2541
  description: "Emits one aggregate severity chip per node (error + warn counts) from the live issue accumulator.",
@@ -2466,7 +2598,7 @@ var LINK_COUNTER_TEXTS = {
2466
2598
  };
2467
2599
 
2468
2600
  // plugins/core/analyzers/link-counter/index.ts
2469
- var ID14 = "link-counter";
2601
+ var ID16 = "link-counter";
2470
2602
  var linksIn = {
2471
2603
  slot: "card.footer.left",
2472
2604
  icon: "pi-download",
@@ -2482,7 +2614,7 @@ var linksOut = {
2482
2614
  priority: 20
2483
2615
  };
2484
2616
  var linkCounterAnalyzer = {
2485
- id: ID14,
2617
+ id: ID16,
2486
2618
  pluginId: CORE_PLUGIN_ID,
2487
2619
  kind: "analyzer",
2488
2620
  description: "Counts incoming and outgoing links per node.",
@@ -2547,10 +2679,10 @@ var LINK_KIND_CONFLICT_TEXTS = {
2547
2679
  };
2548
2680
 
2549
2681
  // plugins/core/analyzers/link-kind-conflict/index.ts
2550
- var ID15 = "link-kind-conflict";
2682
+ var ID17 = "link-kind-conflict";
2551
2683
  var NON_CONFLICTING_KINDS = /* @__PURE__ */ new Set(["points"]);
2552
2684
  var linkKindConflictAnalyzer = {
2553
- id: ID15,
2685
+ id: ID17,
2554
2686
  pluginId: CORE_PLUGIN_ID,
2555
2687
  kind: "analyzer",
2556
2688
  description: "Flags conflicting arrow meanings between extractors (e.g. `references` vs `invokes`).",
@@ -2598,7 +2730,7 @@ var linkKindConflictAnalyzer = {
2598
2730
  const [source, target] = key.split("\0");
2599
2731
  const kindList = variants.map((v) => v.kind).join(" / ");
2600
2732
  issues.push({
2601
- analyzerId: ID15,
2733
+ analyzerId: ID17,
2602
2734
  severity: "warn",
2603
2735
  nodeIds: [source, target],
2604
2736
  message: formatFinding({
@@ -2637,9 +2769,9 @@ var LINK_SELF_LOOP_TEXTS = {
2637
2769
  };
2638
2770
 
2639
2771
  // plugins/core/analyzers/link-self-loop/index.ts
2640
- var ID16 = "link-self-loop";
2772
+ var ID18 = "link-self-loop";
2641
2773
  var linkSelfLoopAnalyzer = {
2642
- id: ID16,
2774
+ id: ID18,
2643
2775
  pluginId: CORE_PLUGIN_ID,
2644
2776
  kind: "analyzer",
2645
2777
  description: "Flags links whose source is also their own resolved target (e.g. a body heading like `# /deploy` inside the file that defines `/deploy`).",
@@ -2649,7 +2781,7 @@ var linkSelfLoopAnalyzer = {
2649
2781
  for (const link of ctx.links) {
2650
2782
  if (!isSelfLoop(link)) continue;
2651
2783
  issues.push({
2652
- analyzerId: ID16,
2784
+ analyzerId: ID18,
2653
2785
  severity: "warn",
2654
2786
  nodeIds: [link.source],
2655
2787
  message: formatFinding({
@@ -2681,9 +2813,9 @@ var NAME_COLLISION_TEXTS = {
2681
2813
  };
2682
2814
 
2683
2815
  // plugins/core/analyzers/name-collision/index.ts
2684
- var ID17 = "name-collision";
2816
+ var ID19 = "name-collision";
2685
2817
  var nameCollisionAnalyzer = {
2686
- id: ID17,
2818
+ id: ID19,
2687
2819
  pluginId: CORE_PLUGIN_ID,
2688
2820
  kind: "analyzer",
2689
2821
  mode: "deterministic",
@@ -2697,7 +2829,7 @@ var nameCollisionAnalyzer = {
2697
2829
  for (const [name, claims] of collisions) {
2698
2830
  const paths = claims.map((c) => c.path);
2699
2831
  issues.push({
2700
- analyzerId: ID17,
2832
+ analyzerId: ID19,
2701
2833
  severity: "error",
2702
2834
  nodeIds: paths,
2703
2835
  message: formatFinding({
@@ -2747,9 +2879,9 @@ var NAME_RESERVED_TEXTS = {
2747
2879
  };
2748
2880
 
2749
2881
  // plugins/core/analyzers/name-reserved/index.ts
2750
- var ID18 = "name-reserved";
2882
+ var ID20 = "name-reserved";
2751
2883
  var nameReservedAnalyzer = {
2752
- id: ID18,
2884
+ id: ID20,
2753
2885
  pluginId: CORE_PLUGIN_ID,
2754
2886
  kind: "analyzer",
2755
2887
  description: "Flags two kinds of reserved-name collision: a file whose name shadows a built-in command of the active runtime, and a link that resolves to one of those reserved names.",
@@ -2767,7 +2899,7 @@ var nameReservedAnalyzer = {
2767
2899
  const node = byPath3.get(path);
2768
2900
  if (!node) continue;
2769
2901
  issues.push({
2770
- analyzerId: ID18,
2902
+ analyzerId: ID20,
2771
2903
  severity: "warn",
2772
2904
  nodeIds: [node.path],
2773
2905
  message: formatFinding({
@@ -2789,7 +2921,7 @@ var nameReservedAnalyzer = {
2789
2921
  adjust(link, { kind: "delta", value: -RESERVED_PENALTY });
2790
2922
  }
2791
2923
  issues.push({
2792
- analyzerId: ID18,
2924
+ analyzerId: ID20,
2793
2925
  severity: "warn",
2794
2926
  nodeIds: [link.source],
2795
2927
  message: formatFinding({
@@ -2841,7 +2973,7 @@ var NODE_STABILITY_TEXTS = {
2841
2973
  };
2842
2974
 
2843
2975
  // plugins/core/analyzers/node-stability/index.ts
2844
- var ID19 = "node-stability";
2976
+ var ID21 = "node-stability";
2845
2977
  var EXPERIMENTAL_TOOLTIP = "Experimental: API may change";
2846
2978
  var DEPRECATED_TOOLTIP = "Deprecated: avoid in new code";
2847
2979
  var experimental = {
@@ -2859,7 +2991,7 @@ var deprecated = {
2859
2991
  priority: 10
2860
2992
  };
2861
2993
  var nodeStabilityAnalyzer = {
2862
- id: ID19,
2994
+ id: ID21,
2863
2995
  pluginId: CORE_PLUGIN_ID,
2864
2996
  kind: "analyzer",
2865
2997
  description: "Surfaces a node's stability stage on the card: `deprecated` as a chip plus a finding, `experimental` as a chip only; `stable` and unset stay silent.",
@@ -2881,7 +3013,7 @@ var nodeStabilityAnalyzer = {
2881
3013
  severity: "warn"
2882
3014
  });
2883
3015
  issues.push({
2884
- analyzerId: ID19,
3016
+ analyzerId: ID21,
2885
3017
  severity: "warn",
2886
3018
  nodeIds: [node.path],
2887
3019
  message: formatFinding({ body: tx(NODE_STABILITY_TEXTS.deprecated) }),
@@ -2933,9 +3065,9 @@ var REFERENCE_BROKEN_TEXTS = {
2933
3065
  };
2934
3066
 
2935
3067
  // plugins/core/analyzers/reference-broken/index.ts
2936
- var ID20 = "reference-broken";
3068
+ var ID22 = "reference-broken";
2937
3069
  var referenceBrokenAnalyzer = {
2938
- id: ID20,
3070
+ id: ID22,
2939
3071
  pluginId: CORE_PLUGIN_ID,
2940
3072
  kind: "analyzer",
2941
3073
  description: "Flags arrows pointing at a node not part of the current scan.",
@@ -2978,7 +3110,7 @@ function penalizeBrokenConfidence(adjust, link) {
2978
3110
  }
2979
3111
  function buildIssue(link) {
2980
3112
  return {
2981
- analyzerId: ID20,
3113
+ analyzerId: ID22,
2982
3114
  // `error`, not `warn`: a link whose target is not in the scan is a
2983
3115
  // structural defect the operator must notice, and the card chip
2984
3116
  // paints `danger` (red) to match. Per the chip-vs-issue policy in
@@ -3042,9 +3174,9 @@ var REFERENCE_REDUNDANT_TEXTS = {
3042
3174
  };
3043
3175
 
3044
3176
  // plugins/core/analyzers/reference-redundant/index.ts
3045
- var ID21 = "reference-redundant";
3177
+ var ID23 = "reference-redundant";
3046
3178
  var referenceRedundantAnalyzer = {
3047
- id: ID21,
3179
+ id: ID23,
3048
3180
  pluginId: CORE_PLUGIN_ID,
3049
3181
  kind: "analyzer",
3050
3182
  description: "Flags when one node references the same target through two or more different links (e.g. a markdown link plus a `references:` entry).",
@@ -3067,7 +3199,7 @@ var referenceRedundantAnalyzer = {
3067
3199
  const [source, resolvedTarget] = key.split("\0");
3068
3200
  const flat = flattenOccurrences(links);
3069
3201
  issues.push({
3070
- analyzerId: ID21,
3202
+ analyzerId: ID23,
3071
3203
  severity: "info",
3072
3204
  nodeIds: [source],
3073
3205
  message: formatFinding({
@@ -3403,9 +3535,9 @@ var SCHEMA_VIOLATION_TEXTS = {
3403
3535
  };
3404
3536
 
3405
3537
  // plugins/core/analyzers/schema-violation/index.ts
3406
- var ID22 = "schema-violation";
3538
+ var ID24 = "schema-violation";
3407
3539
  var schemaViolationAnalyzer = {
3408
- id: ID22,
3540
+ id: ID24,
3409
3541
  pluginId: CORE_PLUGIN_ID,
3410
3542
  kind: "analyzer",
3411
3543
  description: "Flags nodes or links that violate the project schemas.",
@@ -3455,7 +3587,7 @@ function collectNodeFindings(v, node, out) {
3455
3587
  const result = v.validate("node", toNodeForSchema(node));
3456
3588
  if (result.ok) return;
3457
3589
  out.push({
3458
- analyzerId: ID22,
3590
+ analyzerId: ID24,
3459
3591
  severity: "error",
3460
3592
  nodeIds: [node.path],
3461
3593
  message: formatFinding({
@@ -3470,7 +3602,7 @@ function collectLinkFindings(v, link, out) {
3470
3602
  const result = v.validate("link", toLinkForSchema(link));
3471
3603
  if (result.ok) return;
3472
3604
  out.push({
3473
- analyzerId: ID22,
3605
+ analyzerId: ID24,
3474
3606
  severity: "error",
3475
3607
  nodeIds: [link.source],
3476
3608
  message: formatFinding({
@@ -3539,13 +3671,13 @@ var ASCII_FORMATTER_TEXTS = {
3539
3671
  };
3540
3672
 
3541
3673
  // plugins/core/formatters/ascii/index.ts
3542
- var ID23 = "ascii";
3674
+ var ID25 = "ascii";
3543
3675
  var KIND_ORDER = ["agent", "command", "skill", "markdown"];
3544
3676
  var asciiFormatter = {
3545
- id: ID23,
3677
+ id: ID25,
3546
3678
  pluginId: CORE_PLUGIN_ID,
3547
3679
  kind: "formatter",
3548
- formatId: ID23,
3680
+ formatId: ID25,
3549
3681
  description: "Renders the scan as plain text in three sections: nodes (grouped by kind), arrows, and issues. Used by `sm scan --format ascii`.",
3550
3682
  // ASCII tree formatter, header + per-kind sections + per-issue
3551
3683
  // section. Each section iterates and renders; splitting per section
@@ -3639,13 +3771,13 @@ function renderSection(out, kind, group) {
3639
3771
  }
3640
3772
 
3641
3773
  // plugins/core/formatters/json/index.ts
3642
- var ID24 = "json";
3774
+ var ID26 = "json";
3643
3775
  var jsonFormatter = {
3644
- id: ID24,
3776
+ id: ID26,
3645
3777
  pluginId: CORE_PLUGIN_ID,
3646
3778
  kind: "formatter",
3647
3779
  description: "Renders the persisted scan as JSON (conforms to `scan-result.schema.json`). Used by `sm graph --format json` and `GET /api/graph?format=json`.",
3648
- formatId: ID24,
3780
+ formatId: ID26,
3649
3781
  format(ctx) {
3650
3782
  if (ctx.scanResult !== void 0) {
3651
3783
  return JSON.stringify(ctx.scanResult);
@@ -3792,13 +3924,13 @@ var BUMP_TEXTS = {
3792
3924
  };
3793
3925
 
3794
3926
  // plugins/core/actions/node-bump/index.ts
3795
- var ID25 = "node-bump";
3927
+ var ID27 = "node-bump";
3796
3928
  var bumpButton = {
3797
3929
  slot: "inspector.action.button",
3798
3930
  priority: 10
3799
3931
  };
3800
3932
  var nodeBumpAction = {
3801
- id: ID25,
3933
+ id: ID27,
3802
3934
  pluginId: CORE_PLUGIN_ID,
3803
3935
  kind: "action",
3804
3936
  description: "Marks a node as updated: bumps `annotations.version`, refreshes sidecar hashes, and records the timestamp.",
@@ -3901,13 +4033,13 @@ var NODE_SET_STABILITY_TEXTS = {
3901
4033
  };
3902
4034
 
3903
4035
  // plugins/core/actions/node-set-stability/index.ts
3904
- var ID26 = "node-set-stability";
4036
+ var ID28 = "node-set-stability";
3905
4037
  var setStabilityButton = {
3906
4038
  slot: "inspector.action.button",
3907
4039
  priority: 15
3908
4040
  };
3909
4041
  var nodeSetStabilityAction = {
3910
- id: ID26,
4042
+ id: ID28,
3911
4043
  pluginId: CORE_PLUGIN_ID,
3912
4044
  kind: "action",
3913
4045
  description: "Sets the lifecycle stage of the current node (writes `stability` to the sidecar).",
@@ -3979,9 +4111,9 @@ function invokeSetStability(input, ctx) {
3979
4111
  }
3980
4112
 
3981
4113
  // plugins/core/actions/node-set-tags/index.ts
3982
- var ID27 = "node-set-tags";
4114
+ var ID29 = "node-set-tags";
3983
4115
  var nodeSetTagsAction = {
3984
- id: ID27,
4116
+ id: ID29,
3985
4117
  pluginId: CORE_PLUGIN_ID,
3986
4118
  kind: "action",
3987
4119
  description: "Sets the taxonomy tags of the current node (writes `tags` to the sidecar; whole-array replace).",
@@ -4523,6 +4655,8 @@ var slashCommandExtractor2 = { ...slashCommandExtractor, pluginId: "claude", ver
4523
4655
  var toolsCounterExtractor2 = { ...toolsCounterExtractor, pluginId: "claude", version: VERSION };
4524
4656
  var antigravityProvider2 = { ...antigravityProvider, pluginId: "antigravity", version: VERSION };
4525
4657
  var codexProvider2 = { ...codexProvider, pluginId: "codex", version: VERSION };
4658
+ var atFileExtractor2 = { ...atFileExtractor, pluginId: "codex", version: VERSION };
4659
+ var dollarSkillExtractor2 = { ...dollarSkillExtractor, pluginId: "codex", version: VERSION };
4526
4660
  var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills", version: VERSION };
4527
4661
  var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core", version: VERSION };
4528
4662
  var backtickPathExtractor2 = { ...backtickPathExtractor, pluginId: "core", version: VERSION };
@@ -4572,7 +4706,9 @@ var builtInPlugins = [
4572
4706
  id: "codex",
4573
4707
  description: "OpenAI Codex CLI platform integration. Classifies TOML sub-agent definitions under `.codex/agents/*.toml`.",
4574
4708
  extensions: [
4575
- codexProvider2
4709
+ codexProvider2,
4710
+ atFileExtractor2,
4711
+ dollarSkillExtractor2
4576
4712
  ]
4577
4713
  },
4578
4714
  {
@@ -12176,14 +12312,16 @@ import { existsSync as existsSync16 } from "fs";
12176
12312
  import { join as join10 } from "path";
12177
12313
  function detectProvidersFromFilesystem(cwd, providers) {
12178
12314
  const seen = /* @__PURE__ */ new Set();
12179
- const out = [];
12315
+ const matched = [];
12180
12316
  for (const provider of providers) {
12181
12317
  if (seen.has(provider.id)) continue;
12182
12318
  if (!isDetectableUnderCwd(cwd, provider)) continue;
12183
12319
  seen.add(provider.id);
12184
- out.push(provider.id);
12320
+ matched.push(provider);
12185
12321
  }
12186
- return out;
12322
+ const hasVendor = matched.some((p) => p.detect?.fallback !== true);
12323
+ const kept = hasVendor ? matched.filter((p) => p.detect?.fallback !== true) : matched;
12324
+ return kept.map((p) => p.id);
12187
12325
  }
12188
12326
  function isDetectableUnderCwd(cwd, provider) {
12189
12327
  if (!installedDefaultEnabled(provider.stability)) return false;
@@ -17254,7 +17392,7 @@ function classifyLinkSource(source, shortIdToQualified, cachedQualifiedIds, appl
17254
17392
  }
17255
17393
 
17256
17394
  // kernel/orchestrator/node-identifiers.ts
17257
- import { posix as pathPosix4 } from "path";
17395
+ import { posix as pathPosix5 } from "path";
17258
17396
  function deriveNodeIdentifiers(node, kindDescriptor) {
17259
17397
  const sources = kindDescriptor?.identifiers;
17260
17398
  if (!sources || sources.length === 0) return [];
@@ -17278,16 +17416,16 @@ function readFrontmatterName(node) {
17278
17416
  return raw.length > 0 ? raw : null;
17279
17417
  }
17280
17418
  function readFilenameBasename(node) {
17281
- const base = pathPosix4.basename(node.path);
17419
+ const base = pathPosix5.basename(node.path);
17282
17420
  if (!base) return null;
17283
- const ext = pathPosix4.extname(base);
17421
+ const ext = pathPosix5.extname(base);
17284
17422
  const stem = ext ? base.slice(0, -ext.length) : base;
17285
17423
  return stem.length > 0 ? stem : null;
17286
17424
  }
17287
17425
  function readDirname(node) {
17288
- const dir = pathPosix4.dirname(node.path);
17426
+ const dir = pathPosix5.dirname(node.path);
17289
17427
  if (!dir || dir === "." || dir === "/") return null;
17290
- const base = pathPosix4.basename(dir);
17428
+ const base = pathPosix5.basename(dir);
17291
17429
  return base.length > 0 ? base : null;
17292
17430
  }
17293
17431
  function collectNameCollisions(nodes, kindRegistry) {
@@ -17384,7 +17522,7 @@ function lookupAllowedKinds(link, _indexes, ctx) {
17384
17522
  }
17385
17523
  function stripTriggerSigil(normalized) {
17386
17524
  if (!normalized) return null;
17387
- const trimmed = normalized.replace(/^[/@]/, "").trim();
17525
+ const trimmed = normalized.replace(/^[/@$]/, "").trim();
17388
17526
  return trimmed.length === 0 ? null : trimmed;
17389
17527
  }
17390
17528
  function indexNode(node, ctx, byName) {
@@ -30420,6 +30558,7 @@ function presentationOptionals(ui) {
30420
30558
  if (ui.emoji !== void 0) out.emoji = ui.emoji;
30421
30559
  if (ui.icon !== void 0) out.icon = ui.icon;
30422
30560
  if (ui.hideChip !== void 0) out.hideChip = ui.hideChip;
30561
+ if (ui.invocationSigil !== void 0) out.invocationSigil = ui.invocationSigil;
30423
30562
  return out;
30424
30563
  }
30425
30564
  function resolveProviderBodyField(read) {
@@ -32989,4 +33128,4 @@ function resolveBareDefault() {
32989
33128
  process.exit(ExitCode.Error);
32990
33129
  }
32991
33130
  //# sourceMappingURL=cli.js.map
32992
- //# debugId=75d0c2c2-d08f-5d7c-aa62-3e1d53dd4ef1
33131
+ //# debugId=ca8113af-a7ac-5cd7-b45c-03a5974bca86