@skill-map/cli 0.42.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,
@@ -498,7 +505,7 @@ var claudeProvider = {
498
505
  icon: { kind: "pi", id: "pi-user" }
499
506
  },
500
507
  // `frontmatter.name` is the documented canonical identifier
501
- // (https://code.claude.com/docs/en/agents.md); `filename-basename`
508
+ // (https://code.claude.com/docs/en/sub-agents.md); `filename-basename`
502
509
  // is a graceful fallback for agents authored without an explicit
503
510
  // `name:` field, the file at `.claude/agents/<id>.md` resolves
504
511
  // `@<id>` even when frontmatter is partial.
@@ -981,11 +988,11 @@ var antigravityProvider = {
981
988
  // plugins/openai/providers/openai/schemas/agent.schema.json
982
989
  var agent_schema_default2 = {
983
990
  $schema: "https://json-schema.org/draft/2020-12/schema",
984
- $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",
985
992
  title: "FrontmatterCodexAgent",
986
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.",
987
994
  allOf: [
988
- { $ref: "https://skill-map.dev/spec/v0/frontmatter/base.schema.json" }
995
+ { $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
989
996
  ],
990
997
  type: "object",
991
998
  additionalProperties: true,
@@ -1088,15 +1095,34 @@ var openaiProvider = {
1088
1095
  // plugins/agent-skills/providers/agent-skills/schemas/skill.schema.json
1089
1096
  var skill_schema_default2 = {
1090
1097
  $schema: "https://json-schema.org/draft/2020-12/schema",
1091
- $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",
1092
1099
  title: "FrontmatterAgentSkillsSkill",
1093
- 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.",
1094
1101
  allOf: [
1095
- { $ref: "https://skill-map.dev/spec/v0/frontmatter/base.schema.json" }
1102
+ { $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
1096
1103
  ],
1097
1104
  type: "object",
1098
1105
  additionalProperties: true,
1099
- 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
+ }
1100
1126
  };
1101
1127
 
1102
1128
  // plugins/agent-skills/providers/agent-skills/index.ts
@@ -1150,11 +1176,11 @@ var agentSkillsProvider = {
1150
1176
  // plugins/core/providers/core-markdown/schemas/markdown.schema.json
1151
1177
  var markdown_schema_default = {
1152
1178
  $schema: "https://json-schema.org/draft/2020-12/schema",
1153
- $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",
1154
1180
  title: "FrontmatterMarkdown",
1155
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.",
1156
1182
  allOf: [
1157
- { $ref: "https://skill-map.dev/spec/v0/frontmatter/base.schema.json" }
1183
+ { $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
1158
1184
  ],
1159
1185
  type: "object",
1160
1186
  additionalProperties: true
@@ -2935,10 +2961,10 @@ function buildSchemaValidators() {
2935
2961
  hook: "extension-hook"
2936
2962
  };
2937
2963
  const pluginManifestValidator = ajv.compile({
2938
- $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"
2939
2965
  });
2940
2966
  const contributionValidators = /* @__PURE__ */ new Map();
2941
- 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";
2942
2968
  function getContributionValidator(slot) {
2943
2969
  if (!KNOWN_SLOT_NAMES.has(slot)) return null;
2944
2970
  const existing = contributionValidators.get(slot);
@@ -3920,11 +3946,11 @@ var UPDATE_CHECK_TEXTS = {
3920
3946
  // package.json
3921
3947
  var package_default = {
3922
3948
  name: "@skill-map/cli",
3923
- version: "0.42.0",
3949
+ version: "0.43.0",
3924
3950
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
3925
3951
  license: "MIT",
3926
3952
  type: "module",
3927
- homepage: "https://skill-map.dev",
3953
+ homepage: "https://skill-map.ai",
3928
3954
  repository: {
3929
3955
  type: "git",
3930
3956
  url: "git+https://github.com/crystian/skill-map.git",
@@ -4352,40 +4378,40 @@ var updateCheckHook = {
4352
4378
  };
4353
4379
 
4354
4380
  // plugins/built-ins.ts
4355
- var claudeProvider2 = { ...claudeProvider, pluginId: "claude", version: "0.42.0" };
4356
- var atDirectiveExtractor2 = { ...atDirectiveExtractor, pluginId: "claude", version: "0.42.0" };
4357
- var slashCommandExtractor2 = { ...slashCommandExtractor, pluginId: "claude", version: "0.42.0" };
4358
- var antigravityProvider2 = { ...antigravityProvider, pluginId: "antigravity", version: "0.42.0" };
4359
- var openaiProvider2 = { ...openaiProvider, pluginId: "openai", version: "0.42.0" };
4360
- var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills", version: "0.42.0" };
4361
- var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core", version: "0.42.0" };
4362
- var annotationsExtractor2 = { ...annotationsExtractor, pluginId: "core", version: "0.42.0" };
4363
- var externalUrlCounterExtractor2 = { ...externalUrlCounterExtractor, pluginId: "core", version: "0.42.0" };
4364
- var markdownLinkExtractor2 = { ...markdownLinkExtractor, pluginId: "core", version: "0.42.0" };
4365
- var mcpToolsExtractor2 = { ...mcpToolsExtractor, pluginId: "core", version: "0.42.0" };
4366
- var toolsCounterExtractor2 = { ...toolsCounterExtractor, pluginId: "core", version: "0.42.0" };
4367
- var annotationFieldUnknownAnalyzer2 = { ...annotationFieldUnknownAnalyzer, pluginId: "core", version: "0.42.0" };
4368
- var annotationOrphanAnalyzer2 = { ...annotationOrphanAnalyzer, pluginId: "core", version: "0.42.0" };
4369
- var annotationStaleAnalyzer2 = { ...annotationStaleAnalyzer, pluginId: "core", version: "0.42.0" };
4370
- var contributionOrphanAnalyzer2 = { ...contributionOrphanAnalyzer, pluginId: "core", version: "0.42.0" };
4371
- var issueCounterAnalyzer2 = { ...issueCounterAnalyzer, pluginId: "core", version: "0.42.0" };
4372
- var jobFileOrphanAnalyzer2 = { ...jobFileOrphanAnalyzer, pluginId: "core", version: "0.42.0" };
4373
- var linkConflictAnalyzer2 = { ...linkConflictAnalyzer, pluginId: "core", version: "0.42.0" };
4374
- var linkCounterAnalyzer2 = { ...linkCounterAnalyzer, pluginId: "core", version: "0.42.0" };
4375
- var linkSelfLoopAnalyzer2 = { ...linkSelfLoopAnalyzer, pluginId: "core", version: "0.42.0" };
4376
- var nameReservedAnalyzer2 = { ...nameReservedAnalyzer, pluginId: "core", version: "0.42.0" };
4377
- var nodeStabilityAnalyzer2 = { ...nodeStabilityAnalyzer, pluginId: "core", version: "0.42.0" };
4378
- var nodeSupersededAnalyzer2 = { ...nodeSupersededAnalyzer, pluginId: "core", version: "0.42.0" };
4379
- var referenceBrokenAnalyzer2 = { ...referenceBrokenAnalyzer, pluginId: "core", version: "0.42.0" };
4380
- var referenceRedundantAnalyzer2 = { ...referenceRedundantAnalyzer, pluginId: "core", version: "0.42.0" };
4381
- var schemaViolationAnalyzer2 = { ...schemaViolationAnalyzer, pluginId: "core", version: "0.42.0" };
4382
- var signalCollisionAnalyzer2 = { ...signalCollisionAnalyzer, pluginId: "core", version: "0.42.0" };
4383
- var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core", version: "0.42.0" };
4384
- var asciiFormatter2 = { ...asciiFormatter, pluginId: "core", version: "0.42.0" };
4385
- var jsonFormatter2 = { ...jsonFormatter, pluginId: "core", version: "0.42.0" };
4386
- var nodeBumpAction2 = { ...nodeBumpAction, pluginId: "core", version: "0.42.0" };
4387
- var nodeSupersedeAction2 = { ...nodeSupersedeAction, pluginId: "core", version: "0.42.0" };
4388
- var updateCheckHook2 = { ...updateCheckHook, pluginId: "core", version: "0.42.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" };
4389
4415
  var builtInBundles = [
4390
4416
  {
4391
4417
  id: "claude",
@@ -7444,8 +7470,7 @@ async function replaceAllScanTags(trx, records, livePaths = /* @__PURE__ */ new
7444
7470
  if (records.length === 0) return;
7445
7471
  const rows = records.map((r) => ({
7446
7472
  nodePath: r.nodePath,
7447
- tag: r.tag,
7448
- source: r.source
7473
+ tag: r.tag
7449
7474
  }));
7450
7475
  const BATCH = 300;
7451
7476
  for (let i = 0; i < rows.length; i += BATCH) {
@@ -7453,18 +7478,16 @@ async function replaceAllScanTags(trx, records, livePaths = /* @__PURE__ */ new
7453
7478
  }
7454
7479
  }
7455
7480
  async function loadTagsForNode(db, nodePath) {
7456
- const rows = await db.selectFrom("scan_node_tags").select(["nodePath", "tag", "source"]).where("nodePath", "=", nodePath).orderBy("source", "asc").orderBy("tag", "asc").execute();
7457
- 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 }));
7458
7483
  }
7459
7484
  async function loadTagsForPaths(db, nodePaths) {
7460
7485
  if (nodePaths.length === 0) return [];
7461
- const rows = await db.selectFrom("scan_node_tags").select(["nodePath", "tag", "source"]).where("nodePath", "in", [...nodePaths]).orderBy("source", "asc").orderBy("tag", "asc").execute();
7462
- 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 }));
7463
7488
  }
7464
- async function findNodesByTag(db, tag, source) {
7465
- let q = db.selectFrom("scan_node_tags").select("nodePath").where("tag", "=", tag);
7466
- if (source !== void 0) q = q.where("source", "=", source);
7467
- 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();
7468
7491
  return rows.map((r) => r.nodePath);
7469
7492
  }
7470
7493
 
@@ -7662,19 +7685,18 @@ function pickIntegerVersion(v) {
7662
7685
  function nodesToTagRecords(nodes) {
7663
7686
  const records = [];
7664
7687
  for (const node of nodes) {
7665
- pushTagRecords(records, node.path, node.frontmatter?.["tags"], "author");
7666
- pushTagRecords(records, node.path, node.sidecar?.annotations?.["tags"], "user");
7688
+ pushTagRecords(records, node.path, node.sidecar?.annotations?.["tags"]);
7667
7689
  }
7668
7690
  return records;
7669
7691
  }
7670
- function pushTagRecords(out, nodePath, raw, source) {
7692
+ function pushTagRecords(out, nodePath, raw) {
7671
7693
  if (!Array.isArray(raw)) return;
7672
7694
  const seen = /* @__PURE__ */ new Set();
7673
7695
  for (const item of raw) {
7674
7696
  if (typeof item !== "string" || item.length === 0) continue;
7675
7697
  if (seen.has(item)) continue;
7676
7698
  seen.add(item);
7677
- out.push({ nodePath, tag: item, source });
7699
+ out.push({ nodePath, tag: item });
7678
7700
  }
7679
7701
  }
7680
7702
  function linkToRow(link) {
@@ -7915,7 +7937,7 @@ var SqliteStorageAdapter = class {
7915
7937
  this.tags = {
7916
7938
  listForNode: (nodePath) => loadTagsForNode(this.db, nodePath),
7917
7939
  listForPaths: (paths) => loadTagsForPaths(this.db, paths),
7918
- findNodes: (tag, source) => findNodesByTag(this.db, tag, source)
7940
+ findNodes: (tag) => findNodesByTag(this.db, tag)
7919
7941
  };
7920
7942
  this.issues = {
7921
7943
  listAll: () => listAllIssues(this.db),
@@ -12618,9 +12640,22 @@ var DbRestoreCommand = class extends SmCommand {
12618
12640
  };
12619
12641
 
12620
12642
  // cli/commands/db/reset.ts
12621
- import { rm as rm2 } from "fs/promises";
12622
12643
  import { DatabaseSync as DatabaseSync5 } from "node:sqlite";
12623
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
12624
12659
  var DbResetCommand = class extends SmCommand {
12625
12660
  static paths = [["db", "reset"]];
12626
12661
  static usage = Command8.Usage({
@@ -12680,10 +12715,7 @@ var DbResetCommand = class extends SmCommand {
12680
12715
  return ExitCode.Error;
12681
12716
  }
12682
12717
  }
12683
- for (const suffix of ["", "-wal", "-shm"]) {
12684
- const p = `${path}${suffix}`;
12685
- if (await pathExists(p)) await rm2(p);
12686
- }
12718
+ await removeDbFiles(path);
12687
12719
  const ansiHard = this.ansiFor("stdout");
12688
12720
  this.printer.data(
12689
12721
  tx(DB_TEXTS.resetHardDeleted, {
@@ -14424,7 +14456,7 @@ import { join as join17 } from "path";
14424
14456
  import { Command as Command17, Option as Option16 } from "clipanion";
14425
14457
 
14426
14458
  // kernel/orchestrator/index.ts
14427
- import { existsSync as existsSync21, statSync as statSync6 } from "fs";
14459
+ import { existsSync as existsSync22, statSync as statSync6 } from "fs";
14428
14460
  import { isAbsolute as isAbsolute7, resolve as resolve28 } from "path";
14429
14461
  import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
14430
14462
  import cl100k_base from "js-tiktoken/ranks/cl100k_base";
@@ -15471,7 +15503,7 @@ function computeDriftStatus(args2) {
15471
15503
  }
15472
15504
 
15473
15505
  // kernel/sidecar/discover-orphans.ts
15474
- 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";
15475
15507
  import { join as join13, relative as relative4, sep as sep4 } from "path";
15476
15508
  function discoverOrphanSidecars(roots, shouldSkip) {
15477
15509
  const out = [];
@@ -15499,7 +15531,7 @@ function walk(root, current, shouldSkip, out) {
15499
15531
  if (!entry.isFile()) continue;
15500
15532
  if (!entry.name.endsWith(".sm")) continue;
15501
15533
  const expectedMd = `${full.slice(0, -".sm".length)}.md`;
15502
- if (existsSync19(expectedMd) && safeIsFile(expectedMd)) continue;
15534
+ if (existsSync20(expectedMd) && safeIsFile(expectedMd)) continue;
15503
15535
  out.push({ sidecarPath: full, relativePath: rel, expectedMdPath: expectedMd });
15504
15536
  }
15505
15537
  }
@@ -15513,7 +15545,7 @@ function safeIsFile(path) {
15513
15545
 
15514
15546
  // kernel/orchestrator/node-build.ts
15515
15547
  import { createHash } from "crypto";
15516
- import { existsSync as existsSync20 } from "fs";
15548
+ import { existsSync as existsSync21 } from "fs";
15517
15549
  import { isAbsolute as isAbsolute6, resolve as resolvePath } from "path";
15518
15550
  import "js-tiktoken/lite";
15519
15551
  import yaml4 from "js-yaml";
@@ -15677,11 +15709,11 @@ function resolveSidecarOverlay(relativePath2, nodePathForIssue, roots, liveBodyH
15677
15709
  }
15678
15710
  function resolveAbsoluteMdPath(relativePath2, roots) {
15679
15711
  if (isAbsolute6(relativePath2)) {
15680
- return existsSync20(relativePath2) ? relativePath2 : null;
15712
+ return existsSync21(relativePath2) ? relativePath2 : null;
15681
15713
  }
15682
15714
  for (const root of roots) {
15683
15715
  const candidate = resolvePath(root, relativePath2);
15684
- if (existsSync20(candidate)) return candidate;
15716
+ if (existsSync21(candidate)) return candidate;
15685
15717
  }
15686
15718
  return null;
15687
15719
  }
@@ -16246,7 +16278,7 @@ function validateRoots(roots) {
16246
16278
  throw new Error(ORCHESTRATOR_TEXTS.runScanRootEmptyArray);
16247
16279
  }
16248
16280
  for (const root of roots) {
16249
- if (!existsSync21(root) || !statSync6(root).isDirectory()) {
16281
+ if (!existsSync22(root) || !statSync6(root).isDirectory()) {
16250
16282
  throw new Error(tx(ORCHESTRATOR_TEXTS.runScanRootMissing, { root }));
16251
16283
  }
16252
16284
  }
@@ -16255,7 +16287,7 @@ function resolveActiveProviderOption(optionValue, roots, providers) {
16255
16287
  if (optionValue !== void 0) return optionValue;
16256
16288
  for (const root of roots) {
16257
16289
  const absRoot = isAbsolute7(root) ? root : resolve28(root);
16258
- if (!existsSync21(absRoot)) continue;
16290
+ if (!existsSync22(absRoot)) continue;
16259
16291
  const detected = resolveActiveProvider(absRoot, providers).resolved;
16260
16292
  if (detected !== null) return detected;
16261
16293
  }
@@ -16895,6 +16927,99 @@ async function promptForLens(detected, stdin, stderr, warnGlyph) {
16895
16927
  }
16896
16928
  }
16897
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
+
16898
17023
  // core/runtime/scan-runner.ts
16899
17024
  async function runScanForCommand(opts) {
16900
17025
  const ctx = opts.ctx ?? defaultRuntimeContext();
@@ -17092,7 +17217,29 @@ function buildRunScanEmitter(opts) {
17092
17217
  colorEnabled: opts.colorEnabled === true
17093
17218
  });
17094
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
+ }
17095
17240
  async function runPersistPath(opts, dbPath, jobsDir, strict, loadPrior, runScanWith, extensions) {
17241
+ const driftError = await rebuildOnDrift(opts, dbPath);
17242
+ if (driftError) return driftError;
17096
17243
  let outcome;
17097
17244
  try {
17098
17245
  outcome = await withSqlite({ databasePath: dbPath }, async (adapter) => {
@@ -18182,19 +18329,6 @@ import { Command as Command20, Option as Option19 } from "clipanion";
18182
18329
  var LIST_TEXTS = {
18183
18330
  invalidSortBy: '{{glyph}} --sort-by: invalid sort field "{{value}}".\n {{hint}}\n',
18184
18331
  invalidSortByHint: "Allowed: {{allowed}}.",
18185
- /**
18186
- * §3.1b two-line block. Closed enum: hint enumerates the two valid
18187
- * values so the operator does not need to re-read `--help`.
18188
- */
18189
- invalidTagSource: '{{glyph}} --tag-source: expected "author" or "user", got "{{value}}".\n {{hint}}\n',
18190
- invalidTagSourceHint: "Allowed: author, user.",
18191
- /**
18192
- * §3.1b two-line block. `--tag-source` is a narrowing filter on
18193
- * `--tag`; the hint repeats the dependency in operator-actionable
18194
- * form.
18195
- */
18196
- tagSourceWithoutTag: "{{glyph}} --tag-source requires --tag <name>.\n {{hint}}\n",
18197
- tagSourceWithoutTagHint: "The source filter narrows tag matches, it does not stand alone. Pass --tag <name> alongside --tag-source.",
18198
18332
  noNodesFound: "No nodes found.\n",
18199
18333
  // --- renderTable column headers ----------------------------------------
18200
18334
  tableHeaderPath: "PATH",
@@ -18231,9 +18365,8 @@ var ListCommand = class extends SmCommand {
18231
18365
  Reads from the persisted scan snapshot (scan_nodes). Filters:
18232
18366
  --kind <k> restricts to one node kind; --issue keeps only nodes
18233
18367
  that touch at least one current issue; --tag <name> keeps only
18234
- nodes carrying that tag (matches the union of frontmatter.tags
18235
- and sidecar.annotations.tags by default; --tag-source author|user
18236
- narrows to one side).
18368
+ nodes carrying that tag in their \`.sm\` sidecar
18369
+ (\`annotations.tags\`).
18237
18370
 
18238
18371
  --sort-by accepts: path, kind, tokens_total, links_out_count,
18239
18372
  links_in_count, external_refs_count. Default: path. --limit N caps
@@ -18246,8 +18379,7 @@ var ListCommand = class extends SmCommand {
18246
18379
  ["List only agents", "$0 list --kind agent"],
18247
18380
  ["Top 5 by total tokens", "$0 list --sort-by tokens_total --limit 5"],
18248
18381
  ["Only nodes with issues, machine-readable", "$0 list --issue --json"],
18249
- ["Filter by tag (author or user surfaces)", "$0 list --tag urgent"],
18250
- ["Filter by user-only tag", "$0 list --tag wip --tag-source user"]
18382
+ ["Filter by tag", "$0 list --tag urgent"]
18251
18383
  ]
18252
18384
  });
18253
18385
  kind = Option19.String("--kind", { required: false });
@@ -18255,7 +18387,6 @@ var ListCommand = class extends SmCommand {
18255
18387
  sortBy = Option19.String("--sort-by", { required: false });
18256
18388
  limit = Option19.String("--limit", { required: false });
18257
18389
  tag = Option19.String("--tag", { required: false });
18258
- tagSource = Option19.String("--tag-source", { required: false });
18259
18390
  async run() {
18260
18391
  const stderrAnsi = this.ansiFor("stderr");
18261
18392
  const flags = this.#parseFlags(stderrAnsi);
@@ -18274,7 +18405,6 @@ var ListCommand = class extends SmCommand {
18274
18405
  * resolved values or a precomputed exit code (already printed
18275
18406
  * directed errors before returning).
18276
18407
  */
18277
- // eslint-disable-next-line complexity
18278
18408
  #parseFlags(stderrAnsi) {
18279
18409
  let sortColumn = "path";
18280
18410
  let sortDirection = "asc";
@@ -18301,30 +18431,7 @@ var ListCommand = class extends SmCommand {
18301
18431
  if (parsed === null) return { ok: false, exit: ExitCode.Error };
18302
18432
  limitValue = parsed;
18303
18433
  }
18304
- let tagSourceValue;
18305
- if (this.tagSource !== void 0) {
18306
- if (this.tag === void 0) {
18307
- this.printer.error(
18308
- tx(LIST_TEXTS.tagSourceWithoutTag, {
18309
- glyph: stderrAnsi.red("\u2715"),
18310
- hint: stderrAnsi.dim(LIST_TEXTS.tagSourceWithoutTagHint)
18311
- })
18312
- );
18313
- return { ok: false, exit: ExitCode.Error };
18314
- }
18315
- if (this.tagSource !== "author" && this.tagSource !== "user") {
18316
- this.printer.error(
18317
- tx(LIST_TEXTS.invalidTagSource, {
18318
- glyph: stderrAnsi.red("\u2715"),
18319
- value: this.tagSource,
18320
- hint: stderrAnsi.dim(LIST_TEXTS.invalidTagSourceHint)
18321
- })
18322
- );
18323
- return { ok: false, exit: ExitCode.Error };
18324
- }
18325
- tagSourceValue = this.tagSource;
18326
- }
18327
- return { ok: true, sortColumn, sortDirection, limitValue, tagSourceValue };
18434
+ return { ok: true, sortColumn, sortDirection, limitValue };
18328
18435
  }
18329
18436
  /**
18330
18437
  * Issue the DB queries: optional `--tag` allow-list narrowing, the
@@ -18332,7 +18439,7 @@ var ListCommand = class extends SmCommand {
18332
18439
  * out so `run()` reads as a thin orchestrator.
18333
18440
  */
18334
18441
  async #runQuery(adapter, flags) {
18335
- const tagAllowList = await this.#resolveTagAllowList(adapter, flags);
18442
+ const tagAllowList = await this.#resolveTagAllowList(adapter);
18336
18443
  if (tagAllowList === "no-match") return this.#renderEmpty();
18337
18444
  const filter = this.#buildFindNodesFilter(flags);
18338
18445
  const allMatchingNodes = await adapter.scans.findNodes(filter);
@@ -18348,16 +18455,15 @@ var ListCommand = class extends SmCommand {
18348
18455
  return ExitCode.Ok;
18349
18456
  }
18350
18457
  /**
18351
- * Resolve `--tag` (and the optional `--tag-source` filter) into a
18352
- * path allow-list. Returns:
18458
+ * Resolve `--tag` into a path allow-list. Returns:
18353
18459
  * - `null` when `--tag` was not supplied (no narrowing).
18354
18460
  * - `'no-match'` when the tag matched zero nodes (caller renders
18355
18461
  * the empty surface and exits).
18356
18462
  * - a Set of paths otherwise.
18357
18463
  */
18358
- async #resolveTagAllowList(adapter, flags) {
18464
+ async #resolveTagAllowList(adapter) {
18359
18465
  if (this.tag === void 0) return null;
18360
- const matchingPaths = await adapter.tags.findNodes(this.tag, flags.tagSourceValue);
18466
+ const matchingPaths = await adapter.tags.findNodes(this.tag);
18361
18467
  if (matchingPaths.length === 0) return "no-match";
18362
18468
  return new Set(matchingPaths);
18363
18469
  }
@@ -20447,7 +20553,7 @@ function resolveBareToggle(id, catalogue) {
20447
20553
  }
20448
20554
 
20449
20555
  // cli/commands/plugins/create.ts
20450
- import { existsSync as existsSync22, mkdirSync as mkdirSync5, writeFileSync } from "fs";
20556
+ import { existsSync as existsSync24, mkdirSync as mkdirSync5, writeFileSync } from "fs";
20451
20557
  import { join as join18, resolve as resolve33 } from "path";
20452
20558
  import { Command as Command26, Option as Option25 } from "clipanion";
20453
20559
  var PluginsCreateCommand = class extends SmCommand {
@@ -20476,7 +20582,7 @@ var PluginsCreateCommand = class extends SmCommand {
20476
20582
  const ctx = defaultRuntimeContext();
20477
20583
  const baseDir = defaultProjectPluginsDir(ctx);
20478
20584
  const targetDir = this.at ? resolve33(this.at) : join18(baseDir, this.pluginId);
20479
- if (existsSync22(targetDir) && !this.force) {
20585
+ if (existsSync24(targetDir) && !this.force) {
20480
20586
  this.printer.error(
20481
20587
  tx(PLUGINS_TEXTS.createRefuseOverwrite, {
20482
20588
  glyph: errGlyph,
@@ -21202,6 +21308,12 @@ var RUNTIME_TEXTS = {
21202
21308
 
21203
21309
  // core/watcher/runtime.ts
21204
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
+ }
21205
21317
  function createWatcherRuntime(opts) {
21206
21318
  const events = opts.events ?? {};
21207
21319
  const cwd = opts.runtimeContext.cwd;
@@ -21245,6 +21357,7 @@ function createWatcherRuntime(opts) {
21245
21357
  };
21246
21358
  let handleBatch = null;
21247
21359
  const start = async () => {
21360
+ await rebuildWatcherDbOnDrift(opts.dbPath, events);
21248
21361
  cfg = loadEffectiveConfig();
21249
21362
  ignoreFilter = composeIgnoreFilter(cfg, readIgnoreFileText(cwd));
21250
21363
  applyConfigDerivedState(cfg);
@@ -21600,6 +21713,16 @@ async function runWatchLoop(opts) {
21600
21713
  printer.warn(`${message}
21601
21714
  `);
21602
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
+ },
21603
21726
  onConfigLoaded: ({ debounceMs }) => {
21604
21727
  if (opts.json) return;
21605
21728
  context.stderr.write(
@@ -21821,7 +21944,7 @@ var ScanCommand = class extends SmCommand {
21821
21944
  description: "Long-running mode: watch the roots and trigger an incremental scan after each debounced batch of filesystem events. Alias of `sm watch`."
21822
21945
  });
21823
21946
  yes = Option29.Boolean("--yes", false, {
21824
- 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."
21825
21948
  });
21826
21949
  maxNodes = Option29.String("--max-nodes", {
21827
21950
  required: false,
@@ -22355,7 +22478,7 @@ function renderDeltaIssues(issues) {
22355
22478
 
22356
22479
  // cli/commands/serve.ts
22357
22480
  import { spawn as spawn2 } from "child_process";
22358
- import { existsSync as existsSync28 } from "fs";
22481
+ import { existsSync as existsSync30 } from "fs";
22359
22482
  import { Command as Command33, Option as Option31 } from "clipanion";
22360
22483
 
22361
22484
  // kernel/util/dev-mode.ts
@@ -22510,6 +22633,11 @@ var SERVER_TEXTS = {
22510
22633
  // watcher loop continues, a transient FS error must not kill the
22511
22634
  // broadcaster.
22512
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",
22513
22641
  // chokidar surfaced an error. The watcher stays open per IFsWatcher's
22514
22642
  // contract; the BFF also broadcasts a `watcher.error` advisory so the
22515
22643
  // SPA can surface it in the live event log.
@@ -23090,7 +23218,7 @@ function contentTypeFor(format) {
23090
23218
  }
23091
23219
 
23092
23220
  // server/health.ts
23093
- import { existsSync as existsSync23 } from "fs";
23221
+ import { existsSync as existsSync25 } from "fs";
23094
23222
  var FALLBACK_SCHEMA_VERSION = "1";
23095
23223
  function buildHealth(deps) {
23096
23224
  const dev = isDevBuild();
@@ -23099,7 +23227,7 @@ function buildHealth(deps) {
23099
23227
  schemaVersion: FALLBACK_SCHEMA_VERSION,
23100
23228
  specVersion: deps.specVersion,
23101
23229
  implVersion: VERSION,
23102
- db: existsSync23(deps.dbPath) ? "present" : "missing",
23230
+ db: existsSync25(deps.dbPath) ? "present" : "missing",
23103
23231
  cwd: deps.cwd,
23104
23232
  dbPath: deps.dbPath,
23105
23233
  // Only emit when truthy so a published install keeps the wire
@@ -23350,7 +23478,7 @@ function registerNodesRoutes(app, deps) {
23350
23478
  bundle: null,
23351
23479
  isFavorite: false,
23352
23480
  contributions: [],
23353
- tags: { byAuthor: [], byUser: [] }
23481
+ tags: []
23354
23482
  };
23355
23483
  }
23356
23484
  const favSet = await adapter.favorites.listPaths();
@@ -23360,14 +23488,14 @@ function registerNodesRoutes(app, deps) {
23360
23488
  bundle: b,
23361
23489
  isFavorite: favSet.has(b.node.path),
23362
23490
  contributions: contributions2,
23363
- tags: groupTagsBySource(tagRows)
23491
+ tags: tagRows.map((r) => r.tag)
23364
23492
  };
23365
23493
  }
23366
23494
  );
23367
23495
  const bundle = result?.bundle ?? null;
23368
23496
  const isFavorite = result?.isFavorite ?? false;
23369
23497
  const contributions = result?.contributions ?? [];
23370
- const tags = result?.tags ?? { byAuthor: [], byUser: [] };
23498
+ const tags = result?.tags ?? [];
23371
23499
  if (!bundle) {
23372
23500
  throw new HTTPException6(404, {
23373
23501
  message: tx(SERVER_TEXTS.nodeNotFound, { path: nodePath })
@@ -23421,7 +23549,7 @@ function registerNodesRoutes(app, deps) {
23421
23549
  const contribByPath = contributionsOmitted ? /* @__PURE__ */ new Map() : await groupContributionsByPath(
23422
23550
  await adapter.contributions.listForPaths(pagePaths)
23423
23551
  );
23424
- const tagByPath = await groupTagsByPath(
23552
+ const tagByPath = groupTagsByPath(
23425
23553
  await adapter.tags.listForPaths(pagePaths)
23426
23554
  );
23427
23555
  return { contributionsByPath: contribByPath, tagsByPath: tagByPath };
@@ -23434,7 +23562,7 @@ function registerNodesRoutes(app, deps) {
23434
23562
  ...n,
23435
23563
  isFavorite: favSet.has(n.path),
23436
23564
  contributions: contributionsByPath.get(n.path) ?? [],
23437
- tags: tagsByPath.get(n.path) ?? { byAuthor: [], byUser: [] }
23565
+ tags: tagsByPath.get(n.path) ?? []
23438
23566
  }));
23439
23567
  return c.json(
23440
23568
  buildListEnvelope({
@@ -23457,24 +23585,15 @@ function registerNodesRoutes(app, deps) {
23457
23585
  function parseIncludes(raw) {
23458
23586
  return new Set(parseCsv(raw));
23459
23587
  }
23460
- function groupTagsBySource(rows) {
23461
- const byAuthor = /* @__PURE__ */ new Set();
23462
- const byUser = /* @__PURE__ */ new Set();
23463
- for (const r of rows) (r.source === "author" ? byAuthor : byUser).add(r.tag);
23464
- return {
23465
- byAuthor: [...byAuthor].sort(),
23466
- byUser: [...byUser].sort()
23467
- };
23468
- }
23469
- async function groupTagsByPath(rows) {
23588
+ function groupTagsByPath(rows) {
23470
23589
  const buckets = /* @__PURE__ */ new Map();
23471
23590
  for (const r of rows) {
23472
- const list = buckets.get(r.nodePath);
23473
- if (list) list.push({ tag: r.tag, source: r.source });
23474
- 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]));
23475
23594
  }
23476
23595
  const out = /* @__PURE__ */ new Map();
23477
- for (const [path, entries] of buckets) out.set(path, groupTagsBySource(entries));
23596
+ for (const [path, set] of buckets) out.set(path, [...set].sort());
23478
23597
  return out;
23479
23598
  }
23480
23599
  async function groupContributionsByPath(rows) {
@@ -23981,12 +24100,12 @@ var parsePatchBody2 = makeBodyValidator(PATCH_BODY_SCHEMA, {
23981
24100
  import { HTTPException as HTTPException10 } from "hono/http-exception";
23982
24101
 
23983
24102
  // server/util/skillmapignore-io.ts
23984
- 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";
23985
24104
  import { resolve as resolve35 } from "path";
23986
24105
  var IGNORE_FILENAME2 = ".skillmapignore";
23987
24106
  function readPatterns(cwd) {
23988
24107
  const path = resolve35(cwd, IGNORE_FILENAME2);
23989
- if (!existsSync24(path)) return [];
24108
+ if (!existsSync26(path)) return [];
23990
24109
  let raw;
23991
24110
  try {
23992
24111
  raw = readFileSync17(path, "utf8");
@@ -23997,7 +24116,7 @@ function readPatterns(cwd) {
23997
24116
  }
23998
24117
  function writePatterns(cwd, nextPatterns) {
23999
24118
  const path = resolve35(cwd, IGNORE_FILENAME2);
24000
- const prior = existsSync24(path) ? safeRead(path) : "";
24119
+ const prior = existsSync26(path) ? safeRead(path) : "";
24001
24120
  const content = buildContent(prior, nextPatterns);
24002
24121
  writeFileSync2(path, content, "utf8");
24003
24122
  }
@@ -24347,7 +24466,7 @@ var parsePatchBody4 = makeBodyValidator(PATCH_BODY_SCHEMA3, {
24347
24466
  });
24348
24467
 
24349
24468
  // server/routes/active-provider.ts
24350
- import { existsSync as existsSync25 } from "fs";
24469
+ import { existsSync as existsSync27 } from "fs";
24351
24470
  import { HTTPException as HTTPException12 } from "hono/http-exception";
24352
24471
  function registerActiveProviderRoute(app, deps) {
24353
24472
  app.get("/api/active-provider", (c) => {
@@ -24380,7 +24499,7 @@ function applyLensSwitch(deps, newValue) {
24380
24499
  });
24381
24500
  }
24382
24501
  const dbPath = resolveDbPath({ db: void 0, cwd });
24383
- if (!existsSync25(dbPath)) return { dropped: null };
24502
+ if (!existsSync27(dbPath)) return { dropped: null };
24384
24503
  const dropResult = dropScanZone(dbPath);
24385
24504
  return {
24386
24505
  dropped: {
@@ -24501,6 +24620,14 @@ function createWatcherService(opts) {
24501
24620
  onPluginWarning: (message) => {
24502
24621
  log.warn(sanitizeForTerminal(message));
24503
24622
  },
24623
+ onDriftReset: (info) => {
24624
+ log.warn(
24625
+ tx(SERVER_TEXTS.watcherDriftReset, {
24626
+ dbVersion: info.dbVersion,
24627
+ currentVersion: info.currentVersion
24628
+ })
24629
+ );
24630
+ },
24504
24631
  onReady: (info) => {
24505
24632
  opts.broadcaster.broadcast(
24506
24633
  buildWatcherStartedEvent({ roots: info.roots, debounceMs: info.debounceMs })
@@ -24643,13 +24770,8 @@ async function loadPersistedScan(deps) {
24643
24770
  if (list) list.push(r);
24644
24771
  else byPath3.set(r.nodePath, [r]);
24645
24772
  }
24646
- const tagBuckets = /* @__PURE__ */ new Map();
24647
- for (const r of tagRows) {
24648
- const list = tagBuckets.get(r.nodePath);
24649
- if (list) list.push({ tag: r.tag, source: r.source });
24650
- else tagBuckets.set(r.nodePath, [{ tag: r.tag, source: r.source }]);
24651
- }
24652
- return { loaded, favSet, contribByPath: byPath3, tagBuckets };
24773
+ const tagsByPath = groupTagsByPath2(tagRows);
24774
+ return { loaded, favSet, contribByPath: byPath3, tagsByPath };
24653
24775
  }
24654
24776
  );
24655
24777
  if (opened === null) {
@@ -24661,18 +24783,20 @@ async function loadPersistedScan(deps) {
24661
24783
  ...n,
24662
24784
  isFavorite: opened.favSet.has(n.path),
24663
24785
  contributions: opened.contribByPath.get(n.path) ?? [],
24664
- tags: groupTagsBySource2(opened.tagBuckets.get(n.path) ?? [])
24786
+ tags: opened.tagsByPath.get(n.path) ?? []
24665
24787
  }))
24666
24788
  };
24667
24789
  }
24668
- function groupTagsBySource2(rows) {
24669
- const byAuthor = /* @__PURE__ */ new Set();
24670
- const byUser = /* @__PURE__ */ new Set();
24671
- for (const r of rows) (r.source === "author" ? byAuthor : byUser).add(r.tag);
24672
- return {
24673
- byAuthor: [...byAuthor].sort(),
24674
- byUser: [...byUser].sort()
24675
- };
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;
24676
24800
  }
24677
24801
  async function runFreshScan(deps) {
24678
24802
  if (deps.options.noBuiltIns || deps.options.noPlugins) {
@@ -24910,7 +25034,7 @@ function registerUpdateStatusRoute(app, deps) {
24910
25034
  }
24911
25035
 
24912
25036
  // server/static.ts
24913
- import { existsSync as existsSync26 } from "fs";
25037
+ import { existsSync as existsSync28 } from "fs";
24914
25038
  import { readFile as readFile6 } from "fs/promises";
24915
25039
  import { extname, join as join19 } from "path";
24916
25040
  import { serveStatic } from "@hono/node-server/serve-static";
@@ -24965,7 +25089,7 @@ function createSpaFallback(opts) {
24965
25089
  if (c.req.method !== "GET" && c.req.method !== "HEAD") return c.notFound();
24966
25090
  if (opts.uiDist === null) return htmlResponse(c, placeholder);
24967
25091
  const indexPath = join19(opts.uiDist, INDEX_HTML);
24968
- if (!existsSync26(indexPath)) return htmlResponse(c, placeholder);
25092
+ if (!existsSync28(indexPath)) return htmlResponse(c, placeholder);
24969
25093
  return fileResponse(c, indexPath);
24970
25094
  };
24971
25095
  }
@@ -25574,7 +25698,7 @@ function validateNoUi(noUi, uiDist) {
25574
25698
  }
25575
25699
 
25576
25700
  // server/paths.ts
25577
- import { existsSync as existsSync27, statSync as statSync10 } from "fs";
25701
+ import { existsSync as existsSync29, statSync as statSync10 } from "fs";
25578
25702
  import { dirname as dirname18, isAbsolute as isAbsolute11, join as join20, resolve as resolve37 } from "path";
25579
25703
  import { fileURLToPath as fileURLToPath6 } from "url";
25580
25704
  var DEFAULT_UI_REL = join20("ui", "dist", "ui", "browser");
@@ -25589,10 +25713,10 @@ function resolveExplicitUiDist(ctx, raw) {
25589
25713
  return isAbsolute11(raw) ? raw : resolve37(ctx.cwd, raw);
25590
25714
  }
25591
25715
  function isUiBundleDir(path) {
25592
- if (!existsSync27(path)) return false;
25716
+ if (!existsSync29(path)) return false;
25593
25717
  try {
25594
25718
  if (!statSync10(path).isDirectory()) return false;
25595
- return existsSync27(join20(path, INDEX_HTML2));
25719
+ return existsSync29(join20(path, INDEX_HTML2));
25596
25720
  } catch {
25597
25721
  return false;
25598
25722
  }
@@ -26147,7 +26271,7 @@ var ServeCommand = class extends SmCommand {
26147
26271
  return ExitCode.Error;
26148
26272
  }
26149
26273
  const dbPath = resolveDbPath({ db: this.db, ...runtimeCtx });
26150
- if (this.db !== void 0 && !existsSync28(dbPath)) {
26274
+ if (this.db !== void 0 && !existsSync30(dbPath)) {
26151
26275
  this.printer.info(
26152
26276
  tx(SERVE_TEXTS.dbNotFound, {
26153
26277
  glyph: errGlyph,
@@ -27412,7 +27536,7 @@ var STUB_COMMANDS = [
27412
27536
  ];
27413
27537
 
27414
27538
  // cli/commands/tutorial.ts
27415
- 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";
27416
27540
  import { dirname as dirname19, join as join21, resolve as resolve39 } from "path";
27417
27541
  import { fileURLToPath as fileURLToPath7 } from "url";
27418
27542
  import { Command as Command37, Option as Option35 } from "clipanion";
@@ -27511,7 +27635,7 @@ var TutorialCommand = class extends SmCommand {
27511
27635
  const spec = VARIANT_SPECS[variant];
27512
27636
  const targetDir = join21(ctx.cwd, ".claude", "skills", spec.slug);
27513
27637
  const targetDisplay = `.claude/skills/${spec.slug}/`;
27514
- if (existsSync29(targetDir) && !this.force) {
27638
+ if (existsSync31(targetDir) && !this.force) {
27515
27639
  this.printer.error(
27516
27640
  tx(TUTORIAL_TEXTS.alreadyExists, {
27517
27641
  glyph: errGlyph,
@@ -27594,7 +27718,7 @@ function resolveSkillSourceDir(variant) {
27594
27718
  resolve39(here, "../cli/tutorial", spec.slug)
27595
27719
  ];
27596
27720
  for (const candidate of candidates) {
27597
- if (existsSync29(candidate) && statSync11(candidate).isDirectory()) {
27721
+ if (existsSync31(candidate) && statSync11(candidate).isDirectory()) {
27598
27722
  cachedSourceDirs.set(variant, candidate);
27599
27723
  return candidate;
27600
27724
  }
@@ -27779,7 +27903,7 @@ function resolveBareInvocation(rawArgs) {
27779
27903
  if (first !== void 0 && first.startsWith("-") && !passthrough.has(first)) {
27780
27904
  const isSingleDashLong = !first.startsWith("--") && first.length > 2;
27781
27905
  if (isSingleDashLong) return null;
27782
- if (existsSync30(defaultProjectDbPath(defaultRuntimeContext()))) {
27906
+ if (existsSync32(defaultProjectDbPath(defaultRuntimeContext()))) {
27783
27907
  return ["serve", ...rawArgs];
27784
27908
  }
27785
27909
  return resolveBareDefault();
@@ -27788,7 +27912,7 @@ function resolveBareInvocation(rawArgs) {
27788
27912
  }
27789
27913
  function resolveBareDefault() {
27790
27914
  const ctx = defaultRuntimeContext();
27791
- if (existsSync30(defaultProjectDbPath(ctx))) {
27915
+ if (existsSync32(defaultProjectDbPath(ctx))) {
27792
27916
  return ["serve"];
27793
27917
  }
27794
27918
  const stderr = process.stderr;