@skill-map/cli 0.34.1 → 0.36.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
@@ -470,7 +470,13 @@ var claudeProvider = {
470
470
  color: "#3b82f6",
471
471
  colorDark: "#60a5fa",
472
472
  icon: { kind: "pi", id: "pi-user" }
473
- }
473
+ },
474
+ // `frontmatter.name` is the documented canonical identifier
475
+ // (https://code.claude.com/docs/en/agents.md); `filename-basename`
476
+ // is a graceful fallback for agents authored without an explicit
477
+ // `name:` field, the file at `.claude/agents/<id>.md` resolves
478
+ // `@<id>` even when frontmatter is partial.
479
+ identifiers: ["frontmatter.name", "filename-basename"]
474
480
  },
475
481
  command: {
476
482
  schema: "./schemas/command.schema.json",
@@ -483,7 +489,12 @@ var claudeProvider = {
483
489
  kind: "svg",
484
490
  path: "M4 17 L10 11 L4 5 M12 19 L20 19"
485
491
  }
486
- }
492
+ },
493
+ // Commands share Anthropic's documented merger with skills
494
+ // (skills.md), so the same dual-source applies: explicit
495
+ // `frontmatter.name` first, filename basename fallback for
496
+ // `.claude/commands/<name>.md`.
497
+ identifiers: ["frontmatter.name", "filename-basename"]
487
498
  },
488
499
  skill: {
489
500
  schema: "./schemas/skill.schema.json",
@@ -493,7 +504,13 @@ var claudeProvider = {
493
504
  color: "#10b981",
494
505
  colorDark: "#34d399",
495
506
  icon: { kind: "pi", id: "pi-bolt" }
496
- }
507
+ },
508
+ // Anthropic explicitly documents the directory name as the
509
+ // canonical identifier for skills (skills.md: "The directory name
510
+ // becomes the command you type"). `frontmatter.name` is an
511
+ // optional override; when absent, the dirname between
512
+ // `.claude/skills/` and `/SKILL.md` is the invocation handle.
513
+ identifiers: ["frontmatter.name", "dirname"]
497
514
  },
498
515
  /**
499
516
  * Phase 5 of the active-lens migration: MCP servers surface as
@@ -518,9 +535,81 @@ var claudeProvider = {
518
535
  color: "#8b5cf6",
519
536
  colorDark: "#a78bfa",
520
537
  icon: { kind: "pi", id: "pi-server" }
521
- }
538
+ },
539
+ // MCP nodes are virtual (`mcp://<server>`), no filesystem path
540
+ // and no Claude invocation surface (mentions / slashes don't
541
+ // resolve to MCPs). `frontmatter.name` is set by the
542
+ // `core/mcp-tools` extractor at emission so the index stays
543
+ // consistent if a future link.kind ever targets MCPs by name.
544
+ identifiers: ["frontmatter.name"]
522
545
  }
523
546
  },
547
+ // Strict resolution matrix consumed by `liftResolvedLinkConfidence`:
548
+ // an `@<name>` mention resolves to an agent only; a `/<name>` slash
549
+ // resolves to a command OR skill (Anthropic merged the two surfaces
550
+ // per skills.md). MCP nodes are not invocable through either
551
+ // mention or slash, so they intentionally do not appear here.
552
+ resolution: {
553
+ mentions: ["agent"],
554
+ invokes: ["command", "skill"]
555
+ },
556
+ // Reserved invocation names the Claude runtime owns. A user file
557
+ // declaring one of these (e.g. `.claude/commands/help.md`) is
558
+ // silently shadowed: the runtime runs the built-in instead.
559
+ //
560
+ // - `command`: canonical list of built-in slash commands documented
561
+ // at https://docs.claude.com/en/docs/claude-code/slash-commands.
562
+ // The list will drift as Anthropic adds / removes commands;
563
+ // updates ship via a kernel patch + changeset (the catalog is
564
+ // considered API surface that users rely on the analyzer to
565
+ // reflect).
566
+ // - `agent`: built-in agents Anthropic ships with the CLI. Smaller
567
+ // surface than commands today.
568
+ // - `skill`: no built-in skills today (skills are user-defined and
569
+ // discovered from disk); the key is omitted on purpose, defaulting
570
+ // to no reserved names for the kind.
571
+ reservedNames: {
572
+ command: [
573
+ "help",
574
+ "clear",
575
+ "compact",
576
+ "cost",
577
+ "init",
578
+ "model",
579
+ "agents",
580
+ "login",
581
+ "logout",
582
+ "mcp",
583
+ "memory",
584
+ "config",
585
+ "doctor",
586
+ "permissions",
587
+ "add-dir",
588
+ "bug",
589
+ "pr-comments",
590
+ "release-notes",
591
+ "review",
592
+ "terminal-setup",
593
+ "vim",
594
+ "output-style",
595
+ "hooks",
596
+ "install-github-app",
597
+ "migrate-installer",
598
+ "upgrade",
599
+ "resume",
600
+ "exit",
601
+ "quit",
602
+ "security-review",
603
+ "statusline",
604
+ "usage",
605
+ "feedback"
606
+ ],
607
+ agent: [
608
+ "general-purpose",
609
+ "output-style-setup",
610
+ "statusline-setup"
611
+ ]
612
+ },
524
613
  // Auxiliary schemas the per-kind schemas $ref by $id. AJV needs them
525
614
  // registered via addSchema BEFORE the per-kind schemas compile, so the
526
615
  // validator builder pre-registers them. `skill-base.schema.json` is the
@@ -605,7 +694,7 @@ var atDirectiveExtractor = {
605
694
  pluginId: "claude",
606
695
  kind: "extractor",
607
696
  version: "1.0.0",
608
- description: "Detects `@<token>` directives in a node's body using Claude Code interpretation rules. A bare handle (e.g. `@team`) becomes a `mentions` link; a file-flavoured token (e.g. `@docs/api.md`, `@./readme.md`) becomes a `references` link. Gated by `precondition.provider: ['claude']` so Gemini / Cursor / Codex apply their own at-directive flavours via their own extractors.",
697
+ description: "Detects `@<token>` directives in a node's body using Claude Code interpretation rules. A bare handle (e.g. `@team`) becomes a `mentions` link; a file-flavoured token (e.g. `@docs/api.md`, `@./readme.md`) becomes a `references` link. Gated by `precondition.provider: ['claude']` so Antigravity / Cursor / Codex apply their own at-directive flavours via their own extractors.",
609
698
  scope: "body",
610
699
  precondition: { provider: ["claude"] },
611
700
  extract(ctx) {
@@ -673,7 +762,7 @@ var slashExtractor = {
673
762
  pluginId: "claude",
674
763
  kind: "extractor",
675
764
  version: "1.0.0",
676
- description: "Detects `/command` invocations in a node's body using Claude Code routing rules and turns each one into an arrow between nodes in the graph. Gated by `precondition.provider: ['claude']` so Gemini / Cursor / Codex apply their own slash flavours (Gemini has 4 routing separators, Codex deprecated user slash commands, etc.) via their own extractors.",
765
+ description: "Detects `/command` invocations in a node's body using Claude Code routing rules and turns each one into an arrow between nodes in the graph. Gated by `precondition.provider: ['claude']` so Antigravity / Cursor / Codex apply their own slash flavours (Antigravity ships subagent and skill panels, Codex deprecated user slash commands, etc.) via their own extractors.",
677
766
  scope: "body",
678
767
  precondition: { provider: ["claude"] },
679
768
  extract(ctx) {
@@ -706,126 +795,40 @@ var slashExtractor = {
706
795
  }
707
796
  };
708
797
 
709
- // plugins/gemini/providers/gemini/schemas/agent.schema.json
710
- var agent_schema_default2 = {
711
- $schema: "https://json-schema.org/draft/2020-12/schema",
712
- $id: "https://skill-map.dev/providers/gemini/v1/frontmatter/agent.schema.json",
713
- title: "FrontmatterGeminiAgent",
714
- description: "Frontmatter shape for nodes classified as `agent` by the Gemini Provider. Mirrors Google's documented Gemini CLI subagent frontmatter verbatim (https://geminicli.com/docs/core/subagents/): `name` and `description` come from the spec base; this schema adds the 7 vendor-specific fields. skill-map AGGREGATES the vendor spec, it does not curate it. `additionalProperties: true` so future Google additions flow through unchanged until this schema catches up. Note: Gemini's `kind` field (enum `local|remote`) lives at a different layer than the kernel's extension-kind discriminator and the node-kind discriminator \u2014 both use the word `kind` but in distinct namespaces (manifest vs frontmatter).",
715
- allOf: [
716
- { $ref: "https://skill-map.dev/spec/v0/frontmatter/base.schema.json" }
717
- ],
718
- type: "object",
719
- additionalProperties: true,
720
- properties: {
721
- kind: {
722
- type: "string",
723
- enum: ["local", "remote"],
724
- default: "local",
725
- description: "Subagent execution mode. `local` runs in-process; `remote` is the documented value for the future remote-agent surface (https://geminicli.com/docs/core/remote-agents/)."
726
- },
727
- tools: {
728
- type: "array",
729
- description: "Tool names this subagent can invoke. Wildcards supported: `*` (all), `mcp_*` (all MCP), `mcp_<server>_*` (one MCP server). When omitted, the subagent inherits the parent session's full tool set.",
730
- items: { type: "string" }
731
- },
732
- mcpServers: {
733
- type: "object",
734
- additionalProperties: true,
735
- description: "Inline MCP server configuration isolated to this subagent. Shape is platform-defined; preserved as an opaque object."
736
- },
737
- model: {
738
- type: "string",
739
- description: "Model id override (e.g. `gemini-3-flash-preview`). When omitted, inherits from the parent session."
740
- },
741
- temperature: {
742
- type: "number",
743
- minimum: 0,
744
- maximum: 2,
745
- default: 1,
746
- description: "Model sampling temperature. Range 0.0\u20132.0 per Google docs."
747
- },
748
- max_turns: {
749
- type: "integer",
750
- minimum: 1,
751
- default: 30,
752
- description: "Hard cap on conversation turns before the subagent must return."
753
- },
754
- timeout_mins: {
755
- type: "integer",
756
- minimum: 1,
757
- default: 10,
758
- description: "Maximum execution duration in minutes."
759
- }
760
- }
761
- };
762
-
763
- // plugins/gemini/providers/gemini/schemas/skill.schema.json
764
- var skill_schema_default2 = {
765
- $schema: "https://json-schema.org/draft/2020-12/schema",
766
- $id: "https://skill-map.dev/providers/gemini/v1/frontmatter/skill.schema.json",
767
- title: "FrontmatterGeminiSkill",
768
- description: "Frontmatter shape for nodes classified as `skill` by the Gemini Provider \u2014 Agent Skills delivered as `SKILL.md` files (https://geminicli.com/docs/cli/creating-skills/). Google documents only `name` and `description` as required frontmatter fields; both already live in the spec's universal `frontmatter/base.schema.json`. `additionalProperties: true` so vendor additions flow through unchanged.",
769
- allOf: [
770
- { $ref: "https://skill-map.dev/spec/v0/frontmatter/base.schema.json" }
771
- ],
772
- type: "object",
773
- additionalProperties: true,
774
- properties: {}
775
- };
776
-
777
- // plugins/gemini/providers/gemini/index.ts
778
- var geminiProvider = {
779
- id: "gemini",
780
- pluginId: "gemini",
798
+ // plugins/antigravity/providers/antigravity/index.ts
799
+ var antigravityProvider = {
800
+ id: "antigravity",
801
+ pluginId: "antigravity",
781
802
  kind: "provider",
782
803
  version: "1.0.0",
783
- description: "Walks Gemini CLI scope conventions (.gemini/{agents,skills}).",
784
- read: { extensions: [".md"], parser: "frontmatter-yaml" },
785
- // Per spec § A.6, defaultRefreshAction values MUST be qualified
786
- // action ids. The summarize-* actions are not yet implemented as
787
- // registry entries (they ship later under the Gemini bundle), but
788
- // the qualified form is the contract.
789
- //
790
- // UI presentation: kind visuals are normalised across Providers, every
791
- // Provider that contributes `agent` declares the same color + icon as
792
- // Claude, every Provider that contributes `skill` declares the same
793
- // color + icon as Claude, etc. The declaration STAYS per-Provider (the
794
- // shape allows divergence the day a Provider wants its own identity for
795
- // a kind), but today the values mirror Claude so the visual vocabulary
796
- // is uniform regardless of where a node was sourced from.
797
- kinds: {
798
- agent: {
799
- schema: "./schemas/agent.schema.json",
800
- schemaJson: agent_schema_default2,
801
- ui: {
802
- label: "Agents",
803
- color: "#3b82f6",
804
- colorDark: "#60a5fa",
805
- icon: { kind: "pi", id: "pi-user" }
806
- }
807
- },
808
- skill: {
809
- schema: "./schemas/skill.schema.json",
810
- schemaJson: skill_schema_default2,
811
- ui: {
812
- label: "Skills",
813
- color: "#10b981",
814
- colorDark: "#34d399",
815
- icon: { kind: "pi", id: "pi-bolt" }
816
- }
817
- }
818
- },
819
- classify(path) {
820
- const lower = path.toLowerCase();
821
- if (lower.startsWith(".gemini/agents/")) return "agent";
822
- if (/^\.gemini\/skills\/[^/]+\/skill\.md$/.test(lower)) return "skill";
804
+ description: "Google Antigravity CLI. Replaces the retired Gemini CLI; skills route through the neutral `agent-skills` Provider via `.agents/skills/`. This Provider contributes lens identity and a reserved-name seed catalog.",
805
+ // No `read` config: this Provider does not walk the filesystem. The
806
+ // kernel walker only fires for Providers with `read` or `walk`; an
807
+ // empty Provider participates in registration (its `ui` block is
808
+ // available, its `reservedNames` catalog is loaded) without owning
809
+ // any on-disk territory.
810
+ kinds: {},
811
+ // Always disclaim: paths are owned by other Providers (`.agents/` ->
812
+ // `agent-skills`, `AGENTS.md` -> `core/markdown` fallback).
813
+ classify() {
823
814
  return null;
815
+ },
816
+ // Seed catalog. Built-in slash commands surfaced by the Antigravity
817
+ // TUI (`/agents`, `/help`, `/quit`, `/skills`, `/hooks`, etc.). The
818
+ // exact list will expand once Google publishes the full reference;
819
+ // start small and document the growth path here rather than over-
820
+ // commit to a catalog that may drift.
821
+ //
822
+ // Inactive today (no nodes are classified under `antigravity`), kept
823
+ // here so the day Antigravity gains an own kind or the analyzer keys
824
+ // on the active lens, the catalog is already in place.
825
+ reservedNames: {
826
+ command: ["agents", "help", "quit", "exit", "skills", "hooks"]
824
827
  }
825
828
  };
826
829
 
827
830
  // plugins/openai/providers/openai/schemas/agent.schema.json
828
- var agent_schema_default3 = {
831
+ var agent_schema_default2 = {
829
832
  $schema: "https://json-schema.org/draft/2020-12/schema",
830
833
  $id: "https://skill-map.dev/providers/openai/v1/frontmatter/agent.schema.json",
831
834
  title: "FrontmatterCodexAgent",
@@ -887,16 +890,27 @@ var openaiProvider = {
887
890
  kinds: {
888
891
  agent: {
889
892
  schema: "./schemas/agent.schema.json",
890
- schemaJson: agent_schema_default3,
893
+ schemaJson: agent_schema_default2,
891
894
  ui: {
892
895
  label: "Codex agents",
893
- // Codex green; distinct from claude / gemini palettes.
896
+ // Codex green; distinct from the claude palette.
894
897
  color: "#22c55e",
895
898
  colorDark: "#4ade80",
896
899
  icon: { kind: "pi", id: "pi-bolt" }
897
- }
900
+ },
901
+ // Codex sub-agents are referenced by file basename
902
+ // (`.codex/agents/<name>.toml`). `frontmatter.name` lives inside
903
+ // the TOML structured frontmatter when the author declared it
904
+ // explicitly.
905
+ identifiers: ["frontmatter.name", "filename-basename"]
898
906
  }
899
907
  },
908
+ // Codex's invocation surface is mention-style today (`@<name>`); slash
909
+ // invocation and skill nodes land in Phase 6b. Empty `invokes` keeps
910
+ // the contract narrow until skills arrive.
911
+ resolution: {
912
+ mentions: ["agent"]
913
+ },
900
914
  classify(path) {
901
915
  const lower = path.toLowerCase();
902
916
  if (lower.startsWith(".codex/agents/") && lower.endsWith(".toml")) return "agent";
@@ -905,7 +919,7 @@ var openaiProvider = {
905
919
  };
906
920
 
907
921
  // plugins/agent-skills/providers/agent-skills/schemas/skill.schema.json
908
- var skill_schema_default3 = {
922
+ var skill_schema_default2 = {
909
923
  $schema: "https://json-schema.org/draft/2020-12/schema",
910
924
  $id: "https://skill-map.dev/providers/agent-skills/v1/frontmatter/skill.schema.json",
911
925
  title: "FrontmatterAgentSkillsSkill",
@@ -929,15 +943,24 @@ var agentSkillsProvider = {
929
943
  kinds: {
930
944
  skill: {
931
945
  schema: "./schemas/skill.schema.json",
932
- schemaJson: skill_schema_default3,
946
+ schemaJson: skill_schema_default2,
933
947
  ui: {
934
948
  label: "Skills",
935
949
  color: "#10b981",
936
950
  colorDark: "#34d399",
937
951
  icon: { kind: "pi", id: "pi-bolt" }
938
- }
952
+ },
953
+ // Open-standard skills mirror Anthropic's: dirname between
954
+ // `.agents/skills/` and `/SKILL.md` is the canonical handle,
955
+ // `frontmatter.name` overrides when present.
956
+ identifiers: ["frontmatter.name", "dirname"]
939
957
  }
940
958
  },
959
+ // The open standard documents slash-style invocation of skills; no
960
+ // mention surface (no agents in this Provider's territory).
961
+ resolution: {
962
+ invokes: ["skill"]
963
+ },
941
964
  classify(path) {
942
965
  if (/^\.agents\/skills\/[^/]+\/skill\.md$/.test(path.toLowerCase())) return "skill";
943
966
  return null;
@@ -949,7 +972,7 @@ var markdown_schema_default = {
949
972
  $schema: "https://json-schema.org/draft/2020-12/schema",
950
973
  $id: "https://skill-map.dev/providers/core/v1/frontmatter/markdown.schema.json",
951
974
  title: "FrontmatterMarkdown",
952
- description: "Frontmatter shape for nodes classified as `markdown` by the built-in `core/markdown` Provider \u2014 the universal fallback for any markdown file no vendor-specific Provider claims (Claude / Gemini / agent-skills / future Codex). The kind is named after the *format* because the file is a generic fallback; format-named kinds apply only as the generic fallback \u2014 a TOML file that IS a Codex agent still classifies as `agent`, not `toml`. Extends the spec's universal `frontmatter/base.schema.json` via $ref-by-$id with no additional fields. Ownership relocated from the Claude Provider in spec 0.18.0 \u2014 markdown is provider-agnostic and lives under `core` so adding new vendor Providers (e.g. Codex) does not require choosing which one owns the universal fallback.",
975
+ description: "Frontmatter shape for nodes classified as `markdown` by the built-in `core/markdown` Provider, the universal fallback for any markdown file no vendor-specific Provider claims (Claude, OpenAI Codex, agent-skills, plus the metadata-only Antigravity bundle). The kind is named after the format because the file is a generic fallback; format-named kinds apply only as the generic fallback, a TOML file that IS a Codex agent still classifies as `agent`, not `toml`. Extends the spec's universal `frontmatter/base.schema.json` via $ref-by-$id with no additional fields. Ownership relocated from the Claude Provider in spec 0.18.0, markdown is provider-agnostic and lives under `core` so adding new vendor Providers does not require choosing which one owns the universal fallback.",
953
976
  allOf: [
954
977
  { $ref: "https://skill-map.dev/spec/v0/frontmatter/base.schema.json" }
955
978
  ],
@@ -970,8 +993,9 @@ var coreMarkdownProvider = {
970
993
  // as a registry entry (it ships later under the core bundle), but
971
994
  // the qualified form is the contract.
972
995
  //
973
- // UI presentation: same neutral teal that Claude / Gemini used to
974
- // declare for their per-vendor markdown duplicates. The kindRegistry
996
+ // UI presentation: same neutral teal that the per-vendor Providers
997
+ // (when they shipped their own markdown kind duplicates) used to
998
+ // declare. The kindRegistry
975
999
  // composer (`buildKindRegistry`) makes this entry the primary owner
976
1000
  // of the `markdown` kind because no other Provider declares it now;
977
1001
  // per-node painting still falls through `entry.providers[node.provider]`
@@ -989,8 +1013,18 @@ var coreMarkdownProvider = {
989
1013
  path: "M14 2 H6 a2 2 0 0 0 -2 2 V20 a2 2 0 0 0 2 2 H18 a2 2 0 0 0 2 -2 V8 L14 2 M14 2 V8 H20 M16 13 H8 M16 17 H8 M10 9 H8"
990
1014
  }
991
1015
  }
1016
+ // No `identifiers`: markdown nodes are addressed by path, not by a
1017
+ // canonical name. The name index built by `liftResolvedLinkConfidence`
1018
+ // never sees markdown entries; resolution falls through to the
1019
+ // path-match rule only.
992
1020
  }
993
1021
  },
1022
+ // No `resolution`: `core/markdown` is the universal fallback Provider,
1023
+ // it does not declare an invocation surface of its own. Mentions /
1024
+ // slashes sourced from markdown bodies still resolve via OTHER
1025
+ // Providers' resolution maps (the lookup keys on the source node's
1026
+ // Provider id, not on `core/markdown`). Leaving the field absent keeps
1027
+ // the contract narrow.
994
1028
  classify() {
995
1029
  return "markdown";
996
1030
  }
@@ -1174,7 +1208,13 @@ var markdownLinkExtractor = {
1174
1208
  source: ctx.node.path,
1175
1209
  target: resolved,
1176
1210
  kind: "references",
1177
- confidence: 0.95,
1211
+ // 1.0: the `[text](path)` syntax is unambiguous. Markdown
1212
+ // explicitly designates an out-link via the brackets +
1213
+ // parentheses pair; there is no inference left to discount.
1214
+ // Whether the path resolves to a real node is a separate
1215
+ // concern (the `core/broken-ref` analyzer flags unresolved
1216
+ // targets), not a confidence question.
1217
+ confidence: 1,
1178
1218
  sources: [ID5],
1179
1219
  trigger: {
1180
1220
  originalTrigger: original,
@@ -1848,12 +1888,56 @@ function formatBreakdown(byKind, direction) {
1848
1888
  return [direction, ...lines].join("\n");
1849
1889
  }
1850
1890
 
1891
+ // plugins/core/analyzers/reserved-name/text.ts
1892
+ var RESERVED_NAME_TEXTS = {
1893
+ /**
1894
+ * `<path> shadows a built-in <provider> <kind>. The runtime ignores
1895
+ * this file in favour of its own built-in. Rename the file or
1896
+ * `frontmatter.name` to a non-reserved value.`
1897
+ */
1898
+ message: "{{path}} shadows a built-in {{provider}} {{kind}}. The runtime ignores this file in favour of its own built-in. Rename the file or `frontmatter.name` to a non-reserved value."
1899
+ };
1900
+
1901
+ // plugins/core/analyzers/reserved-name/index.ts
1902
+ var ID15 = "reserved-name";
1903
+ var reservedNameAnalyzer = {
1904
+ id: ID15,
1905
+ pluginId: "core",
1906
+ kind: "analyzer",
1907
+ version: "1.0.0",
1908
+ description: "Flags user nodes whose name collides with a Provider runtime's built-in invocable (the runtime shadows the file silently).",
1909
+ mode: "deterministic",
1910
+ evaluate(ctx) {
1911
+ const reserved = ctx.reservedNodePaths;
1912
+ if (!reserved || reserved.size === 0) return [];
1913
+ const byPath3 = /* @__PURE__ */ new Map();
1914
+ for (const node of ctx.nodes) byPath3.set(node.path, node);
1915
+ const issues = [];
1916
+ for (const path of reserved) {
1917
+ const node = byPath3.get(path);
1918
+ if (!node) continue;
1919
+ issues.push({
1920
+ analyzerId: ID15,
1921
+ severity: "warn",
1922
+ nodeIds: [node.path],
1923
+ message: tx(RESERVED_NAME_TEXTS.message, {
1924
+ path: node.path,
1925
+ provider: node.provider,
1926
+ kind: node.kind
1927
+ }),
1928
+ data: { provider: node.provider, kind: node.kind }
1929
+ });
1930
+ }
1931
+ return issues;
1932
+ }
1933
+ };
1934
+
1851
1935
  // plugins/core/analyzers/stability/index.ts
1852
- var ID15 = "stability";
1936
+ var ID16 = "stability";
1853
1937
  var EXPERIMENTAL_TOOLTIP = "Experimental: API may change";
1854
1938
  var DEPRECATED_TOOLTIP = "Deprecated: avoid in new code";
1855
1939
  var stabilityAnalyzer = {
1856
- id: ID15,
1940
+ id: ID16,
1857
1941
  pluginId: "core",
1858
1942
  kind: "analyzer",
1859
1943
  version: "1.0.0",
@@ -1885,7 +1969,7 @@ var stabilityAnalyzer = {
1885
1969
  tooltip: EXPERIMENTAL_TOOLTIP
1886
1970
  });
1887
1971
  issues.push({
1888
- analyzerId: ID15,
1972
+ analyzerId: ID16,
1889
1973
  severity: "info",
1890
1974
  nodeIds: [node.path],
1891
1975
  message: `Node '${node.path}' is marked experimental: API may change.`,
@@ -1898,7 +1982,7 @@ var stabilityAnalyzer = {
1898
1982
  severity: "warn"
1899
1983
  });
1900
1984
  issues.push({
1901
- analyzerId: ID15,
1985
+ analyzerId: ID16,
1902
1986
  severity: "warn",
1903
1987
  nodeIds: [node.path],
1904
1988
  message: `Node '${node.path}' is marked deprecated: avoid in new code.`,
@@ -1932,9 +2016,9 @@ var SUPERSEDED_TEXTS = {
1932
2016
  };
1933
2017
 
1934
2018
  // plugins/core/analyzers/superseded/index.ts
1935
- var ID16 = "superseded";
2019
+ var ID17 = "superseded";
1936
2020
  var supersededAnalyzer = {
1937
- id: ID16,
2021
+ id: ID17,
1938
2022
  pluginId: "core",
1939
2023
  kind: "analyzer",
1940
2024
  version: "1.0.0",
@@ -1946,7 +2030,7 @@ var supersededAnalyzer = {
1946
2030
  const supersededBy = pickSupersededBy(node);
1947
2031
  if (supersededBy === null) continue;
1948
2032
  issues.push({
1949
- analyzerId: ID16,
2033
+ analyzerId: ID17,
1950
2034
  severity: "info",
1951
2035
  nodeIds: [node.path],
1952
2036
  message: tx(SUPERSEDED_TEXTS.message, {
@@ -1995,14 +2079,14 @@ var TRIGGER_COLLISION_TEXTS = {
1995
2079
  };
1996
2080
 
1997
2081
  // plugins/core/analyzers/trigger-collision/index.ts
1998
- var ID17 = "trigger-collision";
2082
+ var ID18 = "trigger-collision";
1999
2083
  var ADVERTISING_KINDS = /* @__PURE__ */ new Set([
2000
2084
  "command",
2001
2085
  "skill",
2002
2086
  "agent"
2003
2087
  ]);
2004
2088
  var triggerCollisionAnalyzer = {
2005
- id: ID17,
2089
+ id: ID18,
2006
2090
  pluginId: "core",
2007
2091
  kind: "analyzer",
2008
2092
  mode: "deterministic",
@@ -2101,7 +2185,7 @@ function analyzeTriggerBucket(normalized, claims) {
2101
2185
  part: parts[0]
2102
2186
  });
2103
2187
  return {
2104
- analyzerId: ID17,
2188
+ analyzerId: ID18,
2105
2189
  severity: "error",
2106
2190
  nodeIds,
2107
2191
  message,
@@ -2141,10 +2225,10 @@ var UNKNOWN_FIELD_TEXTS = {
2141
2225
  };
2142
2226
 
2143
2227
  // plugins/core/analyzers/unknown-field/index.ts
2144
- var ID18 = "unknown-field";
2228
+ var ID19 = "unknown-field";
2145
2229
  var RESERVED_ROOT_BLOCKS = /* @__PURE__ */ new Set(["identity", "annotations", "settings", "audit"]);
2146
2230
  var unknownFieldAnalyzer = {
2147
- id: ID18,
2231
+ id: ID19,
2148
2232
  pluginId: "core",
2149
2233
  kind: "analyzer",
2150
2234
  version: "1.0.0",
@@ -2202,7 +2286,7 @@ var unknownFieldAnalyzer = {
2202
2286
  for (const key of Object.keys(annotations)) {
2203
2287
  if (!knownAnnotationKeys.has(key)) {
2204
2288
  issues.push({
2205
- analyzerId: ID18,
2289
+ analyzerId: ID19,
2206
2290
  severity: "warn",
2207
2291
  nodeIds: [node.path],
2208
2292
  message: tx(UNKNOWN_FIELD_TEXTS.unknownAnnotationKey, {
@@ -2229,7 +2313,7 @@ var unknownFieldAnalyzer = {
2229
2313
  if (validator(value)) continue;
2230
2314
  const errors = (validator.errors ?? []).map((e) => `${e.instancePath || "(root)"} ${e.message ?? e.keyword}`).join("; ");
2231
2315
  issues.push({
2232
- analyzerId: ID18,
2316
+ analyzerId: ID19,
2233
2317
  severity: "warn",
2234
2318
  nodeIds: [node.path],
2235
2319
  message: tx(UNKNOWN_FIELD_TEXTS.pluginNamespaceInvalid, {
@@ -2245,7 +2329,7 @@ var unknownFieldAnalyzer = {
2245
2329
  continue;
2246
2330
  }
2247
2331
  issues.push({
2248
- analyzerId: ID18,
2332
+ analyzerId: ID19,
2249
2333
  severity: "warn",
2250
2334
  nodeIds: [node.path],
2251
2335
  message: tx(UNKNOWN_FIELD_TEXTS.unknownRootKey, {
@@ -2538,9 +2622,9 @@ var VALIDATE_ALL_TEXTS = {
2538
2622
  };
2539
2623
 
2540
2624
  // plugins/core/analyzers/validate-all/index.ts
2541
- var ID19 = "validate-all";
2625
+ var ID20 = "validate-all";
2542
2626
  var validateAllAnalyzer = {
2543
- id: ID19,
2627
+ id: ID20,
2544
2628
  pluginId: "core",
2545
2629
  kind: "analyzer",
2546
2630
  version: "1.0.0",
@@ -2603,7 +2687,7 @@ function collectNodeFindings(v, node, out) {
2603
2687
  const result = v.validate("node", toNodeForSchema(node));
2604
2688
  if (result.ok) return;
2605
2689
  out.push({
2606
- analyzerId: ID19,
2690
+ analyzerId: ID20,
2607
2691
  severity: "error",
2608
2692
  nodeIds: [node.path],
2609
2693
  message: tx(VALIDATE_ALL_TEXTS.nodeFailure, {
@@ -2622,7 +2706,7 @@ function collectFrontmatterBaseFindings(node, out) {
2622
2706
  if (isMissingStringField(fm, "description")) missing.push("description");
2623
2707
  if (missing.length === 0) return;
2624
2708
  out.push({
2625
- analyzerId: ID19,
2709
+ analyzerId: ID20,
2626
2710
  // `warn` (not `error`) so the default `sm scan` exit code stays
2627
2711
  // 0 even when nodes are missing frontmatter base fields. Strict
2628
2712
  // mode (`sm scan --strict`) still escalates to exit 1. Matches
@@ -2644,7 +2728,7 @@ function collectLinkFindings(v, link2, out) {
2644
2728
  const result = v.validate("link", toLinkForSchema(link2));
2645
2729
  if (result.ok) return;
2646
2730
  out.push({
2647
- analyzerId: ID19,
2731
+ analyzerId: ID20,
2648
2732
  severity: "error",
2649
2733
  nodeIds: [link2.source],
2650
2734
  message: tx(VALIDATE_ALL_TEXTS.linkFailure, {
@@ -2712,13 +2796,13 @@ var ASCII_FORMATTER_TEXTS = {
2712
2796
  };
2713
2797
 
2714
2798
  // plugins/core/formatters/ascii/index.ts
2715
- var ID20 = "ascii";
2799
+ var ID21 = "ascii";
2716
2800
  var KIND_ORDER = ["agent", "command", "skill", "markdown"];
2717
2801
  var asciiFormatter = {
2718
- id: ID20,
2802
+ id: ID21,
2719
2803
  pluginId: "core",
2720
2804
  kind: "formatter",
2721
- formatId: ID20,
2805
+ formatId: ID21,
2722
2806
  version: "1.0.0",
2723
2807
  description: "Renders the scan as plain text, grouped by kind, arrows, and issues. Used by `sm scan --format=ascii`.",
2724
2808
  // ASCII tree formatter, header + per-kind sections + per-issue
@@ -2813,14 +2897,14 @@ function renderSection(out, kind, group) {
2813
2897
  }
2814
2898
 
2815
2899
  // plugins/core/formatters/json/index.ts
2816
- var ID21 = "json";
2900
+ var ID22 = "json";
2817
2901
  var jsonFormatter = {
2818
- id: ID21,
2902
+ id: ID22,
2819
2903
  pluginId: "core",
2820
2904
  kind: "formatter",
2821
2905
  version: "1.0.0",
2822
2906
  description: "Renders the persisted scan as JSON (conforms to `scan-result.schema.json` when the full ScanResult is available). Used by `sm graph --format json` and `GET /api/graph?format=json`.",
2823
- formatId: ID21,
2907
+ formatId: ID22,
2824
2908
  format(ctx) {
2825
2909
  if (ctx.scanResult !== void 0) {
2826
2910
  return JSON.stringify(ctx.scanResult);
@@ -2959,10 +3043,10 @@ function resolveSpecRoot2() {
2959
3043
  }
2960
3044
 
2961
3045
  // plugins/core/actions/bump/index.ts
2962
- var ID22 = "bump";
3046
+ var ID23 = "bump";
2963
3047
  var PLUGIN_ID = "core";
2964
3048
  var bumpAction = {
2965
- id: ID22,
3049
+ id: ID23,
2966
3050
  pluginId: PLUGIN_ID,
2967
3051
  kind: "action",
2968
3052
  version: "1.0.0",
@@ -3021,10 +3105,10 @@ function pickCurrentVersion(overlay) {
3021
3105
  }
3022
3106
 
3023
3107
  // plugins/core/actions/mark-superseded/index.ts
3024
- var ID23 = "mark-superseded";
3108
+ var ID24 = "mark-superseded";
3025
3109
  var PLUGIN_ID2 = "core";
3026
3110
  var markSupersededAction = {
3027
- id: ID23,
3111
+ id: ID24,
3028
3112
  pluginId: PLUGIN_ID2,
3029
3113
  kind: "action",
3030
3114
  version: "0.0.0",
@@ -3134,7 +3218,7 @@ var UPDATE_CHECK_TEXTS = {
3134
3218
  // package.json
3135
3219
  var package_default = {
3136
3220
  name: "@skill-map/cli",
3137
- version: "0.34.1",
3221
+ version: "0.36.0",
3138
3222
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
3139
3223
  license: "MIT",
3140
3224
  type: "module",
@@ -3513,7 +3597,7 @@ var updateCheckHook = {
3513
3597
  var claudeProvider2 = { ...claudeProvider, pluginId: "claude" };
3514
3598
  var atDirectiveExtractor2 = { ...atDirectiveExtractor, pluginId: "claude" };
3515
3599
  var slashExtractor2 = { ...slashExtractor, pluginId: "claude" };
3516
- var geminiProvider2 = { ...geminiProvider, pluginId: "gemini" };
3600
+ var antigravityProvider2 = { ...antigravityProvider, pluginId: "antigravity" };
3517
3601
  var openaiProvider2 = { ...openaiProvider, pluginId: "openai" };
3518
3602
  var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills" };
3519
3603
  var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core" };
@@ -3529,6 +3613,7 @@ var contributionOrphanAnalyzer2 = { ...contributionOrphanAnalyzer, pluginId: "co
3529
3613
  var jobOrphanFileAnalyzer2 = { ...jobOrphanFileAnalyzer, pluginId: "core" };
3530
3614
  var linkConflictAnalyzer2 = { ...linkConflictAnalyzer, pluginId: "core" };
3531
3615
  var linkCountsAnalyzer2 = { ...linkCountsAnalyzer, pluginId: "core" };
3616
+ var reservedNameAnalyzer2 = { ...reservedNameAnalyzer, pluginId: "core" };
3532
3617
  var stabilityAnalyzer2 = { ...stabilityAnalyzer, pluginId: "core" };
3533
3618
  var supersededAnalyzer2 = { ...supersededAnalyzer, pluginId: "core" };
3534
3619
  var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core" };
@@ -3551,11 +3636,11 @@ var builtInBundles = [
3551
3636
  ]
3552
3637
  },
3553
3638
  {
3554
- id: "gemini",
3639
+ id: "antigravity",
3555
3640
  granularity: "bundle",
3556
- description: "Gemini CLI platform integration. Classifies files under `.gemini/{agents,skills}` and parses Gemini-flavored frontmatter.",
3641
+ description: "Google Antigravity CLI platform integration (released 2026-05-19, replaces the retired Gemini CLI). Antigravity adopted the open-standard `.agents/` layout, so skills are classified by the neutral `agent-skills` Provider; this bundle contributes lens identity + a reserved-name seed catalog for future Antigravity built-in invocables.",
3557
3642
  extensions: [
3558
- geminiProvider2
3643
+ antigravityProvider2
3559
3644
  ]
3560
3645
  },
3561
3646
  {
@@ -3592,6 +3677,7 @@ var builtInBundles = [
3592
3677
  jobOrphanFileAnalyzer2,
3593
3678
  linkConflictAnalyzer2,
3594
3679
  linkCountsAnalyzer2,
3680
+ reservedNameAnalyzer2,
3595
3681
  stabilityAnalyzer2,
3596
3682
  supersededAnalyzer2,
3597
3683
  triggerCollisionAnalyzer2,
@@ -5068,7 +5154,7 @@ var AsyncMutex = class {
5068
5154
  this.#locked = true;
5069
5155
  return;
5070
5156
  }
5071
- await new Promise((resolve39) => this.#waiters.push(resolve39));
5157
+ await new Promise((resolve40) => this.#waiters.push(resolve40));
5072
5158
  this.#locked = true;
5073
5159
  }
5074
5160
  unlock() {
@@ -9678,7 +9764,13 @@ import { existsSync as existsSync14 } from "fs";
9678
9764
  import { join as join10 } from "path";
9679
9765
  var DETECTION_RULES = [
9680
9766
  { providerId: "claude", marker: ".claude" },
9681
- { providerId: "gemini", marker: ".gemini" },
9767
+ // `gemini` retired 2026-05-22: Google replaced the Gemini CLI with the
9768
+ // Antigravity CLI (released 2026-05-19; Gemini CLI sunsets 2026-06-18).
9769
+ // Antigravity adopted the open-standard `.agents/` instead of a
9770
+ // vendor-specific directory, so detection of a Google CLI project
9771
+ // falls through to the universal `agent-skills` lens (`.agents/`
9772
+ // already classifies via that neutral provider). The lens can still
9773
+ // be set manually via `sm config set activeProvider antigravity`.
9682
9774
  { providerId: "openai", marker: ".codex" },
9683
9775
  { providerId: "openai", marker: "AGENTS.md" },
9684
9776
  { providerId: "cursor", marker: ".cursor" }
@@ -13059,7 +13151,7 @@ import { Command as Command17, Option as Option16 } from "clipanion";
13059
13151
 
13060
13152
  // kernel/orchestrator/index.ts
13061
13153
  import { existsSync as existsSync22, statSync as statSync7 } from "fs";
13062
- import { isAbsolute as isAbsolute7, resolve as resolve27 } from "path";
13154
+ import { isAbsolute as isAbsolute7, resolve as resolve28 } from "path";
13063
13155
  import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
13064
13156
  import cl100k_base from "js-tiktoken/ranks/cl100k_base";
13065
13157
 
@@ -13428,7 +13520,7 @@ function isExternalUrlLink(link2) {
13428
13520
  }
13429
13521
 
13430
13522
  // kernel/orchestrator/analyzers.ts
13431
- async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions, orphanJobFiles, referenceablePaths, cwd, registeredActionIds, emitter, hookDispatcher) {
13523
+ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions, orphanJobFiles, referenceablePaths, cwd, registeredActionIds, emitter, hookDispatcher, reservedNodePaths) {
13432
13524
  const issues = [];
13433
13525
  const contributions = [];
13434
13526
  const validators = loadSchemaValidators();
@@ -13492,6 +13584,7 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
13492
13584
  orphanJobFiles,
13493
13585
  ...referenceablePaths ? { referenceablePaths } : {},
13494
13586
  ...cwd ? { cwd } : {},
13587
+ ...reservedNodePaths ? { reservedNodePaths } : {},
13495
13588
  emitContribution
13496
13589
  });
13497
13590
  for (const issue of emitted) {
@@ -13712,37 +13805,106 @@ function classifyLinkSource(source, shortIdToQualified, cachedQualifiedIds, appl
13712
13805
  return "obsolete";
13713
13806
  }
13714
13807
 
13715
- // kernel/orchestrator/lift-mention-confidence.ts
13716
- function liftMentionConfidence(links, nodes) {
13717
- if (!links.some((l) => l.kind === "mentions")) return;
13718
- const byPath3 = /* @__PURE__ */ new Set();
13719
- for (const node of nodes) byPath3.add(node.path);
13720
- const byNormalizedName = indexByNormalizedName2(nodes);
13721
- for (const link2 of links) {
13722
- if (link2.kind !== "mentions") continue;
13723
- if (isResolved2(link2, byPath3, byNormalizedName)) {
13724
- link2.confidence = 1;
13725
- }
13808
+ // kernel/orchestrator/node-identifiers.ts
13809
+ import { posix as pathPosix4 } from "path";
13810
+ function deriveNodeIdentifiers(node, kindDescriptor) {
13811
+ const sources = kindDescriptor?.identifiers;
13812
+ if (!sources || sources.length === 0) return [];
13813
+ const out = [];
13814
+ for (const source of sources) {
13815
+ const raw = readIdentifier(source, node);
13816
+ if (!raw) continue;
13817
+ const normalised = normalizeTrigger(raw);
13818
+ if (normalised) out.push(normalised);
13726
13819
  }
13820
+ return out;
13727
13821
  }
13728
- function isResolved2(link2, byPath3, byNormalizedName) {
13729
- const normalized = link2.trigger?.normalizedTrigger;
13730
- if (normalized) {
13731
- const withoutSigil = normalized.replace(/^[/@]/, "").trim();
13732
- if (byNormalizedName.has(withoutSigil)) return true;
13822
+ function readIdentifier(source, node) {
13823
+ if (source === "frontmatter.name") return readFrontmatterName(node);
13824
+ if (source === "filename-basename") return readFilenameBasename(node);
13825
+ return readDirname(node);
13826
+ }
13827
+ function readFrontmatterName(node) {
13828
+ const raw = node.frontmatter?.["name"];
13829
+ if (typeof raw !== "string") return null;
13830
+ return raw.length > 0 ? raw : null;
13831
+ }
13832
+ function readFilenameBasename(node) {
13833
+ const base = pathPosix4.basename(node.path);
13834
+ if (!base) return null;
13835
+ const ext = pathPosix4.extname(base);
13836
+ const stem = ext ? base.slice(0, -ext.length) : base;
13837
+ return stem.length > 0 ? stem : null;
13838
+ }
13839
+ function readDirname(node) {
13840
+ const dir = pathPosix4.dirname(node.path);
13841
+ if (!dir || dir === "." || dir === "/") return null;
13842
+ const base = pathPosix4.basename(dir);
13843
+ return base.length > 0 ? base : null;
13844
+ }
13845
+
13846
+ // kernel/orchestrator/lift-resolved-link-confidence.ts
13847
+ var RESERVED_TARGET_CONFIDENCE = 0.1;
13848
+ function liftResolvedLinkConfidence(links, nodes, ctx) {
13849
+ if (!links.some((l) => l.confidence < 1)) return;
13850
+ const indexes = buildIndexes(nodes, ctx);
13851
+ for (const link2 of links) {
13852
+ if (link2.confidence >= 1) continue;
13853
+ const resolution = resolve27(link2, indexes, ctx);
13854
+ if (resolution === "none") continue;
13855
+ link2.confidence = ctx.reservedNodePaths.has(resolution) ? RESERVED_TARGET_CONFIDENCE : 1;
13733
13856
  }
13734
- if (byPath3.has(link2.target)) return true;
13735
- return false;
13736
13857
  }
13737
- function indexByNormalizedName2(nodes) {
13738
- const out = /* @__PURE__ */ new Map();
13858
+ function buildIndexes(nodes, ctx) {
13859
+ const byPath3 = /* @__PURE__ */ new Set();
13860
+ const byName = /* @__PURE__ */ new Map();
13861
+ const nodeByPath = /* @__PURE__ */ new Map();
13739
13862
  for (const node of nodes) {
13740
- const raw = node.frontmatter?.["name"];
13741
- const name = typeof raw === "string" ? raw : "";
13742
- if (!name) continue;
13743
- out.set(normalizeTrigger(name), true);
13863
+ byPath3.add(node.path);
13864
+ nodeByPath.set(node.path, node);
13865
+ indexNode(node, ctx, byName);
13866
+ }
13867
+ return { byPath: byPath3, byName, nodeByPath };
13868
+ }
13869
+ function resolve27(link2, indexes, ctx) {
13870
+ if (indexes.byPath.has(link2.target)) return link2.target;
13871
+ return resolveByName(link2, indexes, ctx);
13872
+ }
13873
+ function resolveByName(link2, indexes, ctx) {
13874
+ const stripped = stripTriggerSigil(link2.trigger?.normalizedTrigger);
13875
+ if (stripped === null) return "none";
13876
+ const candidates = indexes.byName.get(stripped);
13877
+ if (!candidates?.length) return "none";
13878
+ const allowedKinds = lookupAllowedKinds(link2, indexes, ctx);
13879
+ if (!allowedKinds?.length) return "none";
13880
+ const winner = candidates.find((c) => allowedKinds.includes(c.kind));
13881
+ return winner ? winner.path : "none";
13882
+ }
13883
+ function lookupAllowedKinds(link2, indexes, ctx) {
13884
+ const sourceNode = indexes.nodeByPath.get(link2.source);
13885
+ if (!sourceNode) return void 0;
13886
+ return ctx.providerResolution.get(sourceNode.provider)?.[link2.kind];
13887
+ }
13888
+ function stripTriggerSigil(normalized) {
13889
+ if (!normalized) return null;
13890
+ const trimmed = normalized.replace(/^[/@]/, "").trim();
13891
+ return trimmed.length === 0 ? null : trimmed;
13892
+ }
13893
+ function indexNode(node, ctx, byName) {
13894
+ const kindDescriptor = ctx.kindRegistry.get(kindKey(node));
13895
+ const normalised = deriveNodeIdentifiers(node, kindDescriptor);
13896
+ for (const name of normalised) {
13897
+ const entry = { kind: node.kind, path: node.path };
13898
+ const bucket = byName.get(name);
13899
+ if (bucket) {
13900
+ bucket.push(entry);
13901
+ } else {
13902
+ byName.set(name, [entry]);
13903
+ }
13744
13904
  }
13745
- return out;
13905
+ }
13906
+ function kindKey(node) {
13907
+ return `${node.provider}/${node.kind}`;
13746
13908
  }
13747
13909
 
13748
13910
  // kernel/orchestrator/post-walk-transforms.ts
@@ -13755,17 +13917,17 @@ var POST_WALK_TRANSFORMS = [
13755
13917
  }
13756
13918
  },
13757
13919
  {
13758
- id: "lift-mention-confidence",
13759
- description: "Bump resolved `mentions` links to confidence 1.0 once the full node graph is known (post-merge polish).",
13760
- run(links, nodes) {
13761
- liftMentionConfidence(links, nodes);
13920
+ id: "lift-resolved-link-confidence",
13921
+ description: "Bump invocation links to confidence 1.0 when target / trigger resolves against the full node graph per the source Provider rules.",
13922
+ run(links, nodes, ctx) {
13923
+ liftResolvedLinkConfidence(links, nodes, ctx);
13762
13924
  }
13763
13925
  }
13764
13926
  ];
13765
- function applyPostWalkTransforms(links, nodes, transforms = POST_WALK_TRANSFORMS) {
13927
+ function applyPostWalkTransforms(links, nodes, ctx, transforms = POST_WALK_TRANSFORMS) {
13766
13928
  let current = links;
13767
13929
  for (const transform of transforms) {
13768
- const next = transform.run(current, nodes);
13930
+ const next = transform.run(current, nodes, ctx);
13769
13931
  if (next) current = next;
13770
13932
  }
13771
13933
  return current;
@@ -14477,7 +14639,8 @@ async function runScanInternal(_kernel, options) {
14477
14639
  pluginStores: options.pluginStores,
14478
14640
  activeProvider: resolveActiveProviderOption(options.activeProvider, options.roots)
14479
14641
  });
14480
- walked.internalLinks = applyPostWalkTransforms(walked.internalLinks, walked.nodes);
14642
+ const postWalkCtx = buildPostWalkTransformCtx(_kernel, walked.nodes);
14643
+ walked.internalLinks = applyPostWalkTransforms(walked.internalLinks, walked.nodes, postWalkCtx);
14481
14644
  recomputeLinkCounts(walked.nodes, walked.internalLinks);
14482
14645
  recomputeExternalRefsCount(walked.nodes, walked.externalLinks, walked.cachedPaths);
14483
14646
  await dispatchExtractorCompleted(exts.extractors, emitter, hookDispatcher);
@@ -14497,7 +14660,8 @@ async function runScanInternal(_kernel, options) {
14497
14660
  options.cwd,
14498
14661
  registeredActionIds,
14499
14662
  emitter,
14500
- hookDispatcher
14663
+ hookDispatcher,
14664
+ postWalkCtx.reservedNodePaths
14501
14665
  );
14502
14666
  mergeAnalyzerEmissions(walked, analyzerResult, exts.analyzers);
14503
14667
  const issues = analyzerResult.issues;
@@ -14510,6 +14674,56 @@ async function runScanInternal(_kernel, options) {
14510
14674
  await hookDispatcher.dispatch("scan.completed", scanCompletedEvent);
14511
14675
  return buildScanReturn(walked, issues, renameOps, stats, options, setup);
14512
14676
  }
14677
+ function buildPostWalkTransformCtx(kernel, nodes) {
14678
+ const { kindRegistry, providerResolution, reservedNamesByProviderKind } = buildProviderIndexes(kernel);
14679
+ const reservedNodePaths = buildReservedNodePaths(
14680
+ nodes,
14681
+ kindRegistry,
14682
+ reservedNamesByProviderKind
14683
+ );
14684
+ return { kindRegistry, providerResolution, reservedNodePaths };
14685
+ }
14686
+ function buildProviderIndexes(kernel) {
14687
+ const kindRegistry = /* @__PURE__ */ new Map();
14688
+ const providerResolution = /* @__PURE__ */ new Map();
14689
+ const reservedNamesByProviderKind = /* @__PURE__ */ new Map();
14690
+ const providers = kernel.registry.all("provider");
14691
+ for (const provider of providers) {
14692
+ if (provider.kinds) {
14693
+ for (const [kindName, descriptor] of Object.entries(provider.kinds)) {
14694
+ kindRegistry.set(`${provider.id}/${kindName}`, descriptor);
14695
+ }
14696
+ }
14697
+ if (provider.resolution) {
14698
+ providerResolution.set(provider.id, provider.resolution);
14699
+ }
14700
+ if (provider.reservedNames) {
14701
+ indexReservedNames(provider, reservedNamesByProviderKind);
14702
+ }
14703
+ }
14704
+ return { kindRegistry, providerResolution, reservedNamesByProviderKind };
14705
+ }
14706
+ function indexReservedNames(provider, out) {
14707
+ for (const [kindName, list] of Object.entries(provider.reservedNames ?? {})) {
14708
+ const normalised = new Set(list.map((raw) => normalizeTrigger(raw)).filter(Boolean));
14709
+ if (normalised.size > 0) {
14710
+ out.set(`${provider.id}/${kindName}`, normalised);
14711
+ }
14712
+ }
14713
+ }
14714
+ function buildReservedNodePaths(nodes, kindRegistry, reservedNamesByProviderKind) {
14715
+ const out = /* @__PURE__ */ new Set();
14716
+ for (const node of nodes) {
14717
+ const key = `${node.provider}/${node.kind}`;
14718
+ const reservedSet = reservedNamesByProviderKind.get(key);
14719
+ if (!reservedSet || reservedSet.size === 0) continue;
14720
+ const ids = deriveNodeIdentifiers(node, kindRegistry.get(key));
14721
+ if (ids.some((id) => reservedSet.has(id))) {
14722
+ out.add(node.path);
14723
+ }
14724
+ }
14725
+ return out;
14726
+ }
14513
14727
  function buildScanSetup(options) {
14514
14728
  const start = Date.now();
14515
14729
  const emitter = options.emitter ?? new InMemoryProgressEmitter();
@@ -14601,7 +14815,7 @@ function validateRoots(roots) {
14601
14815
  function resolveActiveProviderOption(optionValue, roots) {
14602
14816
  if (optionValue !== void 0) return optionValue;
14603
14817
  for (const root of roots) {
14604
- const absRoot = isAbsolute7(root) ? root : resolve27(root);
14818
+ const absRoot = isAbsolute7(root) ? root : resolve28(root);
14605
14819
  if (!existsSync22(absRoot)) continue;
14606
14820
  const detected = resolveActiveProvider(absRoot).resolved;
14607
14821
  if (detected !== null) return detected;
@@ -14610,10 +14824,10 @@ function resolveActiveProviderOption(optionValue, roots) {
14610
14824
  }
14611
14825
 
14612
14826
  // kernel/scan/watcher.ts
14613
- import { resolve as resolve28, relative as relative5, sep as sep4 } from "path";
14827
+ import { resolve as resolve29, relative as relative5, sep as sep4 } from "path";
14614
14828
  import chokidar from "chokidar";
14615
14829
  function createChokidarWatcher(opts) {
14616
- const absRoots = opts.roots.map((r) => resolve28(opts.cwd, r));
14830
+ const absRoots = opts.roots.map((r) => resolve29(opts.cwd, r));
14617
14831
  const ignoreFilterOpt = opts.ignoreFilter;
14618
14832
  const getFilter = ignoreFilterOpt === void 0 ? void 0 : typeof ignoreFilterOpt === "function" ? ignoreFilterOpt : () => ignoreFilterOpt;
14619
14833
  const ignored = getFilter ? (path) => {
@@ -14832,7 +15046,7 @@ function createKernel() {
14832
15046
 
14833
15047
  // kernel/jobs/orphan-files.ts
14834
15048
  import { readdirSync as readdirSync8, statSync as statSync8 } from "fs";
14835
- import { join as join14, resolve as resolve29 } from "path";
15049
+ import { join as join14, resolve as resolve30 } from "path";
14836
15050
  function findOrphanJobFiles(jobsDir, referencedPaths) {
14837
15051
  let entries;
14838
15052
  try {
@@ -14850,7 +15064,7 @@ function findOrphanJobFiles(jobsDir, referencedPaths) {
14850
15064
  if (!entry.isFile()) continue;
14851
15065
  const name = entry.name;
14852
15066
  if (!name.endsWith(".md")) continue;
14853
- const abs = resolve29(join14(jobsDir, name));
15067
+ const abs = resolve30(join14(jobsDir, name));
14854
15068
  if (!referencedPaths.has(abs)) orphans.push(abs);
14855
15069
  }
14856
15070
  orphans.sort();
@@ -14917,12 +15131,12 @@ var SCAN_RUNNER_TEXTS = {
14917
15131
  referenceWalkMissingRoot: 'scan.referencePaths: configured path "{{path}}" does not exist; skipped.',
14918
15132
  /**
14919
15133
  * Active-provider bootstrap: filesystem auto-detect found no
14920
- * markers (`.claude/`, `.gemini/`, `.codex/`, `AGENTS.md`, `.cursor/`)
14921
- * anywhere under cwd or the effective scan roots. Plain-markdown
14922
- * projects keep scanning fine; provider-specific extractors silently
14923
- * no-op for this scan.
15134
+ * markers (`.claude/`, `.codex/`, `AGENTS.md`, `.cursor/`) anywhere
15135
+ * under cwd or the effective scan roots. Plain-markdown projects
15136
+ * keep scanning fine; provider-specific extractors silently no-op
15137
+ * for this scan.
14924
15138
  */
14925
- activeProviderNoMarkerWarning: "No provider markers detected (.claude/, .gemini/, .codex/, AGENTS.md, .cursor/). Scanning as universal markdown only; provider-specific link types (e.g. claude @-directives, /-commands) will not appear in the graph. Set `activeProvider` in .skill-map/settings.json or install a provider plugin to enable them.",
15139
+ activeProviderNoMarkerWarning: "No provider markers detected (.claude/, .codex/, AGENTS.md, .cursor/). Scanning as universal markdown only; provider-specific link types (e.g. claude @-directives, /-commands) will not appear in the graph. Set `activeProvider` in .skill-map/settings.json or install a provider plugin to enable them.",
14926
15140
  /**
14927
15141
  * Active-provider bootstrap: filesystem auto-detect found exactly
14928
15142
  * one marker and persisted the detected id to project settings.
@@ -14969,7 +15183,7 @@ function resolveScanRoots(inputs) {
14969
15183
  // core/runtime/reference-paths-walker.ts
14970
15184
  import { readdirSync as readdirSync9, statSync as statSync9 } from "fs";
14971
15185
  import { homedir as osHomedir2 } from "os";
14972
- import { isAbsolute as isAbsolute8, join as join15, resolve as resolve30 } from "path";
15186
+ import { isAbsolute as isAbsolute8, join as join15, resolve as resolve31 } from "path";
14973
15187
  var REFERENCE_WALK_MAX_FILES = 5e4;
14974
15188
  var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
14975
15189
  "node_modules",
@@ -14977,10 +15191,10 @@ var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
14977
15191
  SKILL_MAP_DIR
14978
15192
  ]);
14979
15193
  function resolveScanPath(raw, cwd) {
14980
- if (raw.startsWith("~/")) return resolve30(join15(osHomedir2(), raw.slice(2)));
14981
- if (raw === "~") return resolve30(osHomedir2());
14982
- if (isAbsolute8(raw)) return resolve30(raw);
14983
- return resolve30(cwd, raw);
15194
+ if (raw.startsWith("~/")) return resolve31(join15(osHomedir2(), raw.slice(2)));
15195
+ if (raw === "~") return resolve31(osHomedir2());
15196
+ if (isAbsolute8(raw)) return resolve31(raw);
15197
+ return resolve31(cwd, raw);
14984
15198
  }
14985
15199
  function walkReferencePaths(rawRoots, cwd) {
14986
15200
  const paths = /* @__PURE__ */ new Set();
@@ -17274,9 +17488,9 @@ var PLUGINS_TEXTS = {
17274
17488
  };
17275
17489
 
17276
17490
  // cli/commands/plugins/shared.ts
17277
- import { resolve as resolve31 } from "path";
17491
+ import { resolve as resolve32 } from "path";
17278
17492
  function resolveSearchPaths2(opts, cwd) {
17279
- if (opts.pluginDir) return [resolve31(opts.pluginDir)];
17493
+ if (opts.pluginDir) return [resolve32(opts.pluginDir)];
17280
17494
  return [defaultProjectPluginsDir({ cwd })];
17281
17495
  }
17282
17496
  async function buildResolver() {
@@ -18298,7 +18512,7 @@ var PluginsEnableCommand = class extends TogglePluginsBase {
18298
18512
  baseline.
18299
18513
 
18300
18514
  Accepts one or more ids in one call, e.g.
18301
- 'sm plugins enable claude gemini openai'. Batches are
18515
+ 'sm plugins enable claude antigravity openai'. Batches are
18302
18516
  all-or-nothing: a single unknown / mismatched id aborts before
18303
18517
  any write. Repeated ids are deduped. Locked plugins inside a
18304
18518
  batch are silently skipped.
@@ -18326,7 +18540,7 @@ var PluginsDisableCommand = class extends TogglePluginsBase {
18326
18540
  will not run them.
18327
18541
 
18328
18542
  Accepts one or more ids in one call, e.g.
18329
- 'sm plugins disable gemini openai agent-skills'. Batches are
18543
+ 'sm plugins disable antigravity openai agent-skills'. Batches are
18330
18544
  all-or-nothing: a single unknown / mismatched id aborts before
18331
18545
  any write. Repeated ids are deduped. Locked plugins inside a
18332
18546
  batch are silently skipped.
@@ -18436,7 +18650,7 @@ function resolveBareToggle(id, catalogue, verb, ansi) {
18436
18650
 
18437
18651
  // cli/commands/plugins/create.ts
18438
18652
  import { existsSync as existsSync23, mkdirSync as mkdirSync6, writeFileSync as writeFileSync3 } from "fs";
18439
- import { join as join18, resolve as resolve32 } from "path";
18653
+ import { join as join18, resolve as resolve33 } from "path";
18440
18654
  import { Command as Command26, Option as Option25 } from "clipanion";
18441
18655
  var PluginsCreateCommand = class extends SmCommand {
18442
18656
  static paths = [["plugins", "create"]];
@@ -18462,7 +18676,7 @@ var PluginsCreateCommand = class extends SmCommand {
18462
18676
  }
18463
18677
  const ctx = defaultRuntimeContext();
18464
18678
  const baseDir = defaultProjectPluginsDir(ctx);
18465
- const targetDir = this.at ? resolve32(this.at) : join18(baseDir, this.pluginId);
18679
+ const targetDir = this.at ? resolve33(this.at) : join18(baseDir, this.pluginId);
18466
18680
  if (existsSync23(targetDir) && !this.force) {
18467
18681
  this.printer.error(
18468
18682
  tx(PLUGINS_TEXTS.createRefuseOverwrite, {
@@ -18703,7 +18917,7 @@ var PLUGIN_COMMANDS = [
18703
18917
 
18704
18918
  // cli/commands/refresh.ts
18705
18919
  import { readFile as readFile3 } from "fs/promises";
18706
- import { resolve as resolve33 } from "path";
18920
+ import { resolve as resolve34 } from "path";
18707
18921
  import { Command as Command29, Option as Option27 } from "clipanion";
18708
18922
 
18709
18923
  // cli/i18n/refresh.texts.ts
@@ -18987,7 +19201,7 @@ var RefreshCommand = class extends SmCommand {
18987
19201
  let body;
18988
19202
  try {
18989
19203
  assertContained(cwd, node.path);
18990
- const raw = await readFile3(resolve33(cwd, node.path), "utf8");
19204
+ const raw = await readFile3(resolve34(cwd, node.path), "utf8");
18991
19205
  body = stripFrontmatterFence(raw);
18992
19206
  } catch (err) {
18993
19207
  if (!this.json) {
@@ -19729,7 +19943,7 @@ var ScanCommand = class extends SmCommand {
19729
19943
  description: "Long-running mode: watch the roots and trigger an incremental scan after each debounced batch of filesystem events. Alias of `sm watch`."
19730
19944
  });
19731
19945
  yes = Option29.Boolean("--yes", false, {
19732
- description: "Non-interactive mode for ambiguous activeProvider auto-detect. With `--yes`, multiple provider markers (.claude/, .gemini/, .codex/, AGENTS.md, .cursor/) under the scan tree exit non-zero instead of prompting the operator. Set the lens manually via `sm config set activeProvider <id>` and re-run."
19946
+ description: "Non-interactive mode for ambiguous activeProvider auto-detect. With `--yes`, multiple provider markers (.claude/, .codex/, AGENTS.md, .cursor/) under the scan tree exit non-zero instead of prompting the operator. Set the lens manually via `sm config set activeProvider <id>` and re-run."
19733
19947
  });
19734
19948
  // Each branch in the orchestrator maps to one validation gate
19735
19949
  // (--watch alias / --changed mutex / -g mutex / dispatch).
@@ -21721,10 +21935,10 @@ import { HTTPException as HTTPException10 } from "hono/http-exception";
21721
21935
 
21722
21936
  // server/util/skillmapignore-io.ts
21723
21937
  import { existsSync as existsSync26, readFileSync as readFileSync19, writeFileSync as writeFileSync4 } from "fs";
21724
- import { resolve as resolve34 } from "path";
21938
+ import { resolve as resolve35 } from "path";
21725
21939
  var IGNORE_FILENAME2 = ".skillmapignore";
21726
21940
  function readPatterns(cwd) {
21727
- const path = resolve34(cwd, IGNORE_FILENAME2);
21941
+ const path = resolve35(cwd, IGNORE_FILENAME2);
21728
21942
  if (!existsSync26(path)) return [];
21729
21943
  let raw;
21730
21944
  try {
@@ -21735,7 +21949,7 @@ function readPatterns(cwd) {
21735
21949
  return raw.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
21736
21950
  }
21737
21951
  function writePatterns(cwd, nextPatterns) {
21738
- const path = resolve34(cwd, IGNORE_FILENAME2);
21952
+ const path = resolve35(cwd, IGNORE_FILENAME2);
21739
21953
  const prior = existsSync26(path) ? safeRead(path) : "";
21740
21954
  const content = buildContent(prior, nextPatterns);
21741
21955
  writeFileSync4(path, content, "utf8");
@@ -22156,14 +22370,14 @@ async function withScanMutex(fn) {
22156
22370
  if (inFlight !== null) {
22157
22371
  throw new ScanBusyError();
22158
22372
  }
22159
- let resolve39;
22373
+ let resolve40;
22160
22374
  inFlight = new Promise((r) => {
22161
- resolve39 = r;
22375
+ resolve40 = r;
22162
22376
  });
22163
22377
  try {
22164
22378
  return await fn();
22165
22379
  } finally {
22166
- resolve39();
22380
+ resolve40();
22167
22381
  inFlight = null;
22168
22382
  }
22169
22383
  }
@@ -22475,7 +22689,7 @@ function emptyScanResult() {
22475
22689
 
22476
22690
  // server/routes/sidecar.ts
22477
22691
  import { HTTPException as HTTPException14 } from "hono/http-exception";
22478
- import { resolve as resolve35 } from "path";
22692
+ import { resolve as resolve36 } from "path";
22479
22693
  var STATUS_FRESH = "fresh";
22480
22694
  var ENVELOPE_KIND2 = "sidecar.bumped";
22481
22695
  var BUMP_BODY_SCHEMA = {
@@ -22509,7 +22723,7 @@ function registerSidecarRoutes(app, deps) {
22509
22723
  let absPath;
22510
22724
  try {
22511
22725
  assertContained(deps.runtimeContext.cwd, node.path);
22512
- absPath = resolve35(deps.runtimeContext.cwd, node.path);
22726
+ absPath = resolve36(deps.runtimeContext.cwd, node.path);
22513
22727
  } catch (err) {
22514
22728
  throw new HTTPException14(500, { message: formatErrorMessage(err) });
22515
22729
  }
@@ -23261,7 +23475,7 @@ function validateNoUi(noUi, uiDist) {
23261
23475
 
23262
23476
  // server/paths.ts
23263
23477
  import { existsSync as existsSync29, statSync as statSync11 } from "fs";
23264
- import { dirname as dirname18, isAbsolute as isAbsolute11, join as join20, resolve as resolve36 } from "path";
23478
+ import { dirname as dirname18, isAbsolute as isAbsolute11, join as join20, resolve as resolve37 } from "path";
23265
23479
  import { fileURLToPath as fileURLToPath5 } from "url";
23266
23480
  var DEFAULT_UI_REL = join20("ui", "dist", "ui", "browser");
23267
23481
  var PACKAGE_UI_REL = "ui";
@@ -23272,7 +23486,7 @@ function resolveDefaultUiDist(ctx) {
23272
23486
  return walkUpForUi(ctx.cwd);
23273
23487
  }
23274
23488
  function resolveExplicitUiDist(ctx, raw) {
23275
- return isAbsolute11(raw) ? raw : resolve36(ctx.cwd, raw);
23489
+ return isAbsolute11(raw) ? raw : resolve37(ctx.cwd, raw);
23276
23490
  }
23277
23491
  function isUiBundleDir(path) {
23278
23492
  if (!existsSync29(path)) return false;
@@ -23306,7 +23520,7 @@ function resolvePackageBundledUiFrom(here) {
23306
23520
  return null;
23307
23521
  }
23308
23522
  function walkUpForUi(startDir) {
23309
- let current = resolve36(startDir);
23523
+ let current = resolve37(startDir);
23310
23524
  for (let i = 0; i < 64; i++) {
23311
23525
  const candidate = join20(current, DEFAULT_UI_REL);
23312
23526
  if (isUiBundleDir(candidate)) return candidate;
@@ -24246,7 +24460,7 @@ function rankConfidenceForGrouping(c) {
24246
24460
 
24247
24461
  // cli/commands/sidecar.ts
24248
24462
  import { existsSync as existsSync31, unlinkSync as unlinkSync2 } from "fs";
24249
- import { resolve as resolve37 } from "path";
24463
+ import { resolve as resolve38 } from "path";
24250
24464
  import { Command as Command35, Option as Option33 } from "clipanion";
24251
24465
 
24252
24466
  // cli/i18n/sidecar.texts.ts
@@ -24397,7 +24611,7 @@ var SidecarRefreshCommand = class extends SmCommand {
24397
24611
  let absPath;
24398
24612
  try {
24399
24613
  assertContained(ctx.cwd, node.path);
24400
- absPath = resolve37(ctx.cwd, node.path);
24614
+ absPath = resolve38(ctx.cwd, node.path);
24401
24615
  } catch (err) {
24402
24616
  this.printer.error(
24403
24617
  tx(SIDECAR_TEXTS.refreshFailed, { glyph: errGlyph, message: formatErrorMessage(err) })
@@ -24678,7 +24892,7 @@ var SidecarAnnotateCommand = class extends SmCommand {
24678
24892
  let absPath;
24679
24893
  try {
24680
24894
  assertContained(ctx.cwd, node.path);
24681
- absPath = resolve37(ctx.cwd, node.path);
24895
+ absPath = resolve38(ctx.cwd, node.path);
24682
24896
  } catch (err) {
24683
24897
  this.printer.error(
24684
24898
  tx(SIDECAR_TEXTS.annotateFailed, { glyph: errGlyph, message: formatErrorMessage(err) })
@@ -24927,7 +25141,7 @@ var STUB_COMMANDS = [
24927
25141
 
24928
25142
  // cli/commands/tutorial.ts
24929
25143
  import { cpSync as cpSync2, existsSync as existsSync32, mkdirSync as mkdirSync7, rmSync as rmSync2, statSync as statSync12 } from "fs";
24930
- import { dirname as dirname19, join as join21, resolve as resolve38 } from "path";
25144
+ import { dirname as dirname19, join as join21, resolve as resolve39 } from "path";
24931
25145
  import { fileURLToPath as fileURLToPath6 } from "url";
24932
25146
  import { Command as Command37, Option as Option35 } from "clipanion";
24933
25147
 
@@ -25101,11 +25315,11 @@ function resolveSkillSourceDir(variant) {
25101
25315
  const here = dirname19(fileURLToPath6(import.meta.url));
25102
25316
  const candidates = [
25103
25317
  // dev: src/cli/commands/ → repo-root .claude/skills/<slug>/
25104
- resolve38(here, "../../..", spec.sourceDir),
25318
+ resolve39(here, "../../..", spec.sourceDir),
25105
25319
  // bundled: dist/cli.js → dist/cli/tutorial/<slug> (sibling)
25106
- resolve38(here, "cli/tutorial", spec.slug),
25320
+ resolve39(here, "cli/tutorial", spec.slug),
25107
25321
  // bundled fallback: any-depth → cli/tutorial/<slug>
25108
- resolve38(here, "../cli/tutorial", spec.slug)
25322
+ resolve39(here, "../cli/tutorial", spec.slug)
25109
25323
  ];
25110
25324
  for (const candidate of candidates) {
25111
25325
  if (existsSync32(candidate) && statSync12(candidate).isDirectory()) {