@skill-map/cli 0.41.0 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // cli/entry.ts
2
- import { existsSync as existsSync30 } from "fs";
2
+ import { existsSync as existsSync32 } from "fs";
3
3
  import { Builtins, Cli as Cli2 } from "clipanion";
4
4
 
5
5
  // kernel/adapters/in-memory-progress.ts
@@ -244,11 +244,11 @@ function bucketByKind(kind, instance, bag) {
244
244
  // plugins/claude/providers/claude/schemas/skill.schema.json
245
245
  var skill_schema_default = {
246
246
  $schema: "https://json-schema.org/draft/2020-12/schema",
247
- $id: "https://skill-map.dev/providers/claude/v1/frontmatter/skill.schema.json",
247
+ $id: "https://skill-map.ai/providers/claude/v1/frontmatter/skill.schema.json",
248
248
  title: "FrontmatterSkill",
249
249
  description: "Frontmatter shape for nodes classified as `skill` by the Claude Provider. Today identical to `command` \u2014 both extend the shared `skill-base.schema.json` per Anthropic's documented merger (https://code.claude.com/docs/en/skills.md \u2014 \"Custom commands have been merged into skills\"). The two are kept SPLIT (not aliased) because skill-map's registry differentiates them in `IProviderKind.ui` (separate label / icon / color) and `defaultRefreshAction`. Splitting also reserves room for future divergence when one kind diverges from the other. No skill-only fields today; `additionalProperties: true` so the file is ready for them.",
250
250
  allOf: [
251
- { $ref: "https://skill-map.dev/providers/claude/v1/frontmatter/skill-base.schema.json" }
251
+ { $ref: "https://skill-map.ai/providers/claude/v1/frontmatter/skill-base.schema.json" }
252
252
  ],
253
253
  type: "object",
254
254
  additionalProperties: true,
@@ -258,11 +258,11 @@ var skill_schema_default = {
258
258
  // plugins/claude/providers/claude/schemas/skill-base.schema.json
259
259
  var skill_base_schema_default = {
260
260
  $schema: "https://json-schema.org/draft/2020-12/schema",
261
- $id: "https://skill-map.dev/providers/claude/v1/frontmatter/skill-base.schema.json",
261
+ $id: "https://skill-map.ai/providers/claude/v1/frontmatter/skill-base.schema.json",
262
262
  title: "FrontmatterSkillBase",
263
263
  description: 'Shared frontmatter base for `skill` and `command` nodes per Anthropic\'s documented merger (https://code.claude.com/docs/en/skills.md \u2014 "Custom commands have been merged into skills"). Both kinds carry the same vendor frontmatter today; skill-map keeps them as distinct `IProviderKind`s in the registry (different UI presentation, different `defaultRefreshAction`) but extends the same base via `allOf` + `$ref` so the field catalog is single-sourced. Field naming is reproduced verbatim from Anthropic \u2014 a deliberate mix of kebab-case (`argument-hint`, `disable-model-invocation`, `user-invocable`, `allowed-tools`), snake_case (`when_to_use`), and camelCase. `additionalProperties: true` so future Anthropic additions flow through unchanged until this schema catches up.',
264
264
  allOf: [
265
- { $ref: "https://skill-map.dev/spec/v0/frontmatter/base.schema.json" }
265
+ { $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
266
266
  ],
267
267
  type: "object",
268
268
  additionalProperties: true,
@@ -297,6 +297,13 @@ var skill_base_schema_default = {
297
297
  ],
298
298
  description: "Tools pre-approved for this skill (no per-use permission prompt). Argument-scoped patterns supported (`Bash(git add *)`)."
299
299
  },
300
+ "disallowed-tools": {
301
+ oneOf: [
302
+ { type: "string" },
303
+ { type: "array", items: { type: "string" } }
304
+ ],
305
+ description: "Denylist sibling of `allowed-tools`: tools removed from the available pool while this skill / command is active (the restriction clears on the next user message). Accepts a space- or comma-separated string or a YAML list. Source: https://code.claude.com/docs/en/skills.md."
306
+ },
300
307
  model: {
301
308
  type: "string",
302
309
  description: "Model alias (`sonnet`, `opus`, `haiku`), full Claude id, or the literal `inherit` to defer to the parent session's model."
@@ -337,11 +344,11 @@ var skill_base_schema_default = {
337
344
  // plugins/claude/providers/claude/schemas/agent.schema.json
338
345
  var agent_schema_default = {
339
346
  $schema: "https://json-schema.org/draft/2020-12/schema",
340
- $id: "https://skill-map.dev/providers/claude/v1/frontmatter/agent.schema.json",
347
+ $id: "https://skill-map.ai/providers/claude/v1/frontmatter/agent.schema.json",
341
348
  title: "FrontmatterAgent",
342
- description: "Frontmatter shape for nodes classified as `agent` by the Claude Provider. Mirrors Anthropic's documented agent frontmatter verbatim (https://code.claude.com/docs/en/agents.md): `name` and `description` come from the spec base; this schema adds the 14 vendor-specific fields. skill-map AGGREGATES the vendor spec, it does not curate it \u2014 keys are reproduced exactly as Anthropic publishes them (mix of camelCase and snake_case). `additionalProperties: true` so future Anthropic additions flow through unchanged until this schema catches up.",
349
+ description: "Frontmatter shape for nodes classified as `agent` by the Claude Provider. Mirrors Anthropic's documented agent frontmatter verbatim (https://code.claude.com/docs/en/sub-agents.md): `name` and `description` come from the spec base; this schema adds the 14 vendor-specific fields. skill-map AGGREGATES the vendor spec, it does not curate it \u2014 keys are reproduced exactly as Anthropic publishes them (mix of camelCase and snake_case). `additionalProperties: true` so future Anthropic additions flow through unchanged until this schema catches up.",
343
350
  allOf: [
344
- { $ref: "https://skill-map.dev/spec/v0/frontmatter/base.schema.json" }
351
+ { $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
345
352
  ],
346
353
  type: "object",
347
354
  additionalProperties: true,
@@ -363,7 +370,7 @@ var agent_schema_default = {
363
370
  permissionMode: {
364
371
  type: "string",
365
372
  enum: ["default", "acceptEdits", "auto", "dontAsk", "bypassPermissions", "plan"],
366
- description: "How the agent handles permission prompts. See https://code.claude.com/docs/en/agents.md."
373
+ description: "How the agent handles permission prompts. See https://code.claude.com/docs/en/sub-agents.md."
367
374
  },
368
375
  maxTurns: {
369
376
  type: "integer",
@@ -418,11 +425,11 @@ var agent_schema_default = {
418
425
  // plugins/claude/providers/claude/schemas/command.schema.json
419
426
  var command_schema_default = {
420
427
  $schema: "https://json-schema.org/draft/2020-12/schema",
421
- $id: "https://skill-map.dev/providers/claude/v1/frontmatter/command.schema.json",
428
+ $id: "https://skill-map.ai/providers/claude/v1/frontmatter/command.schema.json",
422
429
  title: "FrontmatterCommand",
423
430
  description: "Frontmatter shape for nodes classified as `command` by the Claude Provider. Today identical to `skill` per Anthropic's documented merger (https://code.claude.com/docs/en/skills.md \u2014 \"Custom commands have been merged into skills\"). Both extend the shared `skill-base.schema.json` via the same `allOf` pattern. The two are kept SPLIT (not aliased) because skill-map's registry differentiates them in `IProviderKind.ui` (separate label / icon / color) and `defaultRefreshAction`. Splitting also reserves room for future divergence. No command-only fields today; `additionalProperties: true` so the file is ready for them.",
424
431
  allOf: [
425
- { $ref: "https://skill-map.dev/providers/claude/v1/frontmatter/skill-base.schema.json" }
432
+ { $ref: "https://skill-map.ai/providers/claude/v1/frontmatter/skill-base.schema.json" }
426
433
  ],
427
434
  type: "object",
428
435
  additionalProperties: true,
@@ -442,6 +449,19 @@ var claudeProvider = {
442
449
  pluginId: CLAUDE_PLUGIN_ID,
443
450
  kind: "provider",
444
451
  description: "Classifies files under `.claude/{agents,commands,skills}` as Claude Code agents, commands, and skills.",
452
+ // Provider identity for the active-lens dropdown, the topbar lens chip,
453
+ // and the per-node provider chip. Anthropic brand terracotta; the dark
454
+ // variant lifts luminosity for dark mode. Verbatim from the previous
455
+ // static UI catalog (`ui/src/services/provider-ui.ts`).
456
+ presentation: {
457
+ label: "Claude",
458
+ color: "#cc785c",
459
+ colorDark: "#e89270"
460
+ },
461
+ // Auto-detect marker: a `.claude/` directory under the scope root marks
462
+ // a Claude Code project. Provider-owned (replaces the old central
463
+ // detection table in `src/core/config/active-provider.ts`).
464
+ detect: { markers: [".claude"] },
445
465
  // Vendor provider: Claude Code only reads its own `.claude/` territory
446
466
  // and ignores `.codex/` / Antigravity layouts at runtime. Gating the
447
467
  // classifier behind the active lens prevents the walker from inventing
@@ -485,7 +505,7 @@ var claudeProvider = {
485
505
  icon: { kind: "pi", id: "pi-user" }
486
506
  },
487
507
  // `frontmatter.name` is the documented canonical identifier
488
- // (https://code.claude.com/docs/en/agents.md); `filename-basename`
508
+ // (https://code.claude.com/docs/en/sub-agents.md); `filename-basename`
489
509
  // is a graceful fallback for agents authored without an explicit
490
510
  // `name:` field, the file at `.claude/agents/<id>.md` resolves
491
511
  // `@<id>` even when frontmatter is partial.
@@ -863,6 +883,18 @@ var antigravityProvider = {
863
883
  pluginId: ANTIGRAVITY_PLUGIN_ID,
864
884
  kind: "provider",
865
885
  description: "Declares the Google Antigravity runtime and its reserved built-in names.",
886
+ // Provider identity for the active-lens dropdown, the topbar lens chip,
887
+ // and the per-node provider chip. Antigravity violet, distinct from the
888
+ // other vendor palettes.
889
+ presentation: {
890
+ label: "Antigravity",
891
+ color: "#7c3aed",
892
+ colorDark: "#a78bfa"
893
+ },
894
+ // No `detect` block: Antigravity has no vendor-specific workspace marker
895
+ // (it adopted the open-standard `.agents/`, owned by `agent-skills`), so
896
+ // it is never auto-suggested. The lens is set manually via
897
+ // `sm config set activeProvider antigravity`.
866
898
  // Vendor provider: marked gated for the day Antigravity grows its own
867
899
  // on-disk kind beyond the open standard. Today `kinds: {}` and
868
900
  // `classify` returns `null` for every path, so the flag is inert; the
@@ -956,11 +988,11 @@ var antigravityProvider = {
956
988
  // plugins/openai/providers/openai/schemas/agent.schema.json
957
989
  var agent_schema_default2 = {
958
990
  $schema: "https://json-schema.org/draft/2020-12/schema",
959
- $id: "https://skill-map.dev/providers/openai/v1/frontmatter/agent.schema.json",
991
+ $id: "https://skill-map.ai/providers/openai/v1/frontmatter/agent.schema.json",
960
992
  title: "FrontmatterCodexAgent",
961
993
  description: "Frontmatter shape for nodes classified as `agent` by the OpenAI Codex Provider. Codex sub-agents live as TOML files under `.codex/agents/<name>.toml`; the entire file IS the agent definition (no markdown body). The TOML parser feeds the parsed root object into `frontmatter`, so this schema validates the same shape skill-map's other providers carry on per-kind frontmatter. Mirrors Codex's documented sub-agent fields (https://github.com/openai/codex) with `additionalProperties: true` so future additions flow through unchanged.",
962
994
  allOf: [
963
- { $ref: "https://skill-map.dev/spec/v0/frontmatter/base.schema.json" }
995
+ { $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
964
996
  ],
965
997
  type: "object",
966
998
  additionalProperties: true,
@@ -1011,6 +1043,18 @@ var openaiProvider = {
1011
1043
  pluginId: OPENAI_PLUGIN_ID,
1012
1044
  kind: "provider",
1013
1045
  description: "Classifies files under `.codex/agents/*.toml` as OpenAI Codex CLI sub-agents.",
1046
+ // Provider identity for the active-lens dropdown, the topbar lens chip,
1047
+ // and the per-node provider chip. Codex green, distinct from the Claude
1048
+ // palette so the chip reads at a glance.
1049
+ presentation: {
1050
+ label: "OpenAI Codex",
1051
+ color: "#22c55e",
1052
+ colorDark: "#4ade80"
1053
+ },
1054
+ // Auto-detect markers: a `.codex/` directory or a root `AGENTS.md` marks
1055
+ // a Codex CLI project. Provider-owned (replaces the old central
1056
+ // detection table in `src/core/config/active-provider.ts`).
1057
+ detect: { markers: [".codex", "AGENTS.md"] },
1014
1058
  // Vendor provider: Codex CLI only reads its own `.codex/` territory.
1015
1059
  // Gating the classifier behind the active lens keeps the walker from
1016
1060
  // claiming Codex agents under a `claude` (or any other) lens, where
@@ -1051,15 +1095,34 @@ var openaiProvider = {
1051
1095
  // plugins/agent-skills/providers/agent-skills/schemas/skill.schema.json
1052
1096
  var skill_schema_default2 = {
1053
1097
  $schema: "https://json-schema.org/draft/2020-12/schema",
1054
- $id: "https://skill-map.dev/providers/agent-skills/v1/frontmatter/skill.schema.json",
1098
+ $id: "https://skill-map.ai/providers/agent-skills/v1/frontmatter/skill.schema.json",
1055
1099
  title: "FrontmatterAgentSkillsSkill",
1056
- description: "Frontmatter shape for nodes classified as `skill` by the neutral `agent-skills` Provider \u2014 Agent Skills delivered as `SKILL.md` files at the open-standard path `.agents/skills/<name>/SKILL.md`. Jointly adopted by Anthropic, OpenAI (Codex), and Google (Gemini); the path is vendor-neutral so no single Provider should own it. Required fields are `name` and `description` (from spec base); per the open-standard contract no other fields are mandated.",
1100
+ description: "Frontmatter shape for nodes classified as `skill` by the neutral `agent-skills` Provider, Agent Skills delivered as `SKILL.md` files at the open-standard path `.agents/skills/<name>/SKILL.md`. Jointly adopted by Anthropic, OpenAI (Codex), and Google (Gemini); the path is vendor-neutral so no single Provider should own it. Required fields are `name` and `description` (from spec base). The standard's optional frontmatter fields are declared below, mirrored verbatim from https://agentskills.io/specification: `license`, `compatibility`, `metadata`, and the experimental `allowed-tools`. `additionalProperties: true` so any future standard field flows through unchanged until this schema catches up.",
1057
1101
  allOf: [
1058
- { $ref: "https://skill-map.dev/spec/v0/frontmatter/base.schema.json" }
1102
+ { $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
1059
1103
  ],
1060
1104
  type: "object",
1061
1105
  additionalProperties: true,
1062
- properties: {}
1106
+ properties: {
1107
+ license: {
1108
+ type: "string",
1109
+ description: "License applied to the skill: a license name (e.g. `Apache-2.0`) or a reference to a bundled license file. Source: https://agentskills.io/specification."
1110
+ },
1111
+ compatibility: {
1112
+ type: "string",
1113
+ maxLength: 500,
1114
+ description: "Environment requirements (intended product, required system packages, network access, etc.). Most skills omit it. Max 500 characters. Source: https://agentskills.io/specification."
1115
+ },
1116
+ metadata: {
1117
+ type: "object",
1118
+ additionalProperties: { type: "string" },
1119
+ description: "Arbitrary string-keyed, string-valued map for client-defined metadata not covered by the standard. Source: https://agentskills.io/specification."
1120
+ },
1121
+ "allowed-tools": {
1122
+ type: "string",
1123
+ description: "Space-separated list of pre-approved tools the skill may run (e.g. `Bash(git:*) Read`). Experimental in the open standard; support varies between agent implementations. Source: https://agentskills.io/specification."
1124
+ }
1125
+ }
1063
1126
  };
1064
1127
 
1065
1128
  // plugins/agent-skills/providers/agent-skills/index.ts
@@ -1068,6 +1131,20 @@ var agentSkillsProvider = {
1068
1131
  pluginId: AGENT_SKILLS_PLUGIN_ID,
1069
1132
  kind: "provider",
1070
1133
  description: "Classifies files under `.agents/skills/<name>/SKILL.md` as Agent Skills.",
1134
+ // Provider identity for the active-lens dropdown, the topbar lens chip,
1135
+ // and the per-node provider chip. Neutral slate (this is the
1136
+ // vendor-agnostic open-standard Provider, not a brand). Verbatim from
1137
+ // the previous static UI catalog (`ui/src/services/provider-ui.ts`).
1138
+ presentation: {
1139
+ label: "Open Skills",
1140
+ color: "#64748b",
1141
+ colorDark: "#94a3b8"
1142
+ },
1143
+ // Auto-detect marker: a `.agents/` directory marks an open-standard
1144
+ // project. This is also the marker a Google/Antigravity project carries
1145
+ // (Antigravity adopted the open standard), so such projects auto-detect
1146
+ // as this universal lens. Provider-owned.
1147
+ detect: { markers: [".agents"] },
1071
1148
  read: { extensions: [".md"], parser: "frontmatter-yaml" },
1072
1149
  kinds: {
1073
1150
  skill: {
@@ -1099,11 +1176,11 @@ var agentSkillsProvider = {
1099
1176
  // plugins/core/providers/core-markdown/schemas/markdown.schema.json
1100
1177
  var markdown_schema_default = {
1101
1178
  $schema: "https://json-schema.org/draft/2020-12/schema",
1102
- $id: "https://skill-map.dev/providers/core/v1/frontmatter/markdown.schema.json",
1179
+ $id: "https://skill-map.ai/providers/core/v1/frontmatter/markdown.schema.json",
1103
1180
  title: "FrontmatterMarkdown",
1104
1181
  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.",
1105
1182
  allOf: [
1106
- { $ref: "https://skill-map.dev/spec/v0/frontmatter/base.schema.json" }
1183
+ { $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
1107
1184
  ],
1108
1185
  type: "object",
1109
1186
  additionalProperties: true
@@ -1115,6 +1192,20 @@ var coreMarkdownProvider = {
1115
1192
  pluginId: CORE_PLUGIN_ID,
1116
1193
  kind: "provider",
1117
1194
  description: "Universal `.md` fallback. Claims any markdown file that no vendor-specific provider has classified.",
1195
+ // Provider identity. `hideChip: true` suppresses the per-card provider
1196
+ // chip: this fallback carries the majority of nodes in any project, so
1197
+ // badging every generic `.md` would be noise and dilute the chip's
1198
+ // purpose (signalling a NON-default platform). The Provider still shows
1199
+ // in the active-lens dropdown and the topbar lens chip with this label.
1200
+ presentation: {
1201
+ label: "Markdown",
1202
+ color: "#9ca3af",
1203
+ colorDark: "#6b7280",
1204
+ hideChip: true
1205
+ },
1206
+ // No `detect` block: the universal fallback is never auto-suggested as a
1207
+ // lens (selecting it would gate out every vendor Provider). Operators
1208
+ // pick it manually if they want an open-standard-only view.
1118
1209
  read: { extensions: [".md"], parser: "frontmatter-yaml" },
1119
1210
  // Per spec § A.6, defaultRefreshAction values MUST be qualified
1120
1211
  // action ids. The summarize-markdown action is not yet implemented
@@ -2870,10 +2961,10 @@ function buildSchemaValidators() {
2870
2961
  hook: "extension-hook"
2871
2962
  };
2872
2963
  const pluginManifestValidator = ajv.compile({
2873
- $ref: "https://skill-map.dev/spec/v0/plugins-registry.schema.json#/$defs/PluginManifest"
2964
+ $ref: "https://skill-map.ai/spec/v0/plugins-registry.schema.json#/$defs/PluginManifest"
2874
2965
  });
2875
2966
  const contributionValidators = /* @__PURE__ */ new Map();
2876
- const VIEW_SLOTS_ID = "https://skill-map.dev/spec/v0/view-slots.schema.json";
2967
+ const VIEW_SLOTS_ID = "https://skill-map.ai/spec/v0/view-slots.schema.json";
2877
2968
  function getContributionValidator(slot) {
2878
2969
  if (!KNOWN_SLOT_NAMES.has(slot)) return null;
2879
2970
  const existing = contributionValidators.get(slot);
@@ -3855,11 +3946,11 @@ var UPDATE_CHECK_TEXTS = {
3855
3946
  // package.json
3856
3947
  var package_default = {
3857
3948
  name: "@skill-map/cli",
3858
- version: "0.41.0",
3949
+ version: "0.43.0",
3859
3950
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
3860
3951
  license: "MIT",
3861
3952
  type: "module",
3862
- homepage: "https://skill-map.dev",
3953
+ homepage: "https://skill-map.ai",
3863
3954
  repository: {
3864
3955
  type: "git",
3865
3956
  url: "git+https://github.com/crystian/skill-map.git",
@@ -4287,40 +4378,40 @@ var updateCheckHook = {
4287
4378
  };
4288
4379
 
4289
4380
  // plugins/built-ins.ts
4290
- var claudeProvider2 = { ...claudeProvider, pluginId: "claude", version: "0.41.0" };
4291
- var atDirectiveExtractor2 = { ...atDirectiveExtractor, pluginId: "claude", version: "0.41.0" };
4292
- var slashCommandExtractor2 = { ...slashCommandExtractor, pluginId: "claude", version: "0.41.0" };
4293
- var antigravityProvider2 = { ...antigravityProvider, pluginId: "antigravity", version: "0.41.0" };
4294
- var openaiProvider2 = { ...openaiProvider, pluginId: "openai", version: "0.41.0" };
4295
- var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills", version: "0.41.0" };
4296
- var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core", version: "0.41.0" };
4297
- var annotationsExtractor2 = { ...annotationsExtractor, pluginId: "core", version: "0.41.0" };
4298
- var externalUrlCounterExtractor2 = { ...externalUrlCounterExtractor, pluginId: "core", version: "0.41.0" };
4299
- var markdownLinkExtractor2 = { ...markdownLinkExtractor, pluginId: "core", version: "0.41.0" };
4300
- var mcpToolsExtractor2 = { ...mcpToolsExtractor, pluginId: "core", version: "0.41.0" };
4301
- var toolsCounterExtractor2 = { ...toolsCounterExtractor, pluginId: "core", version: "0.41.0" };
4302
- var annotationFieldUnknownAnalyzer2 = { ...annotationFieldUnknownAnalyzer, pluginId: "core", version: "0.41.0" };
4303
- var annotationOrphanAnalyzer2 = { ...annotationOrphanAnalyzer, pluginId: "core", version: "0.41.0" };
4304
- var annotationStaleAnalyzer2 = { ...annotationStaleAnalyzer, pluginId: "core", version: "0.41.0" };
4305
- var contributionOrphanAnalyzer2 = { ...contributionOrphanAnalyzer, pluginId: "core", version: "0.41.0" };
4306
- var issueCounterAnalyzer2 = { ...issueCounterAnalyzer, pluginId: "core", version: "0.41.0" };
4307
- var jobFileOrphanAnalyzer2 = { ...jobFileOrphanAnalyzer, pluginId: "core", version: "0.41.0" };
4308
- var linkConflictAnalyzer2 = { ...linkConflictAnalyzer, pluginId: "core", version: "0.41.0" };
4309
- var linkCounterAnalyzer2 = { ...linkCounterAnalyzer, pluginId: "core", version: "0.41.0" };
4310
- var linkSelfLoopAnalyzer2 = { ...linkSelfLoopAnalyzer, pluginId: "core", version: "0.41.0" };
4311
- var nameReservedAnalyzer2 = { ...nameReservedAnalyzer, pluginId: "core", version: "0.41.0" };
4312
- var nodeStabilityAnalyzer2 = { ...nodeStabilityAnalyzer, pluginId: "core", version: "0.41.0" };
4313
- var nodeSupersededAnalyzer2 = { ...nodeSupersededAnalyzer, pluginId: "core", version: "0.41.0" };
4314
- var referenceBrokenAnalyzer2 = { ...referenceBrokenAnalyzer, pluginId: "core", version: "0.41.0" };
4315
- var referenceRedundantAnalyzer2 = { ...referenceRedundantAnalyzer, pluginId: "core", version: "0.41.0" };
4316
- var schemaViolationAnalyzer2 = { ...schemaViolationAnalyzer, pluginId: "core", version: "0.41.0" };
4317
- var signalCollisionAnalyzer2 = { ...signalCollisionAnalyzer, pluginId: "core", version: "0.41.0" };
4318
- var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core", version: "0.41.0" };
4319
- var asciiFormatter2 = { ...asciiFormatter, pluginId: "core", version: "0.41.0" };
4320
- var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: "0.41.0" };
4321
- var nodeBumpAction2 = { ...nodeBumpAction, pluginId: "core", version: "0.41.0" };
4322
- var nodeSupersedeAction2 = { ...nodeSupersedeAction, pluginId: "core", version: "0.41.0" };
4323
- var updateCheckHook2 = { ...updateCheckHook, pluginId: "core", version: "0.41.0" };
4381
+ var claudeProvider2 = { ...claudeProvider, pluginId: "claude", version: "0.43.0" };
4382
+ var atDirectiveExtractor2 = { ...atDirectiveExtractor, pluginId: "claude", version: "0.43.0" };
4383
+ var slashCommandExtractor2 = { ...slashCommandExtractor, pluginId: "claude", version: "0.43.0" };
4384
+ var antigravityProvider2 = { ...antigravityProvider, pluginId: "antigravity", version: "0.43.0" };
4385
+ var openaiProvider2 = { ...openaiProvider, pluginId: "openai", version: "0.43.0" };
4386
+ var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills", version: "0.43.0" };
4387
+ var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core", version: "0.43.0" };
4388
+ var annotationsExtractor2 = { ...annotationsExtractor, pluginId: "core", version: "0.43.0" };
4389
+ var externalUrlCounterExtractor2 = { ...externalUrlCounterExtractor, pluginId: "core", version: "0.43.0" };
4390
+ var markdownLinkExtractor2 = { ...markdownLinkExtractor, pluginId: "core", version: "0.43.0" };
4391
+ var mcpToolsExtractor2 = { ...mcpToolsExtractor, pluginId: "core", version: "0.43.0" };
4392
+ var toolsCounterExtractor2 = { ...toolsCounterExtractor, pluginId: "core", version: "0.43.0" };
4393
+ var annotationFieldUnknownAnalyzer2 = { ...annotationFieldUnknownAnalyzer, pluginId: "core", version: "0.43.0" };
4394
+ var annotationOrphanAnalyzer2 = { ...annotationOrphanAnalyzer, pluginId: "core", version: "0.43.0" };
4395
+ var annotationStaleAnalyzer2 = { ...annotationStaleAnalyzer, pluginId: "core", version: "0.43.0" };
4396
+ var contributionOrphanAnalyzer2 = { ...contributionOrphanAnalyzer, pluginId: "core", version: "0.43.0" };
4397
+ var issueCounterAnalyzer2 = { ...issueCounterAnalyzer, pluginId: "core", version: "0.43.0" };
4398
+ var jobFileOrphanAnalyzer2 = { ...jobFileOrphanAnalyzer, pluginId: "core", version: "0.43.0" };
4399
+ var linkConflictAnalyzer2 = { ...linkConflictAnalyzer, pluginId: "core", version: "0.43.0" };
4400
+ var linkCounterAnalyzer2 = { ...linkCounterAnalyzer, pluginId: "core", version: "0.43.0" };
4401
+ var linkSelfLoopAnalyzer2 = { ...linkSelfLoopAnalyzer, pluginId: "core", version: "0.43.0" };
4402
+ var nameReservedAnalyzer2 = { ...nameReservedAnalyzer, pluginId: "core", version: "0.43.0" };
4403
+ var nodeStabilityAnalyzer2 = { ...nodeStabilityAnalyzer, pluginId: "core", version: "0.43.0" };
4404
+ var nodeSupersededAnalyzer2 = { ...nodeSupersededAnalyzer, pluginId: "core", version: "0.43.0" };
4405
+ var referenceBrokenAnalyzer2 = { ...referenceBrokenAnalyzer, pluginId: "core", version: "0.43.0" };
4406
+ var referenceRedundantAnalyzer2 = { ...referenceRedundantAnalyzer, pluginId: "core", version: "0.43.0" };
4407
+ var schemaViolationAnalyzer2 = { ...schemaViolationAnalyzer, pluginId: "core", version: "0.43.0" };
4408
+ var signalCollisionAnalyzer2 = { ...signalCollisionAnalyzer, pluginId: "core", version: "0.43.0" };
4409
+ var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core", version: "0.43.0" };
4410
+ var asciiFormatter2 = { ...asciiFormatter, pluginId: "core", version: "0.43.0" };
4411
+ var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: "0.43.0" };
4412
+ var nodeBumpAction2 = { ...nodeBumpAction, pluginId: "core", version: "0.43.0" };
4413
+ var nodeSupersedeAction2 = { ...nodeSupersedeAction, pluginId: "core", version: "0.43.0" };
4414
+ var updateCheckHook2 = { ...updateCheckHook, pluginId: "core", version: "0.43.0" };
4324
4415
  var builtInBundles = [
4325
4416
  {
4326
4417
  id: "claude",
@@ -7379,8 +7470,7 @@ async function replaceAllScanTags(trx, records, livePaths = /* @__PURE__ */ new
7379
7470
  if (records.length === 0) return;
7380
7471
  const rows = records.map((r) => ({
7381
7472
  nodePath: r.nodePath,
7382
- tag: r.tag,
7383
- source: r.source
7473
+ tag: r.tag
7384
7474
  }));
7385
7475
  const BATCH = 300;
7386
7476
  for (let i = 0; i < rows.length; i += BATCH) {
@@ -7388,18 +7478,16 @@ async function replaceAllScanTags(trx, records, livePaths = /* @__PURE__ */ new
7388
7478
  }
7389
7479
  }
7390
7480
  async function loadTagsForNode(db, nodePath) {
7391
- const rows = await db.selectFrom("scan_node_tags").select(["nodePath", "tag", "source"]).where("nodePath", "=", nodePath).orderBy("source", "asc").orderBy("tag", "asc").execute();
7392
- return rows.map((r) => ({ nodePath: r.nodePath, tag: r.tag, source: r.source }));
7481
+ const rows = await db.selectFrom("scan_node_tags").select(["nodePath", "tag"]).where("nodePath", "=", nodePath).orderBy("tag", "asc").execute();
7482
+ return rows.map((r) => ({ nodePath: r.nodePath, tag: r.tag }));
7393
7483
  }
7394
7484
  async function loadTagsForPaths(db, nodePaths) {
7395
7485
  if (nodePaths.length === 0) return [];
7396
- const rows = await db.selectFrom("scan_node_tags").select(["nodePath", "tag", "source"]).where("nodePath", "in", [...nodePaths]).orderBy("source", "asc").orderBy("tag", "asc").execute();
7397
- return rows.map((r) => ({ nodePath: r.nodePath, tag: r.tag, source: r.source }));
7486
+ const rows = await db.selectFrom("scan_node_tags").select(["nodePath", "tag"]).where("nodePath", "in", [...nodePaths]).orderBy("tag", "asc").execute();
7487
+ return rows.map((r) => ({ nodePath: r.nodePath, tag: r.tag }));
7398
7488
  }
7399
- async function findNodesByTag(db, tag, source) {
7400
- let q = db.selectFrom("scan_node_tags").select("nodePath").where("tag", "=", tag);
7401
- if (source !== void 0) q = q.where("source", "=", source);
7402
- const rows = await q.distinct().orderBy("nodePath", "asc").execute();
7489
+ async function findNodesByTag(db, tag) {
7490
+ const rows = await db.selectFrom("scan_node_tags").select("nodePath").where("tag", "=", tag).distinct().orderBy("nodePath", "asc").execute();
7403
7491
  return rows.map((r) => r.nodePath);
7404
7492
  }
7405
7493
 
@@ -7597,19 +7685,18 @@ function pickIntegerVersion(v) {
7597
7685
  function nodesToTagRecords(nodes) {
7598
7686
  const records = [];
7599
7687
  for (const node of nodes) {
7600
- pushTagRecords(records, node.path, node.frontmatter?.["tags"], "author");
7601
- pushTagRecords(records, node.path, node.sidecar?.annotations?.["tags"], "user");
7688
+ pushTagRecords(records, node.path, node.sidecar?.annotations?.["tags"]);
7602
7689
  }
7603
7690
  return records;
7604
7691
  }
7605
- function pushTagRecords(out, nodePath, raw, source) {
7692
+ function pushTagRecords(out, nodePath, raw) {
7606
7693
  if (!Array.isArray(raw)) return;
7607
7694
  const seen = /* @__PURE__ */ new Set();
7608
7695
  for (const item of raw) {
7609
7696
  if (typeof item !== "string" || item.length === 0) continue;
7610
7697
  if (seen.has(item)) continue;
7611
7698
  seen.add(item);
7612
- out.push({ nodePath, tag: item, source });
7699
+ out.push({ nodePath, tag: item });
7613
7700
  }
7614
7701
  }
7615
7702
  function linkToRow(link) {
@@ -7850,7 +7937,7 @@ var SqliteStorageAdapter = class {
7850
7937
  this.tags = {
7851
7938
  listForNode: (nodePath) => loadTagsForNode(this.db, nodePath),
7852
7939
  listForPaths: (paths) => loadTagsForPaths(this.db, paths),
7853
- findNodes: (tag, source) => findNodesByTag(this.db, tag, source)
7940
+ findNodes: (tag) => findNodesByTag(this.db, tag)
7854
7941
  };
7855
7942
  this.issues = {
7856
7943
  listAll: () => listAllIssues(this.db),
@@ -10781,21 +10868,8 @@ import { Command as Command4, Option as Option4 } from "clipanion";
10781
10868
  // core/config/active-provider.ts
10782
10869
  import { existsSync as existsSync14 } from "fs";
10783
10870
  import { join as join10 } from "path";
10784
- var DETECTION_RULES = [
10785
- { providerId: "claude", marker: ".claude" },
10786
- // `gemini` retired 2026-05-22: Google replaced the Gemini CLI with the
10787
- // Antigravity CLI (released 2026-05-19; Gemini CLI sunsets 2026-06-18).
10788
- // Antigravity adopted the open-standard `.agents/` instead of a
10789
- // vendor-specific directory, so detection of a Google CLI project
10790
- // falls through to the universal `agent-skills` lens (`.agents/`
10791
- // already classifies via that neutral provider). The lens can still
10792
- // be set manually via `sm config set activeProvider antigravity`.
10793
- { providerId: "openai", marker: ".codex" },
10794
- { providerId: "openai", marker: "AGENTS.md" },
10795
- { providerId: "cursor", marker: ".cursor" }
10796
- ];
10797
- function resolveActiveProvider(cwd) {
10798
- const detected = detectProvidersFromFilesystem(cwd);
10871
+ function resolveActiveProvider(cwd, providers = []) {
10872
+ const detected = detectProvidersFromFilesystem(cwd, providers);
10799
10873
  const fromConfig = readConfigValue("activeProvider", { cwd });
10800
10874
  if (typeof fromConfig === "string" && fromConfig.length > 0) {
10801
10875
  return { resolved: fromConfig, source: "config", detected };
@@ -10805,14 +10879,16 @@ function resolveActiveProvider(cwd) {
10805
10879
  }
10806
10880
  return { resolved: null, source: "none", detected };
10807
10881
  }
10808
- function detectProvidersFromFilesystem(cwd) {
10882
+ function detectProvidersFromFilesystem(cwd, providers) {
10809
10883
  const seen = /* @__PURE__ */ new Set();
10810
10884
  const out = [];
10811
- for (const rule of DETECTION_RULES) {
10812
- if (seen.has(rule.providerId)) continue;
10813
- if (!existsSync14(join10(cwd, rule.marker))) continue;
10814
- seen.add(rule.providerId);
10815
- out.push(rule.providerId);
10885
+ for (const provider of providers) {
10886
+ if (seen.has(provider.id)) continue;
10887
+ const markers = provider.detect?.markers;
10888
+ if (!markers || markers.length === 0) continue;
10889
+ if (!markers.some((marker) => existsSync14(join10(cwd, marker)))) continue;
10890
+ seen.add(provider.id);
10891
+ out.push(provider.id);
10816
10892
  }
10817
10893
  return out;
10818
10894
  }
@@ -10967,7 +11043,7 @@ function suggestConfigKey(effective, typed, ansi) {
10967
11043
  });
10968
11044
  }
10969
11045
  var KNOWN_DEFAULTLESS_KEY_RESOLVERS = {
10970
- activeProvider: (cwd) => resolveActiveProvider(cwd).resolved
11046
+ activeProvider: (cwd) => resolveActiveProvider(cwd, builtIns().providers).resolved
10971
11047
  };
10972
11048
  function parseCliValue(raw) {
10973
11049
  try {
@@ -11364,7 +11440,7 @@ var ConfigSetCommand = class extends SmCommand {
11364
11440
  cwd: ctx.cwd
11365
11441
  });
11366
11442
  if (this.key === "activeProvider" && typeof value === "string") {
11367
- const detected = resolveActiveProvider(ctx.cwd).detected;
11443
+ const detected = resolveActiveProvider(ctx.cwd, builtIns().providers).detected;
11368
11444
  writeConfigValue("activeProviderMarkers", [...detected], {
11369
11445
  target,
11370
11446
  cwd: ctx.cwd
@@ -12564,9 +12640,22 @@ var DbRestoreCommand = class extends SmCommand {
12564
12640
  };
12565
12641
 
12566
12642
  // cli/commands/db/reset.ts
12567
- import { rm as rm2 } from "fs/promises";
12568
12643
  import { DatabaseSync as DatabaseSync5 } from "node:sqlite";
12569
12644
  import { Command as Command8, Option as Option8 } from "clipanion";
12645
+
12646
+ // core/sqlite/db-files.ts
12647
+ import { existsSync as existsSync19 } from "fs";
12648
+ import { rm as rm2 } from "fs/promises";
12649
+ var DB_FILE_SUFFIXES = ["", "-wal", "-shm"];
12650
+ async function removeDbFiles(dbPath) {
12651
+ if (dbPath === ":memory:") return;
12652
+ for (const suffix of DB_FILE_SUFFIXES) {
12653
+ const p = `${dbPath}${suffix}`;
12654
+ if (existsSync19(p)) await rm2(p);
12655
+ }
12656
+ }
12657
+
12658
+ // cli/commands/db/reset.ts
12570
12659
  var DbResetCommand = class extends SmCommand {
12571
12660
  static paths = [["db", "reset"]];
12572
12661
  static usage = Command8.Usage({
@@ -12626,10 +12715,7 @@ var DbResetCommand = class extends SmCommand {
12626
12715
  return ExitCode.Error;
12627
12716
  }
12628
12717
  }
12629
- for (const suffix of ["", "-wal", "-shm"]) {
12630
- const p = `${path}${suffix}`;
12631
- if (await pathExists(p)) await rm2(p);
12632
- }
12718
+ await removeDbFiles(path);
12633
12719
  const ansiHard = this.ansiFor("stdout");
12634
12720
  this.printer.data(
12635
12721
  tx(DB_TEXTS.resetHardDeleted, {
@@ -14370,7 +14456,7 @@ import { join as join17 } from "path";
14370
14456
  import { Command as Command17, Option as Option16 } from "clipanion";
14371
14457
 
14372
14458
  // kernel/orchestrator/index.ts
14373
- import { existsSync as existsSync21, statSync as statSync6 } from "fs";
14459
+ import { existsSync as existsSync22, statSync as statSync6 } from "fs";
14374
14460
  import { isAbsolute as isAbsolute7, resolve as resolve28 } from "path";
14375
14461
  import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
14376
14462
  import cl100k_base from "js-tiktoken/ranks/cl100k_base";
@@ -15417,7 +15503,7 @@ function computeDriftStatus(args2) {
15417
15503
  }
15418
15504
 
15419
15505
  // kernel/sidecar/discover-orphans.ts
15420
- import { existsSync as existsSync19, readdirSync as readdirSync7, statSync as statSync5 } from "fs";
15506
+ import { existsSync as existsSync20, readdirSync as readdirSync7, statSync as statSync5 } from "fs";
15421
15507
  import { join as join13, relative as relative4, sep as sep4 } from "path";
15422
15508
  function discoverOrphanSidecars(roots, shouldSkip) {
15423
15509
  const out = [];
@@ -15445,7 +15531,7 @@ function walk(root, current, shouldSkip, out) {
15445
15531
  if (!entry.isFile()) continue;
15446
15532
  if (!entry.name.endsWith(".sm")) continue;
15447
15533
  const expectedMd = `${full.slice(0, -".sm".length)}.md`;
15448
- if (existsSync19(expectedMd) && safeIsFile(expectedMd)) continue;
15534
+ if (existsSync20(expectedMd) && safeIsFile(expectedMd)) continue;
15449
15535
  out.push({ sidecarPath: full, relativePath: rel, expectedMdPath: expectedMd });
15450
15536
  }
15451
15537
  }
@@ -15459,7 +15545,7 @@ function safeIsFile(path) {
15459
15545
 
15460
15546
  // kernel/orchestrator/node-build.ts
15461
15547
  import { createHash } from "crypto";
15462
- import { existsSync as existsSync20 } from "fs";
15548
+ import { existsSync as existsSync21 } from "fs";
15463
15549
  import { isAbsolute as isAbsolute6, resolve as resolvePath } from "path";
15464
15550
  import "js-tiktoken/lite";
15465
15551
  import yaml4 from "js-yaml";
@@ -15623,11 +15709,11 @@ function resolveSidecarOverlay(relativePath2, nodePathForIssue, roots, liveBodyH
15623
15709
  }
15624
15710
  function resolveAbsoluteMdPath(relativePath2, roots) {
15625
15711
  if (isAbsolute6(relativePath2)) {
15626
- return existsSync20(relativePath2) ? relativePath2 : null;
15712
+ return existsSync21(relativePath2) ? relativePath2 : null;
15627
15713
  }
15628
15714
  for (const root of roots) {
15629
15715
  const candidate = resolvePath(root, relativePath2);
15630
- if (existsSync20(candidate)) return candidate;
15716
+ if (existsSync21(candidate)) return candidate;
15631
15717
  }
15632
15718
  return null;
15633
15719
  }
@@ -15984,7 +16070,11 @@ async function runScanInternal(_kernel, options) {
15984
16070
  const scanStartedEvent = makeEvent("scan.started", { roots: options.roots });
15985
16071
  emitter.emit(scanStartedEvent);
15986
16072
  await hookDispatcher.dispatch("scan.started", scanStartedEvent);
15987
- const activeProviderId = resolveActiveProviderOption(options.activeProvider, options.roots);
16073
+ const activeProviderId = resolveActiveProviderOption(
16074
+ options.activeProvider,
16075
+ options.roots,
16076
+ exts.providers
16077
+ );
15988
16078
  const walked = await walkAndExtract({
15989
16079
  providers: exts.providers,
15990
16080
  extractors: exts.extractors,
@@ -16188,17 +16278,17 @@ function validateRoots(roots) {
16188
16278
  throw new Error(ORCHESTRATOR_TEXTS.runScanRootEmptyArray);
16189
16279
  }
16190
16280
  for (const root of roots) {
16191
- if (!existsSync21(root) || !statSync6(root).isDirectory()) {
16281
+ if (!existsSync22(root) || !statSync6(root).isDirectory()) {
16192
16282
  throw new Error(tx(ORCHESTRATOR_TEXTS.runScanRootMissing, { root }));
16193
16283
  }
16194
16284
  }
16195
16285
  }
16196
- function resolveActiveProviderOption(optionValue, roots) {
16286
+ function resolveActiveProviderOption(optionValue, roots, providers) {
16197
16287
  if (optionValue !== void 0) return optionValue;
16198
16288
  for (const root of roots) {
16199
16289
  const absRoot = isAbsolute7(root) ? root : resolve28(root);
16200
- if (!existsSync21(absRoot)) continue;
16201
- const detected = resolveActiveProvider(absRoot).resolved;
16290
+ if (!existsSync22(absRoot)) continue;
16291
+ const detected = resolveActiveProvider(absRoot, providers).resolved;
16202
16292
  if (detected !== null) return detected;
16203
16293
  }
16204
16294
  return null;
@@ -16667,17 +16757,23 @@ function safeStat(path) {
16667
16757
  import { createInterface as createInterface2 } from "readline";
16668
16758
  import { isAbsolute as isAbsolute9, join as join16 } from "path";
16669
16759
  async function bootstrapActiveProvider(opts) {
16670
- const fromCwd = resolveActiveProvider(opts.cwd);
16760
+ const fromCwd = resolveActiveProvider(opts.cwd, opts.providers);
16671
16761
  if (fromCwd.source === "config") {
16672
16762
  const currentMarkers = aggregateDetected(
16673
16763
  opts.cwd,
16674
16764
  opts.effectiveRoots,
16675
- fromCwd.detected
16765
+ fromCwd.detected,
16766
+ opts.providers
16676
16767
  );
16677
16768
  handleDrift(opts, fromCwd.resolved, currentMarkers);
16678
16769
  return { kind: "ok", activeProvider: fromCwd.resolved, source: "config" };
16679
16770
  }
16680
- const detected = aggregateDetected(opts.cwd, opts.effectiveRoots, fromCwd.detected);
16771
+ const detected = aggregateDetected(
16772
+ opts.cwd,
16773
+ opts.effectiveRoots,
16774
+ fromCwd.detected,
16775
+ opts.providers
16776
+ );
16681
16777
  if (detected.length === 0) {
16682
16778
  const warnGlyph = opts.style?.warnGlyph ?? "\u26A0";
16683
16779
  const dim = opts.style?.dim ?? ((s) => s);
@@ -16709,7 +16805,7 @@ async function bootstrapActiveProvider(opts) {
16709
16805
  persistActiveProvider(opts.cwd, picked, detected, opts.printer);
16710
16806
  return { kind: "ok", activeProvider: picked, source: "autodetect" };
16711
16807
  }
16712
- function aggregateDetected(cwd, effectiveRoots, cwdDetected) {
16808
+ function aggregateDetected(cwd, effectiveRoots, cwdDetected, providers) {
16713
16809
  const out = [];
16714
16810
  const seen = /* @__PURE__ */ new Set();
16715
16811
  for (const id of cwdDetected) {
@@ -16719,7 +16815,7 @@ function aggregateDetected(cwd, effectiveRoots, cwdDetected) {
16719
16815
  }
16720
16816
  for (const root of effectiveRoots) {
16721
16817
  const absRoot = isAbsolute9(root) ? root : join16(cwd, root);
16722
- const r = resolveActiveProvider(absRoot);
16818
+ const r = resolveActiveProvider(absRoot, providers);
16723
16819
  for (const id of r.detected) {
16724
16820
  if (seen.has(id)) continue;
16725
16821
  seen.add(id);
@@ -16831,6 +16927,99 @@ async function promptForLens(detected, stdin, stderr, warnGlyph) {
16831
16927
  }
16832
16928
  }
16833
16929
 
16930
+ // core/sqlite/db-drift-reset.ts
16931
+ import { existsSync as existsSync23 } from "fs";
16932
+ import { createInterface as createInterface3 } from "readline";
16933
+ import { DatabaseSync as DatabaseSync7 } from "node:sqlite";
16934
+
16935
+ // core/sqlite/i18n/db-drift.texts.ts
16936
+ var DB_DRIFT_TEXTS = {
16937
+ // Interactive confirm (TTY `sm scan`, no `--yes`). The block is
16938
+ // written to stderr, then the question line drives `readline`.
16939
+ driftPrompt: "{{glyph}} The local cache was built by skill-map {{dbVersion}} and you are on {{currentVersion}}.\n {{hint}}\n",
16940
+ driftPromptHint: "Pre-1.0 the DB is a derived cache (your .sm sidecars hold the real data); it cannot be carried across a version change and has to be rebuilt.",
16941
+ driftPromptQuestion: "Delete the local cache and rebuild it on this scan? [y/N] ",
16942
+ // Receipt after the rebuild (printed by the scan / refresh path).
16943
+ driftReset: "{{glyph}} Local cache rebuilt: it was written by skill-map {{dbVersion}}, you are on {{currentVersion}}.\n {{hint}}\n",
16944
+ driftResetHint: "The DB was deleted and is being regenerated by this scan; .sm sidecars were not touched.",
16945
+ // Abort headline when the operator declines (wrapped by the caller's
16946
+ // `sm scan: {message}` shell, so it carries no glyph / verb prefix).
16947
+ driftAborted: "cache rebuild declined: the {{dbVersion}} cache cannot be reused on {{currentVersion}}. {{hint}}",
16948
+ driftAbortedHint: "Re-run with --yes, or run `sm db reset --hard` then `sm scan`."
16949
+ };
16950
+
16951
+ // core/sqlite/db-drift-reset.ts
16952
+ async function maybeResetOnDrift(dbPath, policy) {
16953
+ const dbVersion = readScannedByVersion(dbPath);
16954
+ if (dbVersion === null) return { kind: "no-drift" };
16955
+ const skew = classifyVersionSkew(dbVersion, policy.currentVersion);
16956
+ if (skew.kind === "ok" || skew.kind === "no-meta") return { kind: "no-drift" };
16957
+ const confirmed = await confirmDriftReset(dbVersion, policy);
16958
+ if (!confirmed) {
16959
+ return { kind: "aborted", dbVersion, currentVersion: policy.currentVersion };
16960
+ }
16961
+ await removeDbFiles(dbPath);
16962
+ renderResetReceipt(dbVersion, policy);
16963
+ return { kind: "reset", dbVersion, currentVersion: policy.currentVersion };
16964
+ }
16965
+ function readScannedByVersion(dbPath) {
16966
+ if (dbPath === ":memory:" || !existsSync23(dbPath)) return null;
16967
+ let raw = null;
16968
+ try {
16969
+ raw = new DatabaseSync7(dbPath, { readOnly: true });
16970
+ const row = raw.prepare("SELECT scanned_by_version AS v FROM scan_meta LIMIT 1").get();
16971
+ const v = row?.v;
16972
+ return typeof v === "string" && v.length > 0 ? v : null;
16973
+ } catch {
16974
+ return null;
16975
+ } finally {
16976
+ raw?.close();
16977
+ }
16978
+ }
16979
+ async function confirmDriftReset(dbVersion, policy) {
16980
+ if (!shouldPromptForReset(policy)) return true;
16981
+ return askDriftReset(dbVersion, policy);
16982
+ }
16983
+ function shouldPromptForReset(policy) {
16984
+ if (policy.assumeYes) return false;
16985
+ if (!policy.stdin || !policy.stderr) return false;
16986
+ return policy.stdin.isTTY === true;
16987
+ }
16988
+ async function askDriftReset(dbVersion, policy) {
16989
+ const warnGlyph = policy.style?.warnGlyph ?? "\u26A0";
16990
+ const dim = policy.style?.dim ?? ((s) => s);
16991
+ policy.stderr.write(
16992
+ tx(DB_DRIFT_TEXTS.driftPrompt, {
16993
+ glyph: warnGlyph,
16994
+ dbVersion,
16995
+ currentVersion: policy.currentVersion,
16996
+ hint: dim(DB_DRIFT_TEXTS.driftPromptHint)
16997
+ })
16998
+ );
16999
+ const rl = createInterface3({ input: policy.stdin, output: policy.stderr });
17000
+ try {
17001
+ const answer = await new Promise(
17002
+ (resolveP) => rl.question(DB_DRIFT_TEXTS.driftPromptQuestion, resolveP)
17003
+ );
17004
+ return /^y(es)?$/i.test(answer.trim());
17005
+ } finally {
17006
+ rl.close();
17007
+ }
17008
+ }
17009
+ function renderResetReceipt(dbVersion, policy) {
17010
+ if (!policy.printer) return;
17011
+ const warnGlyph = policy.style?.warnGlyph ?? "\u26A0";
17012
+ const dim = policy.style?.dim ?? ((s) => s);
17013
+ policy.printer.warn(
17014
+ tx(DB_DRIFT_TEXTS.driftReset, {
17015
+ glyph: warnGlyph,
17016
+ dbVersion,
17017
+ currentVersion: policy.currentVersion,
17018
+ hint: dim(DB_DRIFT_TEXTS.driftResetHint)
17019
+ })
17020
+ );
17021
+ }
17022
+
16834
17023
  // core/runtime/scan-runner.ts
16835
17024
  async function runScanForCommand(opts) {
16836
17025
  const ctx = opts.ctx ?? defaultRuntimeContext();
@@ -16849,7 +17038,13 @@ async function runScanForCommand(opts) {
16849
17038
  }
16850
17039
  const loadPrior = makePriorLoader(opts.noBuiltIns, strict);
16851
17040
  const jobsDir = defaultProjectJobsDir(ctx);
16852
- const lens = await resolveActiveLens(opts, ctx, effectiveRoots, pluginRuntime);
17041
+ const lens = await resolveActiveLens(
17042
+ opts,
17043
+ ctx,
17044
+ effectiveRoots,
17045
+ pluginRuntime,
17046
+ detectionProviders(extensions)
17047
+ );
16853
17048
  if (lens.kind === "ambiguous-provider") return lens;
16854
17049
  const activeProvider = lens.activeProvider;
16855
17050
  const runScanWith = makeScanRunner(
@@ -16867,10 +17062,14 @@ async function runScanForCommand(opts) {
16867
17062
  const willPersist = !opts.noBuiltIns && !opts.dryRun;
16868
17063
  return willPersist ? runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanWith, extensions) : runEphemeralPath(opts, dbPath, strict, loadPrior, runScanWith);
16869
17064
  }
16870
- async function resolveActiveLens(opts, ctx, effectiveRoots, pluginRuntime) {
17065
+ function detectionProviders(extensions) {
17066
+ return extensions?.providers ?? [];
17067
+ }
17068
+ async function resolveActiveLens(opts, ctx, effectiveRoots, pluginRuntime, providers) {
16871
17069
  const bootstrap = await bootstrapActiveProvider({
16872
17070
  cwd: ctx.cwd,
16873
17071
  effectiveRoots,
17072
+ providers,
16874
17073
  yes: opts.yes ?? false,
16875
17074
  stdin: opts.stdin ?? process.stdin,
16876
17075
  stderr: opts.stderr,
@@ -17018,7 +17217,29 @@ function buildRunScanEmitter(opts) {
17018
17217
  colorEnabled: opts.colorEnabled === true
17019
17218
  });
17020
17219
  }
17220
+ async function rebuildOnDrift(opts, dbPath) {
17221
+ const drift = await maybeResetOnDrift(dbPath, {
17222
+ currentVersion: VERSION,
17223
+ assumeYes: opts.yes ?? false,
17224
+ stdin: opts.stdin ?? process.stdin,
17225
+ stderr: opts.stderr,
17226
+ printer: opts.printer,
17227
+ ...opts.style ? { style: opts.style } : {}
17228
+ });
17229
+ if (drift.kind !== "aborted") return null;
17230
+ const dim = opts.style?.dim ?? ((s) => s);
17231
+ return {
17232
+ kind: "scan-error",
17233
+ message: tx(DB_DRIFT_TEXTS.driftAborted, {
17234
+ dbVersion: drift.dbVersion,
17235
+ currentVersion: drift.currentVersion,
17236
+ hint: dim(DB_DRIFT_TEXTS.driftAbortedHint)
17237
+ })
17238
+ };
17239
+ }
17021
17240
  async function runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanWith, extensions) {
17241
+ const driftError = await rebuildOnDrift(opts, dbPath);
17242
+ if (driftError) return driftError;
17022
17243
  let outcome;
17023
17244
  try {
17024
17245
  outcome = await withSqlite({ databasePath: dbPath }, async (adapter) => {
@@ -18108,19 +18329,6 @@ import { Command as Command20, Option as Option19 } from "clipanion";
18108
18329
  var LIST_TEXTS = {
18109
18330
  invalidSortBy: '{{glyph}} --sort-by: invalid sort field "{{value}}".\n {{hint}}\n',
18110
18331
  invalidSortByHint: "Allowed: {{allowed}}.",
18111
- /**
18112
- * §3.1b two-line block. Closed enum: hint enumerates the two valid
18113
- * values so the operator does not need to re-read `--help`.
18114
- */
18115
- invalidTagSource: '{{glyph}} --tag-source: expected "author" or "user", got "{{value}}".\n {{hint}}\n',
18116
- invalidTagSourceHint: "Allowed: author, user.",
18117
- /**
18118
- * §3.1b two-line block. `--tag-source` is a narrowing filter on
18119
- * `--tag`; the hint repeats the dependency in operator-actionable
18120
- * form.
18121
- */
18122
- tagSourceWithoutTag: "{{glyph}} --tag-source requires --tag <name>.\n {{hint}}\n",
18123
- tagSourceWithoutTagHint: "The source filter narrows tag matches, it does not stand alone. Pass --tag <name> alongside --tag-source.",
18124
18332
  noNodesFound: "No nodes found.\n",
18125
18333
  // --- renderTable column headers ----------------------------------------
18126
18334
  tableHeaderPath: "PATH",
@@ -18157,9 +18365,8 @@ var ListCommand = class extends SmCommand {
18157
18365
  Reads from the persisted scan snapshot (scan_nodes). Filters:
18158
18366
  --kind <k> restricts to one node kind; --issue keeps only nodes
18159
18367
  that touch at least one current issue; --tag <name> keeps only
18160
- nodes carrying that tag (matches the union of frontmatter.tags
18161
- and sidecar.annotations.tags by default; --tag-source author|user
18162
- narrows to one side).
18368
+ nodes carrying that tag in their \`.sm\` sidecar
18369
+ (\`annotations.tags\`).
18163
18370
 
18164
18371
  --sort-by accepts: path, kind, tokens_total, links_out_count,
18165
18372
  links_in_count, external_refs_count. Default: path. --limit N caps
@@ -18172,8 +18379,7 @@ var ListCommand = class extends SmCommand {
18172
18379
  ["List only agents", "$0 list --kind agent"],
18173
18380
  ["Top 5 by total tokens", "$0 list --sort-by tokens_total --limit 5"],
18174
18381
  ["Only nodes with issues, machine-readable", "$0 list --issue --json"],
18175
- ["Filter by tag (author or user surfaces)", "$0 list --tag urgent"],
18176
- ["Filter by user-only tag", "$0 list --tag wip --tag-source user"]
18382
+ ["Filter by tag", "$0 list --tag urgent"]
18177
18383
  ]
18178
18384
  });
18179
18385
  kind = Option19.String("--kind", { required: false });
@@ -18181,7 +18387,6 @@ var ListCommand = class extends SmCommand {
18181
18387
  sortBy = Option19.String("--sort-by", { required: false });
18182
18388
  limit = Option19.String("--limit", { required: false });
18183
18389
  tag = Option19.String("--tag", { required: false });
18184
- tagSource = Option19.String("--tag-source", { required: false });
18185
18390
  async run() {
18186
18391
  const stderrAnsi = this.ansiFor("stderr");
18187
18392
  const flags = this.#parseFlags(stderrAnsi);
@@ -18200,7 +18405,6 @@ var ListCommand = class extends SmCommand {
18200
18405
  * resolved values or a precomputed exit code (already printed
18201
18406
  * directed errors before returning).
18202
18407
  */
18203
- // eslint-disable-next-line complexity
18204
18408
  #parseFlags(stderrAnsi) {
18205
18409
  let sortColumn = "path";
18206
18410
  let sortDirection = "asc";
@@ -18227,30 +18431,7 @@ var ListCommand = class extends SmCommand {
18227
18431
  if (parsed === null) return { ok: false, exit: ExitCode.Error };
18228
18432
  limitValue = parsed;
18229
18433
  }
18230
- let tagSourceValue;
18231
- if (this.tagSource !== void 0) {
18232
- if (this.tag === void 0) {
18233
- this.printer.error(
18234
- tx(LIST_TEXTS.tagSourceWithoutTag, {
18235
- glyph: stderrAnsi.red("\u2715"),
18236
- hint: stderrAnsi.dim(LIST_TEXTS.tagSourceWithoutTagHint)
18237
- })
18238
- );
18239
- return { ok: false, exit: ExitCode.Error };
18240
- }
18241
- if (this.tagSource !== "author" && this.tagSource !== "user") {
18242
- this.printer.error(
18243
- tx(LIST_TEXTS.invalidTagSource, {
18244
- glyph: stderrAnsi.red("\u2715"),
18245
- value: this.tagSource,
18246
- hint: stderrAnsi.dim(LIST_TEXTS.invalidTagSourceHint)
18247
- })
18248
- );
18249
- return { ok: false, exit: ExitCode.Error };
18250
- }
18251
- tagSourceValue = this.tagSource;
18252
- }
18253
- return { ok: true, sortColumn, sortDirection, limitValue, tagSourceValue };
18434
+ return { ok: true, sortColumn, sortDirection, limitValue };
18254
18435
  }
18255
18436
  /**
18256
18437
  * Issue the DB queries: optional `--tag` allow-list narrowing, the
@@ -18258,7 +18439,7 @@ var ListCommand = class extends SmCommand {
18258
18439
  * out so `run()` reads as a thin orchestrator.
18259
18440
  */
18260
18441
  async #runQuery(adapter, flags) {
18261
- const tagAllowList = await this.#resolveTagAllowList(adapter, flags);
18442
+ const tagAllowList = await this.#resolveTagAllowList(adapter);
18262
18443
  if (tagAllowList === "no-match") return this.#renderEmpty();
18263
18444
  const filter = this.#buildFindNodesFilter(flags);
18264
18445
  const allMatchingNodes = await adapter.scans.findNodes(filter);
@@ -18274,16 +18455,15 @@ var ListCommand = class extends SmCommand {
18274
18455
  return ExitCode.Ok;
18275
18456
  }
18276
18457
  /**
18277
- * Resolve `--tag` (and the optional `--tag-source` filter) into a
18278
- * path allow-list. Returns:
18458
+ * Resolve `--tag` into a path allow-list. Returns:
18279
18459
  * - `null` when `--tag` was not supplied (no narrowing).
18280
18460
  * - `'no-match'` when the tag matched zero nodes (caller renders
18281
18461
  * the empty surface and exits).
18282
18462
  * - a Set of paths otherwise.
18283
18463
  */
18284
- async #resolveTagAllowList(adapter, flags) {
18464
+ async #resolveTagAllowList(adapter) {
18285
18465
  if (this.tag === void 0) return null;
18286
- const matchingPaths = await adapter.tags.findNodes(this.tag, flags.tagSourceValue);
18466
+ const matchingPaths = await adapter.tags.findNodes(this.tag);
18287
18467
  if (matchingPaths.length === 0) return "no-match";
18288
18468
  return new Set(matchingPaths);
18289
18469
  }
@@ -20373,7 +20553,7 @@ function resolveBareToggle(id, catalogue) {
20373
20553
  }
20374
20554
 
20375
20555
  // cli/commands/plugins/create.ts
20376
- import { existsSync as existsSync22, mkdirSync as mkdirSync5, writeFileSync } from "fs";
20556
+ import { existsSync as existsSync24, mkdirSync as mkdirSync5, writeFileSync } from "fs";
20377
20557
  import { join as join18, resolve as resolve33 } from "path";
20378
20558
  import { Command as Command26, Option as Option25 } from "clipanion";
20379
20559
  var PluginsCreateCommand = class extends SmCommand {
@@ -20402,7 +20582,7 @@ var PluginsCreateCommand = class extends SmCommand {
20402
20582
  const ctx = defaultRuntimeContext();
20403
20583
  const baseDir = defaultProjectPluginsDir(ctx);
20404
20584
  const targetDir = this.at ? resolve33(this.at) : join18(baseDir, this.pluginId);
20405
- if (existsSync22(targetDir) && !this.force) {
20585
+ if (existsSync24(targetDir) && !this.force) {
20406
20586
  this.printer.error(
20407
20587
  tx(PLUGINS_TEXTS.createRefuseOverwrite, {
20408
20588
  glyph: errGlyph,
@@ -21128,6 +21308,12 @@ var RUNTIME_TEXTS = {
21128
21308
 
21129
21309
  // core/watcher/runtime.ts
21130
21310
  var DEFAULT_RUN_INITIAL_BATCH = true;
21311
+ async function rebuildWatcherDbOnDrift(dbPath, events) {
21312
+ const drift = await maybeResetOnDrift(dbPath, { currentVersion: VERSION, assumeYes: true });
21313
+ if (drift.kind === "reset") {
21314
+ events.onDriftReset?.({ dbVersion: drift.dbVersion, currentVersion: drift.currentVersion });
21315
+ }
21316
+ }
21131
21317
  function createWatcherRuntime(opts) {
21132
21318
  const events = opts.events ?? {};
21133
21319
  const cwd = opts.runtimeContext.cwd;
@@ -21171,6 +21357,7 @@ function createWatcherRuntime(opts) {
21171
21357
  };
21172
21358
  let handleBatch = null;
21173
21359
  const start = async () => {
21360
+ await rebuildWatcherDbOnDrift(opts.dbPath, events);
21174
21361
  cfg = loadEffectiveConfig();
21175
21362
  ignoreFilter = composeIgnoreFilter(cfg, readIgnoreFileText(cwd));
21176
21363
  applyConfigDerivedState(cfg);
@@ -21526,6 +21713,16 @@ async function runWatchLoop(opts) {
21526
21713
  printer.warn(`${message}
21527
21714
  `);
21528
21715
  },
21716
+ onDriftReset: (info) => {
21717
+ context.stderr.write(
21718
+ tx(DB_DRIFT_TEXTS.driftReset, {
21719
+ glyph: stderrAnsi.yellow("\u26A0"),
21720
+ dbVersion: info.dbVersion,
21721
+ currentVersion: info.currentVersion,
21722
+ hint: stderrAnsi.dim(DB_DRIFT_TEXTS.driftResetHint)
21723
+ })
21724
+ );
21725
+ },
21529
21726
  onConfigLoaded: ({ debounceMs }) => {
21530
21727
  if (opts.json) return;
21531
21728
  context.stderr.write(
@@ -21747,7 +21944,7 @@ var ScanCommand = class extends SmCommand {
21747
21944
  description: "Long-running mode: watch the roots and trigger an incremental scan after each debounced batch of filesystem events. Alias of `sm watch`."
21748
21945
  });
21749
21946
  yes = Option29.Boolean("--yes", false, {
21750
- 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."
21947
+ description: "Non-interactive mode. For ambiguous activeProvider auto-detect, multiple provider markers (.claude/, .codex/, AGENTS.md, .cursor/) under the scan tree exit non-zero instead of prompting; set the lens manually via `sm config set activeProvider <id>` and re-run. Also auto-confirms the pre-1.0 schema-drift rebuild (when the DB was written by a different skill-map major.minor it is deleted and regenerated) instead of prompting."
21751
21948
  });
21752
21949
  maxNodes = Option29.String("--max-nodes", {
21753
21950
  required: false,
@@ -22281,7 +22478,7 @@ function renderDeltaIssues(issues) {
22281
22478
 
22282
22479
  // cli/commands/serve.ts
22283
22480
  import { spawn as spawn2 } from "child_process";
22284
- import { existsSync as existsSync28 } from "fs";
22481
+ import { existsSync as existsSync30 } from "fs";
22285
22482
  import { Command as Command33, Option as Option31 } from "clipanion";
22286
22483
 
22287
22484
  // kernel/util/dev-mode.ts
@@ -22436,6 +22633,11 @@ var SERVER_TEXTS = {
22436
22633
  // watcher loop continues, a transient FS error must not kill the
22437
22634
  // broadcaster.
22438
22635
  watcherBatchFailed: "skill-map server: watcher batch failed ({{message}}).\n",
22636
+ // Logged once when the pre-1.0 schema-drift check rebuilt the DB on
22637
+ // watcher boot (the on-disk cache was written by a different
22638
+ // major.minor). The scan that follows repopulates it; .sm sidecars
22639
+ // are untouched. See spec/db-schema.md §Schema drift (pre-1.0).
22640
+ watcherDriftReset: "skill-map server: local cache rebuilt (was {{dbVersion}}, now on {{currentVersion}}); .sm sidecars untouched.\n",
22439
22641
  // chokidar surfaced an error. The watcher stays open per IFsWatcher's
22440
22642
  // contract; the BFF also broadcasts a `watcher.error` advisory so the
22441
22643
  // SPA can surface it in the live event log.
@@ -22848,15 +23050,17 @@ function buildListEnvelope(opts) {
22848
23050
  filters: opts.filters,
22849
23051
  counts,
22850
23052
  kindRegistry: opts.kindRegistry,
23053
+ providerRegistry: opts.providerRegistry,
22851
23054
  contributionsRegistry: opts.contributionsRegistry
22852
23055
  };
22853
23056
  }
22854
- function buildValueEnvelope(kind, value, kindRegistry, contributionsRegistry) {
23057
+ function buildValueEnvelope(kind, value, kindRegistry, providerRegistry, contributionsRegistry) {
22855
23058
  return {
22856
23059
  schemaVersion: REST_ENVELOPE_SCHEMA_VERSION,
22857
23060
  kind,
22858
23061
  value,
22859
23062
  kindRegistry,
23063
+ providerRegistry,
22860
23064
  contributionsRegistry
22861
23065
  };
22862
23066
  }
@@ -22873,7 +23077,15 @@ function registerConfigRoute(app, deps) {
22873
23077
  for (const warn of loaded.warnings) {
22874
23078
  log.warn(sanitizeForTerminal(warn));
22875
23079
  }
22876
- return c.json(buildValueEnvelope("config", loaded.effective, deps.kindRegistry, deps.contributionsRegistry));
23080
+ return c.json(
23081
+ buildValueEnvelope(
23082
+ "config",
23083
+ loaded.effective,
23084
+ deps.kindRegistry,
23085
+ deps.providerRegistry,
23086
+ deps.contributionsRegistry
23087
+ )
23088
+ );
22877
23089
  });
22878
23090
  }
22879
23091
 
@@ -23006,7 +23218,7 @@ function contentTypeFor(format) {
23006
23218
  }
23007
23219
 
23008
23220
  // server/health.ts
23009
- import { existsSync as existsSync23 } from "fs";
23221
+ import { existsSync as existsSync25 } from "fs";
23010
23222
  var FALLBACK_SCHEMA_VERSION = "1";
23011
23223
  function buildHealth(deps) {
23012
23224
  const dev = isDevBuild();
@@ -23015,7 +23227,7 @@ function buildHealth(deps) {
23015
23227
  schemaVersion: FALLBACK_SCHEMA_VERSION,
23016
23228
  specVersion: deps.specVersion,
23017
23229
  implVersion: VERSION,
23018
- db: existsSync23(deps.dbPath) ? "present" : "missing",
23230
+ db: existsSync25(deps.dbPath) ? "present" : "missing",
23019
23231
  cwd: deps.cwd,
23020
23232
  dbPath: deps.dbPath,
23021
23233
  // Only emit when truthy so a published install keeps the wire
@@ -23071,6 +23283,7 @@ function registerIssuesRoute(app, deps) {
23071
23283
  total: result?.total ?? 0,
23072
23284
  page: { offset: inputs.filter.offset, limit: inputs.filter.limit },
23073
23285
  kindRegistry: deps.kindRegistry,
23286
+ providerRegistry: deps.providerRegistry,
23074
23287
  contributionsRegistry: deps.contributionsRegistry
23075
23288
  })
23076
23289
  );
@@ -23133,6 +23346,7 @@ function registerLinksRoute(app, deps) {
23133
23346
  },
23134
23347
  total: filtered.length,
23135
23348
  kindRegistry: deps.kindRegistry,
23349
+ providerRegistry: deps.providerRegistry,
23136
23350
  contributionsRegistry: deps.contributionsRegistry
23137
23351
  })
23138
23352
  );
@@ -23264,7 +23478,7 @@ function registerNodesRoutes(app, deps) {
23264
23478
  bundle: null,
23265
23479
  isFavorite: false,
23266
23480
  contributions: [],
23267
- tags: { byAuthor: [], byUser: [] }
23481
+ tags: []
23268
23482
  };
23269
23483
  }
23270
23484
  const favSet = await adapter.favorites.listPaths();
@@ -23274,14 +23488,14 @@ function registerNodesRoutes(app, deps) {
23274
23488
  bundle: b,
23275
23489
  isFavorite: favSet.has(b.node.path),
23276
23490
  contributions: contributions2,
23277
- tags: groupTagsBySource(tagRows)
23491
+ tags: tagRows.map((r) => r.tag)
23278
23492
  };
23279
23493
  }
23280
23494
  );
23281
23495
  const bundle = result?.bundle ?? null;
23282
23496
  const isFavorite = result?.isFavorite ?? false;
23283
23497
  const contributions = result?.contributions ?? [];
23284
- const tags = result?.tags ?? { byAuthor: [], byUser: [] };
23498
+ const tags = result?.tags ?? [];
23285
23499
  if (!bundle) {
23286
23500
  throw new HTTPException6(404, {
23287
23501
  message: tx(SERVER_TEXTS.nodeNotFound, { path: nodePath })
@@ -23297,6 +23511,7 @@ function registerNodesRoutes(app, deps) {
23297
23511
  links: { incoming: bundle.linksIn, outgoing: bundle.linksOut },
23298
23512
  issues: bundle.issues,
23299
23513
  kindRegistry: deps.kindRegistry,
23514
+ providerRegistry: deps.providerRegistry,
23300
23515
  contributionsRegistry: deps.contributionsRegistry
23301
23516
  });
23302
23517
  });
@@ -23334,7 +23549,7 @@ function registerNodesRoutes(app, deps) {
23334
23549
  const contribByPath = contributionsOmitted ? /* @__PURE__ */ new Map() : await groupContributionsByPath(
23335
23550
  await adapter.contributions.listForPaths(pagePaths)
23336
23551
  );
23337
- const tagByPath = await groupTagsByPath(
23552
+ const tagByPath = groupTagsByPath(
23338
23553
  await adapter.tags.listForPaths(pagePaths)
23339
23554
  );
23340
23555
  return { contributionsByPath: contribByPath, tagsByPath: tagByPath };
@@ -23347,7 +23562,7 @@ function registerNodesRoutes(app, deps) {
23347
23562
  ...n,
23348
23563
  isFavorite: favSet.has(n.path),
23349
23564
  contributions: contributionsByPath.get(n.path) ?? [],
23350
- tags: tagsByPath.get(n.path) ?? { byAuthor: [], byUser: [] }
23565
+ tags: tagsByPath.get(n.path) ?? []
23351
23566
  }));
23352
23567
  return c.json(
23353
23568
  buildListEnvelope({
@@ -23361,6 +23576,7 @@ function registerNodesRoutes(app, deps) {
23361
23576
  total,
23362
23577
  page: { offset, limit },
23363
23578
  kindRegistry: deps.kindRegistry,
23579
+ providerRegistry: deps.providerRegistry,
23364
23580
  contributionsRegistry: deps.contributionsRegistry
23365
23581
  })
23366
23582
  );
@@ -23369,24 +23585,15 @@ function registerNodesRoutes(app, deps) {
23369
23585
  function parseIncludes(raw) {
23370
23586
  return new Set(parseCsv(raw));
23371
23587
  }
23372
- function groupTagsBySource(rows) {
23373
- const byAuthor = /* @__PURE__ */ new Set();
23374
- const byUser = /* @__PURE__ */ new Set();
23375
- for (const r of rows) (r.source === "author" ? byAuthor : byUser).add(r.tag);
23376
- return {
23377
- byAuthor: [...byAuthor].sort(),
23378
- byUser: [...byUser].sort()
23379
- };
23380
- }
23381
- async function groupTagsByPath(rows) {
23588
+ function groupTagsByPath(rows) {
23382
23589
  const buckets = /* @__PURE__ */ new Map();
23383
23590
  for (const r of rows) {
23384
- const list = buckets.get(r.nodePath);
23385
- if (list) list.push({ tag: r.tag, source: r.source });
23386
- else buckets.set(r.nodePath, [{ tag: r.tag, source: r.source }]);
23591
+ const set = buckets.get(r.nodePath);
23592
+ if (set) set.add(r.tag);
23593
+ else buckets.set(r.nodePath, /* @__PURE__ */ new Set([r.tag]));
23387
23594
  }
23388
23595
  const out = /* @__PURE__ */ new Map();
23389
- for (const [path, entries] of buckets) out.set(path, groupTagsBySource(entries));
23596
+ for (const [path, set] of buckets) out.set(path, [...set].sort());
23390
23597
  return out;
23391
23598
  }
23392
23599
  async function groupContributionsByPath(rows) {
@@ -23515,6 +23722,7 @@ function registerPluginsRoute(app, deps) {
23515
23722
  filters: {},
23516
23723
  total: items.length,
23517
23724
  kindRegistry: deps.kindRegistry,
23725
+ providerRegistry: deps.providerRegistry,
23518
23726
  contributionsRegistry: deps.contributionsRegistry
23519
23727
  })
23520
23728
  );
@@ -23731,6 +23939,7 @@ function projectListResponse(c, deps, overrides) {
23731
23939
  filters: {},
23732
23940
  total: items.length,
23733
23941
  kindRegistry: deps.kindRegistry,
23942
+ providerRegistry: deps.providerRegistry,
23734
23943
  contributionsRegistry: deps.contributionsRegistry
23735
23944
  })
23736
23945
  );
@@ -23891,12 +24100,12 @@ var parsePatchBody2 = makeBodyValidator(PATCH_BODY_SCHEMA, {
23891
24100
  import { HTTPException as HTTPException10 } from "hono/http-exception";
23892
24101
 
23893
24102
  // server/util/skillmapignore-io.ts
23894
- import { existsSync as existsSync24, readFileSync as readFileSync17, writeFileSync as writeFileSync2 } from "fs";
24103
+ import { existsSync as existsSync26, readFileSync as readFileSync17, writeFileSync as writeFileSync2 } from "fs";
23895
24104
  import { resolve as resolve35 } from "path";
23896
24105
  var IGNORE_FILENAME2 = ".skillmapignore";
23897
24106
  function readPatterns(cwd) {
23898
24107
  const path = resolve35(cwd, IGNORE_FILENAME2);
23899
- if (!existsSync24(path)) return [];
24108
+ if (!existsSync26(path)) return [];
23900
24109
  let raw;
23901
24110
  try {
23902
24111
  raw = readFileSync17(path, "utf8");
@@ -23907,7 +24116,7 @@ function readPatterns(cwd) {
23907
24116
  }
23908
24117
  function writePatterns(cwd, nextPatterns) {
23909
24118
  const path = resolve35(cwd, IGNORE_FILENAME2);
23910
- const prior = existsSync24(path) ? safeRead(path) : "";
24119
+ const prior = existsSync26(path) ? safeRead(path) : "";
23911
24120
  const content = buildContent(prior, nextPatterns);
23912
24121
  writeFileSync2(path, content, "utf8");
23913
24122
  }
@@ -24257,7 +24466,7 @@ var parsePatchBody4 = makeBodyValidator(PATCH_BODY_SCHEMA3, {
24257
24466
  });
24258
24467
 
24259
24468
  // server/routes/active-provider.ts
24260
- import { existsSync as existsSync25 } from "fs";
24469
+ import { existsSync as existsSync27 } from "fs";
24261
24470
  import { HTTPException as HTTPException12 } from "hono/http-exception";
24262
24471
  function registerActiveProviderRoute(app, deps) {
24263
24472
  app.get("/api/active-provider", (c) => {
@@ -24271,7 +24480,7 @@ function registerActiveProviderRoute(app, deps) {
24271
24480
  });
24272
24481
  }
24273
24482
  function buildEnvelope4(deps) {
24274
- const r = resolveActiveProvider(deps.runtimeContext.cwd);
24483
+ const r = resolveActiveProvider(deps.runtimeContext.cwd, deps.providers);
24275
24484
  return {
24276
24485
  activeProvider: r.resolved,
24277
24486
  detected: r.detected,
@@ -24290,7 +24499,7 @@ function applyLensSwitch(deps, newValue) {
24290
24499
  });
24291
24500
  }
24292
24501
  const dbPath = resolveDbPath({ db: void 0, cwd });
24293
- if (!existsSync25(dbPath)) return { dropped: null };
24502
+ if (!existsSync27(dbPath)) return { dropped: null };
24294
24503
  const dropResult = dropScanZone(dbPath);
24295
24504
  return {
24296
24505
  dropped: {
@@ -24411,6 +24620,14 @@ function createWatcherService(opts) {
24411
24620
  onPluginWarning: (message) => {
24412
24621
  log.warn(sanitizeForTerminal(message));
24413
24622
  },
24623
+ onDriftReset: (info) => {
24624
+ log.warn(
24625
+ tx(SERVER_TEXTS.watcherDriftReset, {
24626
+ dbVersion: info.dbVersion,
24627
+ currentVersion: info.currentVersion
24628
+ })
24629
+ );
24630
+ },
24414
24631
  onReady: (info) => {
24415
24632
  opts.broadcaster.broadcast(
24416
24633
  buildWatcherStartedEvent({ roots: info.roots, debounceMs: info.debounceMs })
@@ -24553,13 +24770,8 @@ async function loadPersistedScan(deps) {
24553
24770
  if (list) list.push(r);
24554
24771
  else byPath3.set(r.nodePath, [r]);
24555
24772
  }
24556
- const tagBuckets = /* @__PURE__ */ new Map();
24557
- for (const r of tagRows) {
24558
- const list = tagBuckets.get(r.nodePath);
24559
- if (list) list.push({ tag: r.tag, source: r.source });
24560
- else tagBuckets.set(r.nodePath, [{ tag: r.tag, source: r.source }]);
24561
- }
24562
- return { loaded, favSet, contribByPath: byPath3, tagBuckets };
24773
+ const tagsByPath = groupTagsByPath2(tagRows);
24774
+ return { loaded, favSet, contribByPath: byPath3, tagsByPath };
24563
24775
  }
24564
24776
  );
24565
24777
  if (opened === null) {
@@ -24571,18 +24783,20 @@ async function loadPersistedScan(deps) {
24571
24783
  ...n,
24572
24784
  isFavorite: opened.favSet.has(n.path),
24573
24785
  contributions: opened.contribByPath.get(n.path) ?? [],
24574
- tags: groupTagsBySource2(opened.tagBuckets.get(n.path) ?? [])
24786
+ tags: opened.tagsByPath.get(n.path) ?? []
24575
24787
  }))
24576
24788
  };
24577
24789
  }
24578
- function groupTagsBySource2(rows) {
24579
- const byAuthor = /* @__PURE__ */ new Set();
24580
- const byUser = /* @__PURE__ */ new Set();
24581
- for (const r of rows) (r.source === "author" ? byAuthor : byUser).add(r.tag);
24582
- return {
24583
- byAuthor: [...byAuthor].sort(),
24584
- byUser: [...byUser].sort()
24585
- };
24790
+ function groupTagsByPath2(rows) {
24791
+ const buckets = /* @__PURE__ */ new Map();
24792
+ for (const r of rows) {
24793
+ const set = buckets.get(r.nodePath);
24794
+ if (set) set.add(r.tag);
24795
+ else buckets.set(r.nodePath, /* @__PURE__ */ new Set([r.tag]));
24796
+ }
24797
+ const out = /* @__PURE__ */ new Map();
24798
+ for (const [path, set] of buckets) out.set(path, [...set].sort());
24799
+ return out;
24586
24800
  }
24587
24801
  async function runFreshScan(deps) {
24588
24802
  if (deps.options.noBuiltIns || deps.options.noPlugins) {
@@ -24820,7 +25034,7 @@ function registerUpdateStatusRoute(app, deps) {
24820
25034
  }
24821
25035
 
24822
25036
  // server/static.ts
24823
- import { existsSync as existsSync26 } from "fs";
25037
+ import { existsSync as existsSync28 } from "fs";
24824
25038
  import { readFile as readFile6 } from "fs/promises";
24825
25039
  import { extname, join as join19 } from "path";
24826
25040
  import { serveStatic } from "@hono/node-server/serve-static";
@@ -24875,7 +25089,7 @@ function createSpaFallback(opts) {
24875
25089
  if (c.req.method !== "GET" && c.req.method !== "HEAD") return c.notFound();
24876
25090
  if (opts.uiDist === null) return htmlResponse(c, placeholder);
24877
25091
  const indexPath = join19(opts.uiDist, INDEX_HTML);
24878
- if (!existsSync26(indexPath)) return htmlResponse(c, placeholder);
25092
+ if (!existsSync28(indexPath)) return htmlResponse(c, placeholder);
24879
25093
  return fileResponse(c, indexPath);
24880
25094
  };
24881
25095
  }
@@ -25019,6 +25233,8 @@ function createApp(deps) {
25019
25233
  options: deps.options,
25020
25234
  runtimeContext: deps.runtimeContext,
25021
25235
  kindRegistry: deps.kindRegistry,
25236
+ providerRegistry: deps.providerRegistry,
25237
+ providers: deps.providers,
25022
25238
  contributionsRegistry: deps.contributionsRegistry,
25023
25239
  pluginRuntime: deps.pluginRuntime,
25024
25240
  configService,
@@ -25314,6 +25530,25 @@ function buildKindRegistry(providers) {
25314
25530
  return registry;
25315
25531
  }
25316
25532
 
25533
+ // server/provider-registry.ts
25534
+ function buildProviderRegistry(providers) {
25535
+ const registry = {};
25536
+ for (const provider of providers) {
25537
+ const ui = provider.presentation;
25538
+ if (!ui) continue;
25539
+ const entry = {
25540
+ label: ui.label,
25541
+ color: ui.color
25542
+ };
25543
+ if (ui.colorDark !== void 0) entry.colorDark = ui.colorDark;
25544
+ if (ui.emoji !== void 0) entry.emoji = ui.emoji;
25545
+ if (ui.icon !== void 0) entry.icon = ui.icon;
25546
+ if (ui.hideChip !== void 0) entry.hideChip = ui.hideChip;
25547
+ registry[provider.id] = entry;
25548
+ }
25549
+ return registry;
25550
+ }
25551
+
25317
25552
  // server/contributions-registry.ts
25318
25553
  function buildContributionsRegistry(kernel) {
25319
25554
  const registry = {};
@@ -25463,7 +25698,7 @@ function validateNoUi(noUi, uiDist) {
25463
25698
  }
25464
25699
 
25465
25700
  // server/paths.ts
25466
- import { existsSync as existsSync27, statSync as statSync10 } from "fs";
25701
+ import { existsSync as existsSync29, statSync as statSync10 } from "fs";
25467
25702
  import { dirname as dirname18, isAbsolute as isAbsolute11, join as join20, resolve as resolve37 } from "path";
25468
25703
  import { fileURLToPath as fileURLToPath6 } from "url";
25469
25704
  var DEFAULT_UI_REL = join20("ui", "dist", "ui", "browser");
@@ -25478,10 +25713,10 @@ function resolveExplicitUiDist(ctx, raw) {
25478
25713
  return isAbsolute11(raw) ? raw : resolve37(ctx.cwd, raw);
25479
25714
  }
25480
25715
  function isUiBundleDir(path) {
25481
- if (!existsSync27(path)) return false;
25716
+ if (!existsSync29(path)) return false;
25482
25717
  try {
25483
25718
  if (!statSync10(path).isDirectory()) return false;
25484
- return existsSync27(join20(path, INDEX_HTML2));
25719
+ return existsSync29(join20(path, INDEX_HTML2));
25485
25720
  } catch {
25486
25721
  return false;
25487
25722
  }
@@ -25525,7 +25760,7 @@ async function createServer(options, extra = {}) {
25525
25760
  const specVersion = await resolveSpecVersion2();
25526
25761
  const runtimeContext = extra.runtimeContext ?? defaultRuntimeContext();
25527
25762
  const broadcaster = new WsBroadcaster();
25528
- const { pluginRuntime, kindRegistry } = await assemblePluginRuntime(options, runtimeContext);
25763
+ const { pluginRuntime, kindRegistry, providerRegistry, providers } = await assemblePluginRuntime(options, runtimeContext);
25529
25764
  const { kernel, contributionsRegistry } = assembleKernel(pluginRuntime, options.noBuiltIns);
25530
25765
  const watcherHolder = { current: null };
25531
25766
  const app = createApp({
@@ -25534,6 +25769,8 @@ async function createServer(options, extra = {}) {
25534
25769
  broadcaster,
25535
25770
  runtimeContext,
25536
25771
  kindRegistry,
25772
+ providerRegistry,
25773
+ providers,
25537
25774
  contributionsRegistry,
25538
25775
  pluginRuntime,
25539
25776
  watcherHolder,
@@ -25592,11 +25829,10 @@ async function assemblePluginRuntime(options, runtimeContext) {
25592
25829
  log.warn(sanitizeForTerminal(warn));
25593
25830
  }
25594
25831
  const builtInProviders = options.noBuiltIns ? [] : collectBuiltInProviders();
25595
- const kindRegistry = buildKindRegistry([
25596
- ...builtInProviders,
25597
- ...pluginRuntime.extensions.providers
25598
- ]);
25599
- return { pluginRuntime, kindRegistry };
25832
+ const allProviders = [...builtInProviders, ...pluginRuntime.extensions.providers];
25833
+ const kindRegistry = buildKindRegistry(allProviders);
25834
+ const providerRegistry = buildProviderRegistry(allProviders);
25835
+ return { pluginRuntime, kindRegistry, providerRegistry, providers: allProviders };
25600
25836
  }
25601
25837
  function assembleKernel(pluginRuntime, noBuiltIns) {
25602
25838
  const kernel = createKernel();
@@ -26035,7 +26271,7 @@ var ServeCommand = class extends SmCommand {
26035
26271
  return ExitCode.Error;
26036
26272
  }
26037
26273
  const dbPath = resolveDbPath({ db: this.db, ...runtimeCtx });
26038
- if (this.db !== void 0 && !existsSync28(dbPath)) {
26274
+ if (this.db !== void 0 && !existsSync30(dbPath)) {
26039
26275
  this.printer.info(
26040
26276
  tx(SERVE_TEXTS.dbNotFound, {
26041
26277
  glyph: errGlyph,
@@ -27300,7 +27536,7 @@ var STUB_COMMANDS = [
27300
27536
  ];
27301
27537
 
27302
27538
  // cli/commands/tutorial.ts
27303
- import { cpSync as cpSync2, existsSync as existsSync29, mkdirSync as mkdirSync6, rmSync as rmSync2, statSync as statSync11 } from "fs";
27539
+ import { cpSync as cpSync2, existsSync as existsSync31, mkdirSync as mkdirSync6, rmSync as rmSync2, statSync as statSync11 } from "fs";
27304
27540
  import { dirname as dirname19, join as join21, resolve as resolve39 } from "path";
27305
27541
  import { fileURLToPath as fileURLToPath7 } from "url";
27306
27542
  import { Command as Command37, Option as Option35 } from "clipanion";
@@ -27399,7 +27635,7 @@ var TutorialCommand = class extends SmCommand {
27399
27635
  const spec = VARIANT_SPECS[variant];
27400
27636
  const targetDir = join21(ctx.cwd, ".claude", "skills", spec.slug);
27401
27637
  const targetDisplay = `.claude/skills/${spec.slug}/`;
27402
- if (existsSync29(targetDir) && !this.force) {
27638
+ if (existsSync31(targetDir) && !this.force) {
27403
27639
  this.printer.error(
27404
27640
  tx(TUTORIAL_TEXTS.alreadyExists, {
27405
27641
  glyph: errGlyph,
@@ -27482,7 +27718,7 @@ function resolveSkillSourceDir(variant) {
27482
27718
  resolve39(here, "../cli/tutorial", spec.slug)
27483
27719
  ];
27484
27720
  for (const candidate of candidates) {
27485
- if (existsSync29(candidate) && statSync11(candidate).isDirectory()) {
27721
+ if (existsSync31(candidate) && statSync11(candidate).isDirectory()) {
27486
27722
  cachedSourceDirs.set(variant, candidate);
27487
27723
  return candidate;
27488
27724
  }
@@ -27667,7 +27903,7 @@ function resolveBareInvocation(rawArgs) {
27667
27903
  if (first !== void 0 && first.startsWith("-") && !passthrough.has(first)) {
27668
27904
  const isSingleDashLong = !first.startsWith("--") && first.length > 2;
27669
27905
  if (isSingleDashLong) return null;
27670
- if (existsSync30(defaultProjectDbPath(defaultRuntimeContext()))) {
27906
+ if (existsSync32(defaultProjectDbPath(defaultRuntimeContext()))) {
27671
27907
  return ["serve", ...rawArgs];
27672
27908
  }
27673
27909
  return resolveBareDefault();
@@ -27676,7 +27912,7 @@ function resolveBareInvocation(rawArgs) {
27676
27912
  }
27677
27913
  function resolveBareDefault() {
27678
27914
  const ctx = defaultRuntimeContext();
27679
- if (existsSync30(defaultProjectDbPath(ctx))) {
27915
+ if (existsSync32(defaultProjectDbPath(ctx))) {
27680
27916
  return ["serve"];
27681
27917
  }
27682
27918
  const stderr = process.stderr;