@skill-map/cli 0.62.2 → 0.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/cli/example/.claude/agents/content-editor.md +22 -0
  2. package/dist/cli/example/.claude/agents/content-editor.sm +17 -0
  3. package/dist/cli/example/.claude/commands/init.md +9 -0
  4. package/dist/cli/example/.claude/commands/publish.md +15 -0
  5. package/dist/cli/example/.claude/commands/publish.sm +9 -0
  6. package/dist/cli/example/.claude/skills/check-links/SKILL.md +16 -0
  7. package/dist/cli/example/.claude/skills/check-links/SKILL.sm +9 -0
  8. package/dist/cli/example/.skillmapignore +30 -0
  9. package/dist/cli/example/AGENTS.md +10 -0
  10. package/dist/cli/example/AGENTS.sm +8 -0
  11. package/dist/cli/example/docs/DEPLOY.md +11 -0
  12. package/dist/cli/example/docs/STYLE.md +20 -0
  13. package/dist/cli/example/package.json +6 -0
  14. package/dist/cli/example/public/index.html +5 -0
  15. package/dist/cli/example/server.js +11 -0
  16. package/dist/cli/tutorial/sm-tutorial/SKILL.md +1 -1
  17. package/dist/cli/tutorial/sm-tutorial/references/_core.md +8 -8
  18. package/dist/cli/tutorial/sm-tutorial/references/part-authoring.md +22 -11
  19. package/dist/cli/tutorial/sm-tutorial/references/part-project-kickoff.md +2 -2
  20. package/dist/cli/tutorial/sm-tutorial/references/part-settings.md +2 -2
  21. package/dist/cli.js +1032 -807
  22. package/dist/index.js +20 -13
  23. package/dist/kernel/index.d.ts +10 -18
  24. package/dist/kernel/index.js +20 -13
  25. package/dist/ui/chunk-53FVREFR.js +3 -0
  26. package/dist/ui/{chunk-ECKRC6XD.js → chunk-JEWVC3KW.js} +1 -1
  27. package/dist/ui/chunk-PEBQMYAG.js +1 -0
  28. package/dist/ui/{chunk-CM4YB7L4.js → chunk-SR2EXRNN.js} +1 -1
  29. package/dist/ui/index.html +1 -1
  30. package/dist/ui/{main-FUEU25PM.js → main-VSGHRLCJ.js} +4 -4
  31. package/package.json +2 -2
  32. package/dist/ui/chunk-NC3HOVDG.js +0 -1
  33. package/dist/ui/chunk-RXGORKP5.js +0 -3
  34. /package/dist/ui/{chunk-IYC5ZW4L.js → chunk-WDCUTF3U.js} +0 -0
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // cli/entry.ts
2
2
 
3
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="454166ed-eb8e-57bb-b412-c9c6e9ff30f4")}catch(e){}}();
4
- import { existsSync as existsSync33 } from "fs";
3
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="43bb2367-1e14-5df2-9221-a5e7a877a666")}catch(e){}}();
4
+ import { existsSync as existsSync34 } from "fs";
5
5
  import { Builtins, Cli as Cli2 } from "clipanion";
6
6
 
7
7
  // kernel/adapters/in-memory-progress.ts
@@ -250,7 +250,7 @@ function bucketByKind(kind, instance, bag) {
250
250
  // package.json
251
251
  var package_default = {
252
252
  name: "@skill-map/cli",
253
- version: "0.62.2",
253
+ version: "0.64.0",
254
254
  description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
255
255
  license: "MIT",
256
256
  type: "module",
@@ -1068,6 +1068,108 @@ function buildTooltip(names) {
1068
1068
  return `${joined.slice(0, TOOLTIP_MAX - 1)}\u2026`;
1069
1069
  }
1070
1070
 
1071
+ // plugins/agent-skills/providers/agent-skills/schemas/skill.schema.json
1072
+ var skill_schema_default2 = {
1073
+ $schema: "https://json-schema.org/draft/2020-12/schema",
1074
+ $id: "https://skill-map.ai/providers/agent-skills/v1/frontmatter/skill.schema.json",
1075
+ title: "FrontmatterAgentSkillsSkill",
1076
+ 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.",
1077
+ allOf: [
1078
+ { $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
1079
+ ],
1080
+ type: "object",
1081
+ additionalProperties: true,
1082
+ properties: {
1083
+ license: {
1084
+ type: "string",
1085
+ 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."
1086
+ },
1087
+ compatibility: {
1088
+ type: "string",
1089
+ maxLength: 500,
1090
+ description: "Environment requirements (intended product, required system packages, network access, etc.). Most skills omit it. Max 500 characters. Source: https://agentskills.io/specification."
1091
+ },
1092
+ metadata: {
1093
+ type: "object",
1094
+ additionalProperties: { type: "string" },
1095
+ description: "Arbitrary string-keyed, string-valued map for client-defined metadata not covered by the standard. Source: https://agentskills.io/specification."
1096
+ },
1097
+ "allowed-tools": {
1098
+ type: "string",
1099
+ 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."
1100
+ }
1101
+ }
1102
+ };
1103
+
1104
+ // plugins/agent-skills/providers/agent-skills/index.ts
1105
+ var OPEN_SKILLS_READ = {
1106
+ extensions: [".md"],
1107
+ parser: "frontmatter-yaml"
1108
+ };
1109
+ var OPEN_SKILLS_KINDS = {
1110
+ skill: {
1111
+ schema: "./schemas/skill.schema.json",
1112
+ schemaJson: skill_schema_default2,
1113
+ ui: {
1114
+ label: "Skills",
1115
+ color: "#10b981",
1116
+ colorDark: "#34d399",
1117
+ icon: { kind: "pi", id: "pi-bolt" }
1118
+ },
1119
+ // Open-standard skills mirror Anthropic's: dirname between
1120
+ // `.agents/skills/` and `/SKILL.md` is the canonical handle,
1121
+ // `frontmatter.name` overrides when present.
1122
+ identifiers: ["frontmatter.name", "dirname"]
1123
+ }
1124
+ };
1125
+ var OPEN_SKILLS_RESOLUTION = { invokes: ["skill"] };
1126
+ function classifyOpenSkillsPath(path) {
1127
+ if (/^\.agents\/skills\/[^/]+\/skill\.md$/.test(path.toLowerCase())) return "skill";
1128
+ return null;
1129
+ }
1130
+ var agentSkillsProvider = {
1131
+ id: "agent-skills",
1132
+ pluginId: AGENT_SKILLS_PLUGIN_ID,
1133
+ kind: "provider",
1134
+ description: "Classifies files under `.agents/skills/<name>/SKILL.md` as Agent Skills.",
1135
+ // Provider identity for the active-lens dropdown, the topbar lens chip,
1136
+ // and the per-node provider chip. Neutral slate (this is the
1137
+ // vendor-agnostic open-standard Provider, not a brand). Verbatim from
1138
+ // the previous static UI catalog (`ui/src/services/provider-ui.ts`).
1139
+ presentation: {
1140
+ label: "Open Skills",
1141
+ color: "#64748b",
1142
+ colorDark: "#94a3b8"
1143
+ },
1144
+ // Gated like the vendor providers: `.agents/skills/*` is classified as
1145
+ // `skill` ONLY under the `agent-skills` lens; under any other lens
1146
+ // (including `markdown`) it falls through to `core/markdown`, the sole
1147
+ // universal provider. Keeps the "one active lens" model honest.
1148
+ gatedByActiveLens: true,
1149
+ // Not yet ready for end users: ships disabled by default. The operator
1150
+ // opts in via `sm plugins enable` / the Settings toggle / the tutorial's
1151
+ // `--experimental` flow, so it neither classifies nor auto-detects until
1152
+ // enabled.
1153
+ stability: "experimental",
1154
+ // Auto-detect marker: a `.agents/` directory marks an open-standard
1155
+ // project. This is also the marker a Google/Antigravity project carries
1156
+ // (Antigravity adopted the open standard). The marker only produces an
1157
+ // auto-detect candidate once this experimental provider is enabled.
1158
+ // Provider-owned.
1159
+ detect: { markers: [".agents"] },
1160
+ // Authoring target for `sm tutorial`: the open standard discovers skills
1161
+ // under `.agents/skills/<name>/SKILL.md`. The same path is consumed by
1162
+ // Antigravity (adopted the standard rather than a `.gemini/` layout) and
1163
+ // OpenAI Codex (skills mirror the open standard), so `aka` surfaces both
1164
+ // names in the destination prompt to orient testers on those agents.
1165
+ // `aka` is display-only, `--for` still matches the `agent-skills` id.
1166
+ scaffold: { skillDir: ".agents/skills", aka: ["Antigravity", "OpenAI Codex"] },
1167
+ read: OPEN_SKILLS_READ,
1168
+ kinds: OPEN_SKILLS_KINDS,
1169
+ resolution: OPEN_SKILLS_RESOLUTION,
1170
+ classify: classifyOpenSkillsPath
1171
+ };
1172
+
1071
1173
  // plugins/antigravity/providers/antigravity/index.ts
1072
1174
  var antigravityProvider = {
1073
1175
  id: "antigravity",
@@ -1080,10 +1182,7 @@ var antigravityProvider = {
1080
1182
  presentation: {
1081
1183
  label: "Antigravity",
1082
1184
  color: "#7c3aed",
1083
- colorDark: "#a78bfa",
1084
- // Registered but not yet selectable as the active lens; the UI greys
1085
- // it with a `(coming soon)` suffix.
1086
- comingSoon: true
1185
+ colorDark: "#a78bfa"
1087
1186
  },
1088
1187
  // No `detect` block: Antigravity has no vendor-specific workspace marker
1089
1188
  // (it adopted the open-standard `.agents/`, owned by `agent-skills`), so
@@ -1095,17 +1194,23 @@ var antigravityProvider = {
1095
1194
  // declaration anticipates the migration moment so we don't have to
1096
1195
  // remember to flip it then.
1097
1196
  gatedByActiveLens: true,
1098
- // No `read` config: this Provider does not walk the filesystem. The
1099
- // kernel walker only fires for Providers with `read` or `walk`; an
1100
- // empty Provider participates in registration (its `ui` block is
1101
- // available, its `reservedNames` catalog is loaded) without owning
1102
- // any on-disk territory.
1103
- kinds: {},
1104
- // Always disclaim: paths are owned by other Providers (`.agents/` ->
1105
- // `agent-skills`, `AGENTS.md` -> `core/markdown` fallback).
1106
- classify() {
1107
- return null;
1108
- },
1197
+ // Not yet ready for end users: ships disabled by default (the operator
1198
+ // opts in via `sm plugins enable` / Settings / the tutorial's
1199
+ // `--experimental` flow). Replaces the retired `comingSoon` flag.
1200
+ stability: "experimental",
1201
+ // Adopt the open-standard `.agents/skills/` layout by REUSING the
1202
+ // `agent-skills` classifier + kind + read config (composition at the
1203
+ // manifest level, not a kernel rule). Under the antigravity lens the
1204
+ // walker classifies `.agents/skills/<name>/SKILL.md` as
1205
+ // `{ provider: 'antigravity', kind: 'skill' }`, so the reservedNames
1206
+ // below apply via SELF scope. `agent-skills` itself is gated to its own
1207
+ // lens, so it never competes here (under the antigravity lens it does
1208
+ // not participate). This is why there is no cross-provider lens-scope
1209
+ // rule in the kernel any more.
1210
+ read: OPEN_SKILLS_READ,
1211
+ kinds: OPEN_SKILLS_KINDS,
1212
+ resolution: OPEN_SKILLS_RESOLUTION,
1213
+ classify: classifyOpenSkillsPath,
1109
1214
  // Built-in slash-command catalog, captured verbatim from `agy /help`
1110
1215
  // (Antigravity CLI v1.0.3). This REPLACES the earlier provisional list
1111
1216
  // that mirrored Gemini CLI's verbs: `agy` ships its own surface. It
@@ -1123,12 +1228,10 @@ var antigravityProvider = {
1123
1228
  //
1124
1229
  // Declared under the `skill` kind (NOT `command`): Antigravity has no
1125
1230
  // vendor-specific command directory, its user slash-commands are skills
1126
- // (`.agents/skills/<name>/SKILL.md`, owned by the universal `agent-skills`
1127
- // Provider). The catalog is ACTIVE via the LENS SCOPE in
1128
- // `buildReservedNodePaths` (spec/architecture.md §Provider ·
1129
- // reservedNames): when `activeProvider === 'antigravity'` the orchestrator
1130
- // lends this `skill` catalog to `agent-skills` skill nodes, so a user
1131
- // `.agents/skills/goal/SKILL.md` is flagged because `/goal` is built-in.
1231
+ // (`.agents/skills/<name>/SKILL.md`). Because the antigravity lens now
1232
+ // classifies those files itself (inherited classifier above), a user
1233
+ // `.agents/skills/goal/SKILL.md` is flagged by SELF scope because `/goal`
1234
+ // is built-in.
1132
1235
  //
1133
1236
  // **Reconciliation marker**: re-capture from `agy /help` on each major
1134
1237
  // Antigravity CLI release and bump the cited version above.
@@ -1245,10 +1348,7 @@ var openaiProvider = {
1245
1348
  presentation: {
1246
1349
  label: "OpenAI Codex",
1247
1350
  color: "#22c55e",
1248
- colorDark: "#4ade80",
1249
- // Registered but not yet selectable as the active lens; auto-detect
1250
- // skips its markers and the UI greys it with a `(coming soon)` suffix.
1251
- comingSoon: true
1351
+ colorDark: "#4ade80"
1252
1352
  },
1253
1353
  // Auto-detect markers: a `.codex/` directory or a root `AGENTS.md` marks
1254
1354
  // a Codex CLI project. Provider-owned (replaces the old central
@@ -1259,6 +1359,10 @@ var openaiProvider = {
1259
1359
  // claiming Codex agents under a `claude` (or any other) lens, where
1260
1360
  // the Codex runtime would never resolve them anyway.
1261
1361
  gatedByActiveLens: true,
1362
+ // Not yet ready for end users: ships disabled by default (the operator
1363
+ // opts in via `sm plugins enable` / Settings / the tutorial's
1364
+ // `--experimental` flow). Replaces the retired `comingSoon` flag.
1365
+ stability: "experimental",
1262
1366
  read: { extensions: [".toml"], parser: "toml" },
1263
1367
  kinds: {
1264
1368
  agent: {
@@ -1291,98 +1395,6 @@ var openaiProvider = {
1291
1395
  }
1292
1396
  };
1293
1397
 
1294
- // plugins/agent-skills/providers/agent-skills/schemas/skill.schema.json
1295
- var skill_schema_default2 = {
1296
- $schema: "https://json-schema.org/draft/2020-12/schema",
1297
- $id: "https://skill-map.ai/providers/agent-skills/v1/frontmatter/skill.schema.json",
1298
- title: "FrontmatterAgentSkillsSkill",
1299
- 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.",
1300
- allOf: [
1301
- { $ref: "https://skill-map.ai/spec/v0/frontmatter/base.schema.json" }
1302
- ],
1303
- type: "object",
1304
- additionalProperties: true,
1305
- properties: {
1306
- license: {
1307
- type: "string",
1308
- 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."
1309
- },
1310
- compatibility: {
1311
- type: "string",
1312
- maxLength: 500,
1313
- description: "Environment requirements (intended product, required system packages, network access, etc.). Most skills omit it. Max 500 characters. Source: https://agentskills.io/specification."
1314
- },
1315
- metadata: {
1316
- type: "object",
1317
- additionalProperties: { type: "string" },
1318
- description: "Arbitrary string-keyed, string-valued map for client-defined metadata not covered by the standard. Source: https://agentskills.io/specification."
1319
- },
1320
- "allowed-tools": {
1321
- type: "string",
1322
- 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."
1323
- }
1324
- }
1325
- };
1326
-
1327
- // plugins/agent-skills/providers/agent-skills/index.ts
1328
- var agentSkillsProvider = {
1329
- id: "agent-skills",
1330
- pluginId: AGENT_SKILLS_PLUGIN_ID,
1331
- kind: "provider",
1332
- description: "Classifies files under `.agents/skills/<name>/SKILL.md` as Agent Skills.",
1333
- // Provider identity for the active-lens dropdown, the topbar lens chip,
1334
- // and the per-node provider chip. Neutral slate (this is the
1335
- // vendor-agnostic open-standard Provider, not a brand). Verbatim from
1336
- // the previous static UI catalog (`ui/src/services/provider-ui.ts`).
1337
- presentation: {
1338
- label: "Open Skills",
1339
- color: "#64748b",
1340
- colorDark: "#94a3b8",
1341
- // Registered but not yet selectable as the active lens; auto-detect
1342
- // skips its `.agents/` marker and the UI greys it with a
1343
- // `(coming soon)` suffix.
1344
- comingSoon: true
1345
- },
1346
- // Auto-detect marker: a `.agents/` directory marks an open-standard
1347
- // project. This is also the marker a Google/Antigravity project carries
1348
- // (Antigravity adopted the open standard), so such projects auto-detect
1349
- // as this universal lens. Provider-owned.
1350
- detect: { markers: [".agents"] },
1351
- // Authoring target for `sm tutorial`: the open standard discovers skills
1352
- // under `.agents/skills/<name>/SKILL.md`. The same path is consumed by
1353
- // Antigravity (adopted the standard rather than a `.gemini/` layout) and
1354
- // OpenAI Codex (skills mirror the open standard), so `aka` surfaces both
1355
- // names in the destination prompt to orient testers on those agents.
1356
- // `aka` is display-only, `--for` still matches the `agent-skills` id.
1357
- scaffold: { skillDir: ".agents/skills", aka: ["Antigravity", "OpenAI Codex"] },
1358
- read: { extensions: [".md"], parser: "frontmatter-yaml" },
1359
- kinds: {
1360
- skill: {
1361
- schema: "./schemas/skill.schema.json",
1362
- schemaJson: skill_schema_default2,
1363
- ui: {
1364
- label: "Skills",
1365
- color: "#10b981",
1366
- colorDark: "#34d399",
1367
- icon: { kind: "pi", id: "pi-bolt" }
1368
- },
1369
- // Open-standard skills mirror Anthropic's: dirname between
1370
- // `.agents/skills/` and `/SKILL.md` is the canonical handle,
1371
- // `frontmatter.name` overrides when present.
1372
- identifiers: ["frontmatter.name", "dirname"]
1373
- }
1374
- },
1375
- // The open standard documents slash-style invocation of skills; no
1376
- // mention surface (no agents in this Provider's territory).
1377
- resolution: {
1378
- invokes: ["skill"]
1379
- },
1380
- classify(path) {
1381
- if (/^\.agents\/skills\/[^/]+\/skill\.md$/.test(path.toLowerCase())) return "skill";
1382
- return null;
1383
- }
1384
- };
1385
-
1386
1398
  // plugins/core/providers/core-markdown/schemas/markdown.schema.json
1387
1399
  var markdown_schema_default = {
1388
1400
  $schema: "https://json-schema.org/draft/2020-12/schema",
@@ -4548,6 +4560,25 @@ var ENTRY_TEXTS = {
4548
4560
  */
4549
4561
  bareNoProject: "{{glyph}} No skill-map project found in {{cwd}}.\n {{hint}}\n",
4550
4562
  bareNoProjectHint: "Run `sm init` to bootstrap one, or `sm --help` to see all commands.",
4563
+ /**
4564
+ * Hint variant for a bare `sm` in an EMPTY cwd when the interactive
4565
+ * menu cannot run (non-TTY stdin) or the operator gave no valid pick.
4566
+ * Points at the two getting-started verbs instead of `sm init` (a
4567
+ * brand-new user in an empty folder wants to try the tool, not
4568
+ * bootstrap an empty project).
4569
+ */
4570
+ bareEmptyHint: "Run `sm tutorial` for a guided walkthrough, or `sm example` to drop a project to explore.",
4571
+ /**
4572
+ * Getting-started menu shown on bare `sm` in an empty folder on an
4573
+ * interactive terminal. Header uses a yellow `?` glyph; two numbered
4574
+ * options dispatch to `sm tutorial` / `sm example`; the input line
4575
+ * accepts a number, a verb name, or an empty answer (which takes the
4576
+ * default, option 1, the tutorial).
4577
+ */
4578
+ emptyMenuHeader: "{{glyph}} This folder is empty. How would you like to start?",
4579
+ emptyMenuOptionTutorial: " 1) Run the guided tutorial (sm tutorial) (default)",
4580
+ emptyMenuOptionExample: " 2) Copy an example project to try (sm example)",
4581
+ emptyMenuInput: " Enter the number [default 1]: ",
4551
4582
  parseErrorHeadline: "sm: {{message}}",
4552
4583
  parseErrorUnknownOption: "unknown option '{{name}}'",
4553
4584
  parseErrorUnknownOptionForVerb: "{{verb}}: unknown option '{{name}}'",
@@ -4780,6 +4811,64 @@ function requireDbOrExit(path, stderr) {
4780
4811
  return ExitCode.NotFound;
4781
4812
  }
4782
4813
 
4814
+ // cli/util/empty-cwd.ts
4815
+ import { readdirSync } from "fs";
4816
+ function isDirEmpty(dir) {
4817
+ return readdirSync(dir).length === 0;
4818
+ }
4819
+ function listCwdEntries(dir) {
4820
+ const entries = readdirSync(dir).sort();
4821
+ const shown = entries.slice(0, 5);
4822
+ const more = entries.length > shown.length ? ", ..." : "";
4823
+ return shown.join(", ") + more;
4824
+ }
4825
+ function displayCwd(cwd) {
4826
+ const segments = cwd.split("/").filter((s) => s.length > 0);
4827
+ if (segments.length === 0) return "./";
4828
+ return `./${segments[segments.length - 1]}/`;
4829
+ }
4830
+
4831
+ // cli/util/empty-folder-prompt.ts
4832
+ import { createInterface } from "readline";
4833
+ async function decideBareNoArgs(state, prompt) {
4834
+ if (state.hasDb) return { kind: "route", argv: ["serve"] };
4835
+ if (state.isTty && state.isEmptyDir) {
4836
+ const choice = await prompt();
4837
+ if (choice !== null) return { kind: "route", argv: [choice] };
4838
+ }
4839
+ return { kind: "hint" };
4840
+ }
4841
+ function classifyEmptyFolderAnswer(trimmed) {
4842
+ if (trimmed === "") return "tutorial";
4843
+ if (trimmed === "1") return "tutorial";
4844
+ if (trimmed === "2") return "example";
4845
+ const lower = trimmed.toLowerCase();
4846
+ if (lower === "tutorial") return "tutorial";
4847
+ if (lower === "example") return "example";
4848
+ return null;
4849
+ }
4850
+ async function promptEmptyFolderChoice(stdin, stderr, ansi) {
4851
+ const menu = [
4852
+ tx(ENTRY_TEXTS.emptyMenuHeader, { glyph: ansi.yellow("?") }),
4853
+ ENTRY_TEXTS.emptyMenuOptionTutorial,
4854
+ ENTRY_TEXTS.emptyMenuOptionExample
4855
+ ].join("\n");
4856
+ stderr.write(menu + "\n");
4857
+ const rl = createInterface({ input: stdin, output: stderr });
4858
+ try {
4859
+ for (let attempt = 0; attempt < 5; attempt += 1) {
4860
+ const answer = await new Promise(
4861
+ (resolveP) => rl.question(ENTRY_TEXTS.emptyMenuInput, resolveP)
4862
+ );
4863
+ const choice = classifyEmptyFolderAnswer(answer.trim());
4864
+ if (choice !== null) return choice;
4865
+ }
4866
+ return null;
4867
+ } finally {
4868
+ rl.close();
4869
+ }
4870
+ }
4871
+
4783
4872
  // cli/util/edit-distance.ts
4784
4873
  function editDistance(a, b, max) {
4785
4874
  if (a === b) return 0;
@@ -4955,7 +5044,7 @@ function defaultRuntimeContext() {
4955
5044
  }
4956
5045
 
4957
5046
  // cli/telemetry/first-run-prompt.ts
4958
- import { createInterface } from "readline/promises";
5047
+ import { createInterface as createInterface2 } from "readline/promises";
4959
5048
 
4960
5049
  // cli/i18n/telemetry.texts.ts
4961
5050
  var TELEMETRY_PROMPT_TEXTS = {
@@ -5258,7 +5347,7 @@ async function runConsentPrompt(stdin, stdout, nowMs) {
5258
5347
  const rendered = renderConsent(
5259
5348
  ansiFor({ isTTY: stdout.isTTY === true, noColorFlag: false })
5260
5349
  );
5261
- const rl = createInterface({ input: stdin, output: stdout });
5350
+ const rl = createInterface2({ input: stdin, output: stdout });
5262
5351
  try {
5263
5352
  const consented = await readConsentDecision(rl, stdout, rendered);
5264
5353
  writeUserSettings({
@@ -5988,13 +6077,13 @@ var CONSENT_TEXTS = {
5988
6077
  };
5989
6078
 
5990
6079
  // cli/util/confirm.ts
5991
- import { createInterface as createInterface2 } from "readline";
6080
+ import { createInterface as createInterface3 } from "readline";
5992
6081
  var YES_PATTERN = new RegExp(UTIL_TEXTS.confirmYesPatternSource, "i");
5993
6082
  var NO_PATTERN = new RegExp(UTIL_TEXTS.confirmNoPatternSource, "i");
5994
6083
  async function confirm(question, streams, opts) {
5995
6084
  const defaultAnswer = opts?.defaultAnswer ?? "no";
5996
6085
  const suffix = defaultAnswer === "yes" ? UTIL_TEXTS.confirmPromptSuffixDefaultYes : UTIL_TEXTS.confirmPromptSuffix;
5997
- const rl = createInterface2({ input: streams.stdin, output: streams.stderr });
6086
+ const rl = createInterface3({ input: streams.stdin, output: streams.stderr });
5998
6087
  try {
5999
6088
  const answer = await new Promise(
6000
6089
  (resolveP) => rl.question(`${question}${suffix}`, resolveP)
@@ -6348,7 +6437,7 @@ var AsyncMutex = class {
6348
6437
  this.#locked = true;
6349
6438
  return;
6350
6439
  }
6351
- await new Promise((resolve43) => this.#waiters.push(resolve43));
6440
+ await new Promise((resolve44) => this.#waiters.push(resolve44));
6352
6441
  this.#locked = true;
6353
6442
  }
6354
6443
  unlock() {
@@ -6795,7 +6884,7 @@ async function selectReferencedJobFilePaths(db) {
6795
6884
  }
6796
6885
 
6797
6886
  // kernel/adapters/sqlite/migrations.ts
6798
- import { copyFileSync, existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync8, readdirSync } from "fs";
6887
+ import { copyFileSync, existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync8, readdirSync as readdirSync2 } from "fs";
6799
6888
  import { dirname as dirname7, join as join5, resolve as resolve10 } from "path";
6800
6889
  import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
6801
6890
  import { fileURLToPath } from "url";
@@ -6817,7 +6906,7 @@ function defaultMigrationsDir() {
6817
6906
  }
6818
6907
  function discoverMigrations(dir = defaultMigrationsDir()) {
6819
6908
  if (!existsSync8(dir)) return [];
6820
- const files = readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).filter((name) => FILE_RE.test(name)).sort();
6909
+ const files = readdirSync2(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).filter((name) => FILE_RE.test(name)).sort();
6821
6910
  const out = [];
6822
6911
  for (const name of files) {
6823
6912
  const match = FILE_RE.exec(name);
@@ -6932,7 +7021,7 @@ function writeBackup(dbPath, destPath) {
6932
7021
  }
6933
7022
 
6934
7023
  // kernel/adapters/sqlite/plugin-migrations.ts
6935
- import { existsSync as existsSync9, readFileSync as readFileSync9, readdirSync as readdirSync2 } from "fs";
7024
+ import { existsSync as existsSync9, readFileSync as readFileSync9, readdirSync as readdirSync3 } from "fs";
6936
7025
  import { join as join6 } from "path";
6937
7026
 
6938
7027
  // kernel/adapters/sqlite/plugin-migrations-validator.ts
@@ -7252,7 +7341,7 @@ function resolvePluginMigrationsDir(plugin) {
7252
7341
  function discoverPluginMigrations(plugin) {
7253
7342
  const dir = resolvePluginMigrationsDir(plugin);
7254
7343
  if (!dir) return [];
7255
- const files = readdirSync2(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).filter((name) => FILE_RE2.test(name)).sort();
7344
+ const files = readdirSync3(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).filter((name) => FILE_RE2.test(name)).sort();
7256
7345
  const out = [];
7257
7346
  for (const name of files) {
7258
7347
  const match = FILE_RE2.exec(name);
@@ -9851,7 +9940,7 @@ var PLUGIN_LOADER_TEXTS = {
9851
9940
 
9852
9941
  // kernel/adapters/plugin-loader/index.ts
9853
9942
  import { createRequire as createRequire5 } from "module";
9854
- import { existsSync as existsSync13, readFileSync as readFileSync13, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
9943
+ import { existsSync as existsSync13, readFileSync as readFileSync13, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
9855
9944
  import { join as join8, resolve as resolve16 } from "path";
9856
9945
  import { pathToFileURL } from "url";
9857
9946
  import semver from "semver";
@@ -10194,9 +10283,9 @@ function providerKindFailure(opts, status, fileName, errDescription) {
10194
10283
  }
10195
10284
  };
10196
10285
  }
10197
- function isDirectorySafe(path, statSync12) {
10286
+ function isDirectorySafe(path, statSync13) {
10198
10287
  try {
10199
- return statSync12(path).isDirectory();
10288
+ return statSync13(path).isDirectory();
10200
10289
  } catch {
10201
10290
  return false;
10202
10291
  }
@@ -10306,7 +10395,7 @@ var PluginLoader = class {
10306
10395
  const out = [];
10307
10396
  for (const root of this.#options.searchPaths) {
10308
10397
  if (!existsSync13(root)) continue;
10309
- for (const entry of readdirSync4(root, { withFileTypes: true })) {
10398
+ for (const entry of readdirSync5(root, { withFileTypes: true })) {
10310
10399
  if (!entry.isDirectory()) continue;
10311
10400
  const candidate = join8(root, entry.name);
10312
10401
  if (existsSync13(join8(candidate, "plugin.json"))) {
@@ -10677,7 +10766,7 @@ function collectKindEntries(pluginPath, kindDir, out) {
10677
10766
  if (!existsSync13(kindAbs)) return;
10678
10767
  let entries;
10679
10768
  try {
10680
- entries = readdirSync4(kindAbs);
10769
+ entries = readdirSync5(kindAbs);
10681
10770
  } catch {
10682
10771
  return;
10683
10772
  }
@@ -11826,13 +11915,14 @@ function detectProvidersFromFilesystem(cwd, providers) {
11826
11915
  return out;
11827
11916
  }
11828
11917
  function isDetectableUnderCwd(cwd, provider) {
11829
- if (provider.presentation?.comingSoon === true) return false;
11918
+ if (!installedDefaultEnabled(provider.stability)) return false;
11830
11919
  const markers = provider.detect?.markers;
11831
11920
  if (!markers || markers.length === 0) return false;
11832
11921
  return markers.some((marker) => existsSync15(join10(cwd, marker)));
11833
11922
  }
11834
11923
 
11835
11924
  // core/config/active-provider.ts
11925
+ var MARKDOWN_LENS_ID = "markdown";
11836
11926
  function resolveActiveProvider(cwd, providers = []) {
11837
11927
  const detected = detectProvidersFromFilesystem(cwd, providers);
11838
11928
  const fromConfig = readConfigValue("activeProvider", { cwd });
@@ -11842,7 +11932,7 @@ function resolveActiveProvider(cwd, providers = []) {
11842
11932
  if (detected.length > 0) {
11843
11933
  return { resolved: detected[0], source: "autodetect", detected };
11844
11934
  }
11845
- return { resolved: null, source: "none", detected };
11935
+ return { resolved: MARKDOWN_LENS_ID, source: "default", detected };
11846
11936
  }
11847
11937
 
11848
11938
  // cli/util/path-display.ts
@@ -12580,7 +12670,7 @@ import { Command as Command5, Option as Option5 } from "clipanion";
12580
12670
 
12581
12671
  // conformance/index.ts
12582
12672
  import { spawnSync as spawnSync2 } from "child_process";
12583
- import { cpSync, existsSync as existsSync17, mkdtempSync, readdirSync as readdirSync5, readFileSync as readFileSync15, rmSync, statSync as statSync3 } from "fs";
12673
+ import { cpSync, existsSync as existsSync17, mkdtempSync, readdirSync as readdirSync6, readFileSync as readFileSync15, rmSync, statSync as statSync3 } from "fs";
12584
12674
  import { tmpdir } from "os";
12585
12675
  import { isAbsolute as isAbsolute6, join as join11, relative as relative3, resolve as resolve20 } from "path";
12586
12676
 
@@ -12736,7 +12826,7 @@ function runPriorScansSetup(c, options, scope, fixturesRoot, setupEnv) {
12736
12826
  }
12737
12827
  function replaceFixture(scope, fixturesRoot, fixture) {
12738
12828
  assertContained2(fixturesRoot, fixture, "fixture");
12739
- for (const entry of readdirSync5(scope)) {
12829
+ for (const entry of readdirSync6(scope)) {
12740
12830
  if (entry === KERNEL_SKILL_MAP_DIR) continue;
12741
12831
  rmSync(join11(scope, entry), { recursive: true, force: true });
12742
12832
  }
@@ -12973,7 +13063,7 @@ var CONFORMANCE_TEXTS = {
12973
13063
  };
12974
13064
 
12975
13065
  // cli/util/conformance-scopes.ts
12976
- import { existsSync as existsSync18, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
13066
+ import { existsSync as existsSync18, readdirSync as readdirSync7, statSync as statSync4 } from "fs";
12977
13067
  import { dirname as dirname11, resolve as resolve21 } from "path";
12978
13068
  import { createRequire as createRequire6 } from "module";
12979
13069
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -13014,7 +13104,7 @@ function collectProviderScopes(specRoot) {
13014
13104
  }
13015
13105
  const pluginsRoot = resolve21(workspaceRoot, "plugins");
13016
13106
  if (!existsSync18(pluginsRoot)) return out;
13017
- for (const pluginEntry of readdirSync6(pluginsRoot)) {
13107
+ for (const pluginEntry of readdirSync7(pluginsRoot)) {
13018
13108
  const pluginDir = resolve21(pluginsRoot, pluginEntry);
13019
13109
  if (!isDir(pluginDir)) continue;
13020
13110
  const providersRoot = resolve21(pluginDir, "providers");
@@ -13031,7 +13121,7 @@ function isDir(path) {
13031
13121
  }
13032
13122
  }
13033
13123
  function collectPluginProviderScopes(providersRoot, specRoot, out) {
13034
- for (const entry of readdirSync6(providersRoot)) {
13124
+ for (const entry of readdirSync7(providersRoot)) {
13035
13125
  const providerDir = resolve21(providersRoot, entry);
13036
13126
  if (!isDir(providerDir)) continue;
13037
13127
  const conformanceDir = resolve21(providerDir, "conformance");
@@ -13077,7 +13167,7 @@ function selectConformanceScopes(scope) {
13077
13167
  }
13078
13168
  function listCaseFiles(scope) {
13079
13169
  if (!existsSync18(scope.casesDir)) return [];
13080
- return readdirSync6(scope.casesDir).filter((entry) => entry.endsWith(".json")).sort().map((entry) => resolve21(scope.casesDir, entry));
13170
+ return readdirSync7(scope.casesDir).filter((entry) => entry.endsWith(".json")).sort().map((entry) => resolve21(scope.casesDir, entry));
13081
13171
  }
13082
13172
 
13083
13173
  // cli/commands/conformance.ts
@@ -14206,78 +14296,398 @@ var DB_COMMANDS = [
14206
14296
  DbMigrateCommand
14207
14297
  ];
14208
14298
 
14209
- // cli/commands/export.ts
14299
+ // cli/commands/example.ts
14300
+ import { cpSync as cpSync2, existsSync as existsSync21, statSync as statSync5 } from "fs";
14301
+ import { dirname as dirname16, relative as relative5, resolve as resolve26 } from "path";
14302
+ import { fileURLToPath as fileURLToPath5 } from "url";
14210
14303
  import { Command as Command13, Option as Option12 } from "clipanion";
14211
14304
 
14212
- // kernel/scan/query.ts
14213
- var HAS_VALUES = /* @__PURE__ */ new Set(["issues"]);
14214
- var ExportQueryError = class extends Error {
14215
- constructor(message) {
14216
- super(message);
14217
- this.name = "ExportQueryError";
14218
- }
14305
+ // cli/i18n/example.texts.ts
14306
+ var EXAMPLE_TEXTS = {
14307
+ // Success, written to stdout after the example project is created in
14308
+ // the cwd. The project ships unscanned (no `.skill-map/`), so the
14309
+ // next-steps block points the user at `sm scan` (provisions the
14310
+ // project and builds the graph) then `sm serve` (opens the map).
14311
+ written: " {{glyph}} Example project created in {{cwd}}\n\n A small wired portfolio is ready to explore: a handbook\n (AGENTS.md) that mentions a content-editor agent and invokes a\n publish command, a check-links skill the command calls, and the\n docs they reference. Next:\n\n {{scanGlyph}} sm scan build the graph from these files\n {{serveGlyph}} sm serve open the interactive map in the browser\n",
14312
+ writtenScanGlyph: "1)",
14313
+ writtenServeGlyph: "2)",
14314
+ // Refusal, the cwd is not empty and `--force` was not set. Goes to
14315
+ // stderr, exit code 2 (operational error per spec § Exit codes). The
14316
+ // example writes a self-contained project into the cwd, so it needs an
14317
+ // empty directory; the hint spells the two ways forward. Mirrors the
14318
+ // error shape: glyph + headline + dim hint.
14319
+ notEmpty: "{{glyph}} sm example: the current directory is not empty (found {{entries}})\n {{hint}}\n",
14320
+ notEmptyHint: "sm example writes a self-contained project; run it in a fresh empty directory, or pass `--force` to use this one anyway (colliding files are overwritten).",
14321
+ // Unexpected positional argument. The verb takes none. Goes to
14322
+ // stderr, exit code 2. Mirrors the error shape: glyph + headline +
14323
+ // dim hint.
14324
+ unexpectedArg: "{{glyph}} sm example: unexpected argument '{{arg}}'\n {{hint}}\n",
14325
+ unexpectedArgHint: "sm example takes no positional argument. Run `sm example` in an empty directory.",
14326
+ // I/O failure on write or on reading the bundled example payload.
14327
+ writeFailed: "{{glyph}} sm example: failed to write the example project: {{message}}\n",
14328
+ sourceMissing: "{{glyph}} sm example: could not read the bundled example payload from the install.\n {{hint}}\n",
14329
+ sourceMissingHint: "Reinstall @skill-map/cli or report the bug."
14219
14330
  };
14220
- function parseExportQuery(raw) {
14221
- const trimmed = raw.trim();
14222
- const out = { raw: trimmed };
14223
- if (trimmed.length === 0) return out;
14224
- const seen = /* @__PURE__ */ new Set();
14225
- for (const token of trimmed.split(/\s+/)) {
14226
- const eq = token.indexOf("=");
14227
- if (eq <= 0 || eq === token.length - 1) {
14228
- throw new ExportQueryError(
14229
- tx(QUERY_TEXTS.exportQueryInvalidToken, { token })
14230
- );
14231
- }
14232
- const key = token.slice(0, eq).toLowerCase();
14233
- const valuePart = token.slice(eq + 1);
14234
- if (seen.has(key)) {
14235
- throw new ExportQueryError(
14236
- tx(QUERY_TEXTS.exportQueryDuplicateKey, { key })
14237
- );
14238
- }
14239
- seen.add(key);
14240
- const values = valuePart.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
14241
- if (values.length === 0) {
14242
- throw new ExportQueryError(tx(QUERY_TEXTS.exportQueryEmptyValues, { key }));
14243
- }
14244
- switch (key) {
14245
- case "kind":
14246
- out.kinds = parseKindValues(values);
14247
- break;
14248
- case "has":
14249
- if (parseHasValues(values)) out.hasIssues = true;
14250
- break;
14251
- case "path":
14252
- out.pathGlobs = values;
14253
- break;
14254
- default:
14255
- throw new ExportQueryError(
14256
- tx(QUERY_TEXTS.exportQueryUnknownKey, { key })
14257
- );
14258
- }
14259
- }
14260
- return out;
14261
- }
14262
- function parseKindValues(values) {
14263
- for (const v of values) {
14264
- if (v.length === 0) {
14265
- throw new ExportQueryError(QUERY_TEXTS.exportQueryEmptyKind);
14266
- }
14267
- }
14268
- return values;
14269
- }
14270
- function parseHasValues(values) {
14271
- for (const v of values) {
14272
- if (!HAS_VALUES.has(v)) {
14273
- throw new ExportQueryError(
14274
- tx(QUERY_TEXTS.exportQueryUnsupportedHas, {
14275
- value: v,
14276
- allowed: [...HAS_VALUES].join(", ")
14277
- })
14278
- );
14279
- }
14280
- }
14331
+
14332
+ // cli/util/serve-banner.ts
14333
+ import { relative as relative4, isAbsolute as isAbsolute7 } from "path";
14334
+ var ESC2 = {
14335
+ reset: "\x1B[0m",
14336
+ bold: "\x1B[1m",
14337
+ dim: "\x1B[2m",
14338
+ underline: "\x1B[4m",
14339
+ /** 256-color violet (xterm 141). */
14340
+ violet: "\x1B[38;5;141m",
14341
+ /** 256-color green (xterm 42). */
14342
+ green: "\x1B[38;5;42m",
14343
+ /** 256-color yellow (xterm 214), matches `cli/util/ansi.ts:yellow`. */
14344
+ yellow: "\x1B[38;5;214m"
14345
+ };
14346
+ var LOGO_LINES = [
14347
+ " ____ _ _ _ _ __ __ ",
14348
+ " / ___|| | _(_) | | | \\/ | __ _ _ __ ",
14349
+ " \\___ \\| |/ / | | | | |\\/| |/ _` | '_ \\ ",
14350
+ " ___) | <| | | | | | | | (_| | |_) |",
14351
+ " |____/|_|\\_\\_|_|_| |_| |_|\\__,_| .__/ ",
14352
+ " |_| "
14353
+ ];
14354
+ var LOGO_WIDTH = 40;
14355
+ function renderBanner(input) {
14356
+ const url = `http://${input.host}:${input.port}`;
14357
+ const dbDisplay = formatDbPath(input.dbPath, input.cwd);
14358
+ const browserLine = input.openBrowser ? "Opening browser\u2026 Press Ctrl+C to stop." : `Visit ${url}/ in your browser. Press Ctrl+C to stop.`;
14359
+ if (!input.isTTY) {
14360
+ return renderFlat({
14361
+ host: input.host,
14362
+ port: input.port,
14363
+ dbPath: input.dbPath,
14364
+ openBrowser: input.openBrowser,
14365
+ dev: input.dev === true
14366
+ });
14367
+ }
14368
+ return renderFiglet({
14369
+ version: input.version,
14370
+ url,
14371
+ dbDisplay,
14372
+ pathDisplay: formatCwdPath(input.cwd),
14373
+ browserLine,
14374
+ colorEnabled: input.colorEnabled,
14375
+ referencePaths: input.referencePaths ?? [],
14376
+ dev: input.dev === true
14377
+ });
14378
+ }
14379
+ function resolveColorEnabled(opts) {
14380
+ if (opts.noColorFlag) return false;
14381
+ const noColor = opts.env["NO_COLOR"];
14382
+ if (noColor !== void 0 && noColor !== "") return false;
14383
+ const forceColor = opts.env["FORCE_COLOR"];
14384
+ if (forceColor !== void 0 && forceColor !== "") return true;
14385
+ return opts.isTTY;
14386
+ }
14387
+ function renderFlat(input) {
14388
+ const safeHost = sanitizeForTerminal(input.host);
14389
+ const safeDb = sanitizeForTerminal(input.dbPath);
14390
+ const url = `http://${safeHost}:${input.port}`;
14391
+ const devSuffix = input.dev ? " [dev]" : "";
14392
+ const linesOut = [];
14393
+ linesOut.push(`sm serve${devSuffix}: listening on ${url} (db=${safeDb})`);
14394
+ if (input.openBrowser) {
14395
+ linesOut.push(`sm serve: opening ${url}/ in your browser. Press Ctrl+C to stop.`);
14396
+ } else {
14397
+ linesOut.push(`sm serve: visit ${url}/ in your browser. Press Ctrl+C to stop.`);
14398
+ }
14399
+ return linesOut.join("\n") + "\n";
14400
+ }
14401
+ function renderLogoBlock(input) {
14402
+ const { dimOpen, dimClose, violetOpen, violetClose } = resolveAnsi(input.colorEnabled);
14403
+ const logoLines = LOGO_LINES.map((line) => `${violetOpen}${line}${violetClose}`);
14404
+ const versionText = `v${input.version}`;
14405
+ const versionPad = Math.max(0, LOGO_WIDTH - versionText.length);
14406
+ const versionLine = `${" ".repeat(versionPad)}${dimOpen}${versionText}${dimClose}`;
14407
+ return `${logoLines.join("\n")}
14408
+
14409
+ ${versionLine}
14410
+
14411
+ `;
14412
+ }
14413
+ function renderFiglet(input) {
14414
+ const {
14415
+ dimOpen,
14416
+ dimClose,
14417
+ greenUnderline,
14418
+ greenUnderlineClose,
14419
+ violetOpen,
14420
+ violetClose,
14421
+ yellowOpen,
14422
+ yellowClose
14423
+ } = resolveAnsi(input.colorEnabled);
14424
+ const logoLines = LOGO_LINES.map((line) => `${violetOpen}${line}${violetClose}`);
14425
+ const versionText = `v${input.version}`;
14426
+ const devText = "[dev]";
14427
+ const versionWidth = input.dev ? devText.length : versionText.length;
14428
+ const versionPad = Math.max(0, LOGO_WIDTH - versionWidth);
14429
+ const versionLine = input.dev ? `${" ".repeat(versionPad)}${yellowOpen}${devText}${yellowClose}` : `${" ".repeat(versionPad)}${dimOpen}${versionText}${dimClose}`;
14430
+ const lines = [];
14431
+ lines.push(...logoLines);
14432
+ lines.push("");
14433
+ lines.push(versionLine);
14434
+ lines.push("");
14435
+ lines.push(` ${dimOpen}Server${dimClose} ${greenUnderline}${input.url}${greenUnderlineClose}`);
14436
+ lines.push(` ${dimOpen}Path${dimClose} ${input.pathDisplay}`);
14437
+ lines.push(` ${dimOpen}DB${dimClose} ${input.dbDisplay}`);
14438
+ lines.push(...renderListRows("Refs", input.referencePaths, dimOpen, dimClose));
14439
+ lines.push("");
14440
+ lines.push(` ${dimOpen}${input.browserLine}${dimClose}`);
14441
+ lines.push("");
14442
+ return lines.join("\n") + "\n";
14443
+ }
14444
+ function renderListRows(label, values, dimOpen, dimClose) {
14445
+ if (values.length === 0) return [];
14446
+ const out = [];
14447
+ const labelPad = " ".repeat(Math.max(1, 9 - label.length));
14448
+ const continuationPad = " ".repeat(11);
14449
+ out.push(` ${dimOpen}${label}${dimClose}${labelPad}${sanitizeForTerminal(values[0])}`);
14450
+ for (let i = 1; i < values.length; i += 1) {
14451
+ out.push(`${continuationPad}${sanitizeForTerminal(values[i])}`);
14452
+ }
14453
+ return out;
14454
+ }
14455
+ var EMPTY_ANSI = {
14456
+ dimOpen: "",
14457
+ dimClose: "",
14458
+ greenUnderline: "",
14459
+ greenUnderlineClose: "",
14460
+ violetOpen: "",
14461
+ violetClose: "",
14462
+ yellowOpen: "",
14463
+ yellowClose: ""
14464
+ };
14465
+ var ENABLED_ANSI = {
14466
+ dimOpen: ESC2.dim,
14467
+ dimClose: ESC2.reset,
14468
+ greenUnderline: `${ESC2.green}${ESC2.underline}`,
14469
+ greenUnderlineClose: ESC2.reset,
14470
+ violetOpen: ESC2.violet,
14471
+ violetClose: ESC2.reset,
14472
+ yellowOpen: ESC2.yellow,
14473
+ yellowClose: ESC2.reset
14474
+ };
14475
+ function resolveAnsi(colorEnabled) {
14476
+ return colorEnabled ? ENABLED_ANSI : EMPTY_ANSI;
14477
+ }
14478
+ function formatDbPath(dbPath, cwd) {
14479
+ const safe = sanitizeForTerminal(dbPath);
14480
+ if (!isAbsolute7(safe)) return safe;
14481
+ const rel = relative4(cwd, safe);
14482
+ if (rel === "" || rel.startsWith("..") || isAbsolute7(rel)) {
14483
+ return safe;
14484
+ }
14485
+ return rel;
14486
+ }
14487
+ function formatCwdPath(cwd) {
14488
+ return sanitizeForTerminal(cwd);
14489
+ }
14490
+
14491
+ // cli/commands/example.ts
14492
+ var EXAMPLE_SOURCE_DIR = "fixtures/demo-scope";
14493
+ var ExampleCommand = class extends SmCommand {
14494
+ static paths = [["example"]];
14495
+ static usage = Command13.Usage({
14496
+ category: "Setup",
14497
+ description: "Materialize a ready-to-explore example project (the demo harness) in the current directory.",
14498
+ details: `
14499
+ Writes a small wired portfolio into <cwd>: a handbook (AGENTS.md)
14500
+ that mentions a content-editor agent and invokes a publish command,
14501
+ a check-links skill the command calls, and the docs they reference.
14502
+ It is the same harness the public demo renders.
14503
+
14504
+ Ships unscanned (no .skill-map/): run \`sm scan\` then \`sm serve\`
14505
+ to build and open the graph. Requires an empty directory; refuses a
14506
+ non-empty cwd (exit 2) unless --force. Takes no positional argument.
14507
+ `,
14508
+ examples: [
14509
+ ["Create the example project in the cwd", "$0 example"],
14510
+ ["Create it even if the directory is not empty", "$0 example --force"]
14511
+ ]
14512
+ });
14513
+ // The verb takes no positional argument. Accept one so a stray
14514
+ // `sm example foo` lands on a friendly usage error (guarded in
14515
+ // `run()`) instead of clipanion's generic "extraneous argument".
14516
+ legacyPositional = Option12.String({ required: false });
14517
+ force = Option12.Boolean("--force", false, {
14518
+ description: "Overwrite colliding files in a non-empty directory without prompting."
14519
+ });
14520
+ async run() {
14521
+ const ctx = defaultRuntimeContext();
14522
+ const stderr = this.context.stderr;
14523
+ const stderrAnsi = this.ansiFor("stderr");
14524
+ const errGlyph = stderrAnsi.red("\u2715");
14525
+ if (this.legacyPositional !== void 0) {
14526
+ this.printer.error(
14527
+ tx(EXAMPLE_TEXTS.unexpectedArg, {
14528
+ glyph: errGlyph,
14529
+ arg: this.legacyPositional,
14530
+ hint: stderrAnsi.dim(EXAMPLE_TEXTS.unexpectedArgHint)
14531
+ })
14532
+ );
14533
+ return ExitCode.Error;
14534
+ }
14535
+ if (!this.force && !isDirEmpty(ctx.cwd)) {
14536
+ this.printer.error(
14537
+ tx(EXAMPLE_TEXTS.notEmpty, {
14538
+ glyph: errGlyph,
14539
+ entries: listCwdEntries(ctx.cwd),
14540
+ hint: stderrAnsi.dim(EXAMPLE_TEXTS.notEmptyHint)
14541
+ })
14542
+ );
14543
+ return ExitCode.Error;
14544
+ }
14545
+ let sourceDir;
14546
+ try {
14547
+ sourceDir = resolveExampleSourceDir();
14548
+ } catch {
14549
+ this.printer.error(
14550
+ tx(EXAMPLE_TEXTS.sourceMissing, {
14551
+ glyph: errGlyph,
14552
+ hint: stderrAnsi.dim(EXAMPLE_TEXTS.sourceMissingHint)
14553
+ })
14554
+ );
14555
+ return ExitCode.Error;
14556
+ }
14557
+ try {
14558
+ cpSync2(sourceDir, ctx.cwd, {
14559
+ recursive: true,
14560
+ // Never copy the example's own scan state: the project must ship
14561
+ // unscanned so the user's first `sm scan` provisions it fresh.
14562
+ filter: (src) => isExamplePayloadEntry(sourceDir, src)
14563
+ });
14564
+ } catch (err) {
14565
+ this.printer.error(
14566
+ tx(EXAMPLE_TEXTS.writeFailed, {
14567
+ glyph: errGlyph,
14568
+ message: formatErrorMessage(err)
14569
+ })
14570
+ );
14571
+ return ExitCode.Error;
14572
+ }
14573
+ const colorEnabled = resolveColorEnabled({
14574
+ isTTY: stderr.isTTY === true,
14575
+ noColorFlag: this.noColor,
14576
+ env: process.env
14577
+ });
14578
+ this.printer.info(renderLogoBlock({ version: VERSION, colorEnabled }));
14579
+ const ansi = this.ansiFor("stdout");
14580
+ this.printer.data(
14581
+ tx(EXAMPLE_TEXTS.written, {
14582
+ glyph: ansi.green("\u2713"),
14583
+ cwd: ansi.dim(displayCwd(ctx.cwd)),
14584
+ scanGlyph: ansi.dim(EXAMPLE_TEXTS.writtenScanGlyph),
14585
+ serveGlyph: ansi.dim(EXAMPLE_TEXTS.writtenServeGlyph)
14586
+ })
14587
+ );
14588
+ return ExitCode.Ok;
14589
+ }
14590
+ };
14591
+ var cachedSourceDir;
14592
+ function resolveExampleSourceDir() {
14593
+ if (cachedSourceDir !== void 0) return cachedSourceDir;
14594
+ const here = dirname16(fileURLToPath5(import.meta.url));
14595
+ const candidates = [
14596
+ // dev: src/cli/commands/ → repo-root fixtures/demo-scope/
14597
+ resolve26(here, "../../..", EXAMPLE_SOURCE_DIR),
14598
+ // bundled: dist/cli.js → dist/cli/example (sibling)
14599
+ resolve26(here, "cli/example"),
14600
+ // bundled fallback: any-depth → cli/example
14601
+ resolve26(here, "../cli/example")
14602
+ ];
14603
+ for (const candidate of candidates) {
14604
+ if (existsSync21(candidate) && statSync5(candidate).isDirectory()) {
14605
+ cachedSourceDir = candidate;
14606
+ return candidate;
14607
+ }
14608
+ }
14609
+ throw new Error(
14610
+ `example payload directory not found in any candidate location (last tried: ${candidates[candidates.length - 1]})`
14611
+ );
14612
+ }
14613
+ function isExamplePayloadEntry(sourceRoot, src) {
14614
+ const rel = relative5(sourceRoot, src);
14615
+ if (rel === "") return true;
14616
+ return rel.split(/[\\/]/)[0] !== ".skill-map";
14617
+ }
14618
+
14619
+ // cli/commands/export.ts
14620
+ import { Command as Command14, Option as Option13 } from "clipanion";
14621
+
14622
+ // kernel/scan/query.ts
14623
+ var HAS_VALUES = /* @__PURE__ */ new Set(["issues"]);
14624
+ var ExportQueryError = class extends Error {
14625
+ constructor(message) {
14626
+ super(message);
14627
+ this.name = "ExportQueryError";
14628
+ }
14629
+ };
14630
+ function parseExportQuery(raw) {
14631
+ const trimmed = raw.trim();
14632
+ const out = { raw: trimmed };
14633
+ if (trimmed.length === 0) return out;
14634
+ const seen = /* @__PURE__ */ new Set();
14635
+ for (const token of trimmed.split(/\s+/)) {
14636
+ const eq = token.indexOf("=");
14637
+ if (eq <= 0 || eq === token.length - 1) {
14638
+ throw new ExportQueryError(
14639
+ tx(QUERY_TEXTS.exportQueryInvalidToken, { token })
14640
+ );
14641
+ }
14642
+ const key = token.slice(0, eq).toLowerCase();
14643
+ const valuePart = token.slice(eq + 1);
14644
+ if (seen.has(key)) {
14645
+ throw new ExportQueryError(
14646
+ tx(QUERY_TEXTS.exportQueryDuplicateKey, { key })
14647
+ );
14648
+ }
14649
+ seen.add(key);
14650
+ const values = valuePart.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
14651
+ if (values.length === 0) {
14652
+ throw new ExportQueryError(tx(QUERY_TEXTS.exportQueryEmptyValues, { key }));
14653
+ }
14654
+ switch (key) {
14655
+ case "kind":
14656
+ out.kinds = parseKindValues(values);
14657
+ break;
14658
+ case "has":
14659
+ if (parseHasValues(values)) out.hasIssues = true;
14660
+ break;
14661
+ case "path":
14662
+ out.pathGlobs = values;
14663
+ break;
14664
+ default:
14665
+ throw new ExportQueryError(
14666
+ tx(QUERY_TEXTS.exportQueryUnknownKey, { key })
14667
+ );
14668
+ }
14669
+ }
14670
+ return out;
14671
+ }
14672
+ function parseKindValues(values) {
14673
+ for (const v of values) {
14674
+ if (v.length === 0) {
14675
+ throw new ExportQueryError(QUERY_TEXTS.exportQueryEmptyKind);
14676
+ }
14677
+ }
14678
+ return values;
14679
+ }
14680
+ function parseHasValues(values) {
14681
+ for (const v of values) {
14682
+ if (!HAS_VALUES.has(v)) {
14683
+ throw new ExportQueryError(
14684
+ tx(QUERY_TEXTS.exportQueryUnsupportedHas, {
14685
+ value: v,
14686
+ allowed: [...HAS_VALUES].join(", ")
14687
+ })
14688
+ );
14689
+ }
14690
+ }
14281
14691
  return values.includes("issues");
14282
14692
  }
14283
14693
  function applyExportQuery(scan, query) {
@@ -14386,7 +14796,7 @@ var DEFERRED_FORMATS = {
14386
14796
  };
14387
14797
  var ExportCommand = class extends SmCommand {
14388
14798
  static paths = [["export"]];
14389
- static usage = Command13.Usage({
14799
+ static usage = Command14.Usage({
14390
14800
  category: "Browse",
14391
14801
  description: "Filtered export. Query syntax is implementation-defined pre-1.0.",
14392
14802
  details: `
@@ -14411,8 +14821,8 @@ var ExportCommand = class extends SmCommand {
14411
14821
  ["Files under a path glob", '$0 export "path=.claude/commands/**" --format json']
14412
14822
  ]
14413
14823
  });
14414
- query = Option12.String({ required: false });
14415
- format = Option12.String("--format", { required: false });
14824
+ query = Option13.String({ required: false });
14825
+ format = Option13.String("--format", { required: false });
14416
14826
  async run() {
14417
14827
  const stderrAnsi = this.ansiFor("stderr");
14418
14828
  const errGlyph = stderrAnsi.red("\u2715");
@@ -14636,7 +15046,7 @@ function pickTitle2(node) {
14636
15046
  }
14637
15047
 
14638
15048
  // cli/commands/graph.ts
14639
- import { Command as Command14, Option as Option13 } from "clipanion";
15049
+ import { Command as Command15, Option as Option14 } from "clipanion";
14640
15050
 
14641
15051
  // cli/i18n/graph.texts.ts
14642
15052
  var GRAPH_TEXTS = {
@@ -14654,7 +15064,7 @@ var GRAPH_TEXTS = {
14654
15064
  var DEFAULT_FORMAT = "ascii";
14655
15065
  var GraphCommand = class extends SmCommand {
14656
15066
  static paths = [["graph"]];
14657
- static usage = Command14.Usage({
15067
+ static usage = Command15.Usage({
14658
15068
  category: "Browse",
14659
15069
  description: "Render the full graph via the named formatter.",
14660
15070
  details: `
@@ -14671,10 +15081,10 @@ var GraphCommand = class extends SmCommand {
14671
15081
  ["Use a non-default DB file", "$0 graph --db /path/to/skill-map.db"]
14672
15082
  ]
14673
15083
  });
14674
- format = Option13.String("--format", DEFAULT_FORMAT, {
15084
+ format = Option14.String("--format", DEFAULT_FORMAT, {
14675
15085
  description: `Formatter format. Must match the \`formatId\` field of a registered formatter. Default: ${DEFAULT_FORMAT}.`
14676
15086
  });
14677
- noPlugins = Option13.Boolean("--no-plugins", false, {
15087
+ noPlugins = Option14.Boolean("--no-plugins", false, {
14678
15088
  description: "Skip drop-in plugin discovery. Only built-in formatters participate."
14679
15089
  });
14680
15090
  async run() {
@@ -14725,8 +15135,8 @@ var GraphCommand = class extends SmCommand {
14725
15135
  // cli/commands/help.ts
14726
15136
  import { readFileSync as readFileSync17 } from "fs";
14727
15137
  import { createRequire as createRequire7 } from "module";
14728
- import { resolve as resolve26 } from "path";
14729
- import { Command as Command15, Option as Option14 } from "clipanion";
15138
+ import { resolve as resolve27 } from "path";
15139
+ import { Command as Command16, Option as Option15 } from "clipanion";
14730
15140
 
14731
15141
  // cli/i18n/help.texts.ts
14732
15142
  var HELP_TEXTS = {
@@ -14869,9 +15279,9 @@ var HELP_GROUPS = {
14869
15279
  };
14870
15280
 
14871
15281
  // cli/commands/help.ts
14872
- var HelpCommand = class extends Command15 {
15282
+ var HelpCommand = class extends Command16 {
14873
15283
  static paths = [["help"]];
14874
- static usage = Command15.Usage({
15284
+ static usage = Command16.Usage({
14875
15285
  category: "Introspection",
14876
15286
  description: "Self-describing introspection. --format human|md|json.",
14877
15287
  details: `
@@ -14884,13 +15294,13 @@ var HelpCommand = class extends Command15 {
14884
15294
  json : structured surface dump per spec/cli-contract.md.
14885
15295
  `
14886
15296
  });
14887
- verbParts = Option14.Rest({ required: 0 });
14888
- format = Option14.String("--format", "human");
15297
+ verbParts = Option15.Rest({ required: 0 });
15298
+ format = Option15.String("--format", "human");
14889
15299
  // `HelpCommand` extends Clipanion's `Command` directly (see block
14890
15300
  // comment), so it does not inherit `SmCommand`'s `--no-color`. Declared
14891
15301
  // here because the human overview now emits a coloured tutorial glyph;
14892
15302
  // `ansiFor` still layers `NO_COLOR` / `FORCE_COLOR` / TTY on top.
14893
- noColor = Option14.Boolean("--no-color", false, {
15303
+ noColor = Option15.Boolean("--no-color", false, {
14894
15304
  description: "Disable ANSI color codes."
14895
15305
  });
14896
15306
  async execute() {
@@ -15039,7 +15449,7 @@ function resolveSpecVersion() {
15039
15449
  try {
15040
15450
  const req = createRequire7(import.meta.url);
15041
15451
  const indexPath = req.resolve("@skill-map/spec/index.json");
15042
- const pkgPath = resolve26(indexPath, "..", "package.json");
15452
+ const pkgPath = resolve27(indexPath, "..", "package.json");
15043
15453
  const pkg = JSON.parse(readFileSync17(pkgPath, "utf8"));
15044
15454
  return pkg.version;
15045
15455
  } catch {
@@ -15282,12 +15692,12 @@ function renderCompactOverview(verbs, ansi) {
15282
15692
  lines.push(HELP_TEXTS.compactFooter);
15283
15693
  return lines.join("\n") + "\n";
15284
15694
  }
15285
- var RootHelpCommand = class extends Command15 {
15695
+ var RootHelpCommand = class extends Command16 {
15286
15696
  static paths = [["-h"], ["--help"]];
15287
15697
  // Mirrors `HelpCommand.noColor`: this command extends Clipanion's
15288
15698
  // `Command` directly, so the flag is declared here to gate the
15289
15699
  // coloured tutorial glyph in the overview.
15290
- noColor = Option14.Boolean("--no-color", false, {
15700
+ noColor = Option15.Boolean("--no-color", false, {
15291
15701
  description: "Disable ANSI color codes."
15292
15702
  });
15293
15703
  async execute() {
@@ -15347,8 +15757,8 @@ function registeredVerbPaths(cli2) {
15347
15757
 
15348
15758
  // cli/commands/hooks.ts
15349
15759
  import { chmod as chmod2, mkdir as mkdir3, readFile as readFile2, stat as stat2, writeFile } from "fs/promises";
15350
- import { dirname as dirname16, resolve as resolve27 } from "path";
15351
- import { Command as Command16, Option as Option15 } from "clipanion";
15760
+ import { dirname as dirname17, resolve as resolve28 } from "path";
15761
+ import { Command as Command17, Option as Option16 } from "clipanion";
15352
15762
 
15353
15763
  // cli/i18n/hooks.texts.ts
15354
15764
  var HOOKS_TEXTS = {
@@ -15395,7 +15805,7 @@ var SKILL_MAP_BLOCK = [
15395
15805
  var FRESH_HOOK_BODY = HOOK_SHEBANG + SKILL_MAP_BLOCK;
15396
15806
  var HooksInstallCommand = class extends SmCommand {
15397
15807
  static paths = [["hooks", "install"]];
15398
- static usage = Command16.Usage({
15808
+ static usage = Command17.Usage({
15399
15809
  category: "Actions",
15400
15810
  description: "Install a git pre-commit hook that auto-bumps staged sidecar drift.",
15401
15811
  details: `
@@ -15418,8 +15828,8 @@ var HooksInstallCommand = class extends SmCommand {
15418
15828
  ["Preview what would be written", "$0 hooks install pre-commit-bump --dry-run"]
15419
15829
  ]
15420
15830
  });
15421
- flavour = Option15.String({ required: true });
15422
- dryRun = Option15.Boolean("-n,--dry-run", false);
15831
+ flavour = Option16.String({ required: true });
15832
+ dryRun = Option16.Boolean("-n,--dry-run", false);
15423
15833
  // The remaining cyclomatic count is from CLI ergonomics, flavour
15424
15834
  // guard, repo lookup, marker detection, dry-run / json / chained /
15425
15835
  // fresh branches each contributing a guard. Inner work already lives
@@ -15450,8 +15860,8 @@ var HooksInstallCommand = class extends SmCommand {
15450
15860
  );
15451
15861
  return ExitCode.NotFound;
15452
15862
  }
15453
- const hooksDir = resolve27(repoRoot, ".git", "hooks");
15454
- const hookPath = resolve27(hooksDir, "pre-commit");
15863
+ const hooksDir = resolve28(repoRoot, ".git", "hooks");
15864
+ const hookPath = resolve28(hooksDir, "pre-commit");
15455
15865
  const existing = await pathExists(hookPath) ? await readFile2(hookPath, "utf8") : null;
15456
15866
  const planned2 = computePlannedHookContent(existing);
15457
15867
  if (planned2.kind === "already-installed") {
@@ -15509,8 +15919,8 @@ var HooksInstallCommand = class extends SmCommand {
15509
15919
  async function findGitRepoRoot(cwd) {
15510
15920
  let current = cwd;
15511
15921
  while (true) {
15512
- if (await pathExists(resolve27(current, ".git"))) return current;
15513
- const parent = dirname16(current);
15922
+ if (await pathExists(resolve28(current, ".git"))) return current;
15923
+ const parent = dirname17(current);
15514
15924
  if (parent === current) return null;
15515
15925
  current = parent;
15516
15926
  }
@@ -15532,11 +15942,11 @@ var HOOKS_COMMANDS = [HooksInstallCommand];
15532
15942
  // cli/commands/init.ts
15533
15943
  import { mkdir as mkdir4, readFile as readFile3, unlink, writeFile as writeFile2 } from "fs/promises";
15534
15944
  import { join as join16 } from "path";
15535
- import { Command as Command17, Option as Option16 } from "clipanion";
15945
+ import { Command as Command18, Option as Option17 } from "clipanion";
15536
15946
 
15537
15947
  // kernel/orchestrator/index.ts
15538
- import { existsSync as existsSync23, statSync as statSync6 } from "fs";
15539
- import { isAbsolute as isAbsolute9, resolve as resolve30 } from "path";
15948
+ import { existsSync as existsSync24, statSync as statSync7 } from "fs";
15949
+ import { isAbsolute as isAbsolute10, resolve as resolve31 } from "path";
15540
15950
  import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
15541
15951
 
15542
15952
  // kernel/i18n/orchestrator.texts.ts
@@ -16290,8 +16700,7 @@ function matchesKindPrecondition(ex, kind) {
16290
16700
  function matchesProviderPrecondition(ex, activeProvider) {
16291
16701
  const providers = ex.precondition?.provider;
16292
16702
  if (!providers || providers.length === 0) return true;
16293
- if (activeProvider === null) return false;
16294
- return providers.includes(activeProvider);
16703
+ return activeProvider !== null && providers.includes(activeProvider);
16295
16704
  }
16296
16705
  function splitLegacy(applicableExtractors, applicableQualifiedIds, nodeHashCacheEligible) {
16297
16706
  const cachedQualifiedIds = /* @__PURE__ */ new Set();
@@ -16482,7 +16891,7 @@ function collectBrokenLinks(links, nodes, ctx) {
16482
16891
  return broken;
16483
16892
  }
16484
16893
  function applyResolution(link, indexes, ctx) {
16485
- const resolution = resolve28(link, indexes, ctx);
16894
+ const resolution = resolve29(link, indexes, ctx);
16486
16895
  if (resolution === "none") return;
16487
16896
  link.resolvedTarget = resolution;
16488
16897
  }
@@ -16497,7 +16906,7 @@ function buildIndexes(nodes, ctx) {
16497
16906
  }
16498
16907
  return { byPath: byPath3, byName, nodeByPath };
16499
16908
  }
16500
- function resolve28(link, indexes, ctx) {
16909
+ function resolve29(link, indexes, ctx) {
16501
16910
  if (indexes.byPath.has(link.target)) return link.target;
16502
16911
  return resolveByName(link, indexes, ctx);
16503
16912
  }
@@ -16886,7 +17295,7 @@ function detectRenamesAndOrphans(prior, current, issues, silenced) {
16886
17295
  }
16887
17296
 
16888
17297
  // kernel/orchestrator/walk.ts
16889
- import { isAbsolute as isAbsolute8, resolve as resolve29 } from "path";
17298
+ import { isAbsolute as isAbsolute9, resolve as resolve30 } from "path";
16890
17299
 
16891
17300
  // kernel/sidecar/drift.ts
16892
17301
  function computeDriftStatus(args2) {
@@ -16899,8 +17308,8 @@ function computeDriftStatus(args2) {
16899
17308
  }
16900
17309
 
16901
17310
  // kernel/sidecar/discover-orphans.ts
16902
- import { existsSync as existsSync21, readdirSync as readdirSync7, statSync as statSync5 } from "fs";
16903
- import { join as join13, relative as relative4, sep as sep4 } from "path";
17311
+ import { existsSync as existsSync22, readdirSync as readdirSync8, statSync as statSync6 } from "fs";
17312
+ import { join as join13, relative as relative6, sep as sep4 } from "path";
16904
17313
  function discoverOrphanSidecars(roots, shouldSkip) {
16905
17314
  const out = [];
16906
17315
  for (const root of roots) {
@@ -16911,13 +17320,13 @@ function discoverOrphanSidecars(roots, shouldSkip) {
16911
17320
  function walk2(root, current, shouldSkip, out) {
16912
17321
  let entries;
16913
17322
  try {
16914
- entries = readdirSync7(current, { withFileTypes: true, encoding: "utf8" });
17323
+ entries = readdirSync8(current, { withFileTypes: true, encoding: "utf8" });
16915
17324
  } catch {
16916
17325
  return;
16917
17326
  }
16918
17327
  for (const entry of entries) {
16919
17328
  const full = join13(current, entry.name);
16920
- const rel = relative4(root, full).split(sep4).join("/");
17329
+ const rel = relative6(root, full).split(sep4).join("/");
16921
17330
  if (shouldSkip(rel)) continue;
16922
17331
  if (entry.isSymbolicLink()) continue;
16923
17332
  if (entry.isDirectory()) {
@@ -16927,13 +17336,13 @@ function walk2(root, current, shouldSkip, out) {
16927
17336
  if (!entry.isFile()) continue;
16928
17337
  if (!entry.name.endsWith(".sm")) continue;
16929
17338
  const expectedMd = `${full.slice(0, -".sm".length)}.md`;
16930
- if (existsSync21(expectedMd) && safeIsFile(expectedMd)) continue;
17339
+ if (existsSync22(expectedMd) && safeIsFile(expectedMd)) continue;
16931
17340
  out.push({ sidecarPath: full, relativePath: rel, expectedMdPath: expectedMd });
16932
17341
  }
16933
17342
  }
16934
17343
  function safeIsFile(path) {
16935
17344
  try {
16936
- return statSync5(path).isFile();
17345
+ return statSync6(path).isFile();
16937
17346
  } catch {
16938
17347
  return false;
16939
17348
  }
@@ -16941,8 +17350,8 @@ function safeIsFile(path) {
16941
17350
 
16942
17351
  // kernel/orchestrator/node-build.ts
16943
17352
  import { createHash as createHash2 } from "crypto";
16944
- import { existsSync as existsSync22 } from "fs";
16945
- import { isAbsolute as isAbsolute7, resolve as resolvePath } from "path";
17353
+ import { existsSync as existsSync23 } from "fs";
17354
+ import { isAbsolute as isAbsolute8, resolve as resolvePath } from "path";
16946
17355
  import "js-tiktoken/lite";
16947
17356
  import yaml4 from "js-yaml";
16948
17357
 
@@ -17105,12 +17514,12 @@ function resolveSidecarOverlay(relativePath2, nodePathForIssue, roots, liveBodyH
17105
17514
  };
17106
17515
  }
17107
17516
  function resolveAbsoluteMdPath(relativePath2, roots) {
17108
- if (isAbsolute7(relativePath2)) {
17109
- return existsSync22(relativePath2) ? relativePath2 : null;
17517
+ if (isAbsolute8(relativePath2)) {
17518
+ return existsSync23(relativePath2) ? relativePath2 : null;
17110
17519
  }
17111
17520
  for (const root of roots) {
17112
17521
  const candidate = resolvePath(root, relativePath2);
17113
- if (existsSync22(candidate)) return candidate;
17522
+ if (existsSync23(candidate)) return candidate;
17114
17523
  }
17115
17524
  return null;
17116
17525
  }
@@ -17332,8 +17741,8 @@ function expandSidecarPaths(paths, priorNodesByPath) {
17332
17741
  }
17333
17742
  function toAbsolute(relPath, roots) {
17334
17743
  const root = roots[0] ?? ".";
17335
- const absRoot = isAbsolute8(root) ? root : resolve29(root);
17336
- return resolve29(absRoot, relPath);
17744
+ const absRoot = isAbsolute9(root) ? root : resolve30(root);
17745
+ return resolve30(absRoot, relPath);
17337
17746
  }
17338
17747
  function resolveEffectiveCaps(opts) {
17339
17748
  return {
@@ -17351,7 +17760,6 @@ function buildPriorMtimes(opts) {
17351
17760
  }
17352
17761
  function providerParticipates(provider, activeProvider) {
17353
17762
  if (!provider.gatedByActiveLens) return true;
17354
- if (activeProvider === null) return true;
17355
17763
  return provider.id === activeProvider;
17356
17764
  }
17357
17765
  function createWalkAccumulators() {
@@ -17773,8 +18181,7 @@ function buildPostWalkTransformCtx(providers, nodes, activeProvider) {
17773
18181
  const reservedNodePaths = buildReservedNodePaths(
17774
18182
  nodes,
17775
18183
  kindRegistry,
17776
- reservedNamesByProviderKind,
17777
- activeProvider
18184
+ reservedNamesByProviderKind
17778
18185
  );
17779
18186
  return { kindRegistry, providerResolution, activeProvider, reservedNodePaths };
17780
18187
  }
@@ -17805,15 +18212,14 @@ function indexReservedNames(provider, out) {
17805
18212
  }
17806
18213
  }
17807
18214
  }
17808
- function buildReservedNodePaths(nodes, kindRegistry, reservedNamesByProviderKind, activeProvider) {
18215
+ function buildReservedNodePaths(nodes, kindRegistry, reservedNamesByProviderKind) {
17809
18216
  const out = /* @__PURE__ */ new Set();
17810
18217
  for (const node of nodes) {
17811
18218
  const selfKey = `${node.provider}/${node.kind}`;
17812
18219
  const selfReserved = reservedNamesByProviderKind.get(selfKey);
17813
- const lensReserved = activeProvider && activeProvider !== node.provider ? reservedNamesByProviderKind.get(`${activeProvider}/${node.kind}`) : void 0;
17814
- if (!hasEntries(selfReserved) && !hasEntries(lensReserved)) continue;
18220
+ if (!hasEntries(selfReserved)) continue;
17815
18221
  const ids = deriveNodeIdentifiers(node, kindRegistry.get(selfKey));
17816
- if (ids.some((id) => selfReserved?.has(id) === true || lensReserved?.has(id) === true)) {
18222
+ if (ids.some((id) => selfReserved?.has(id) === true)) {
17817
18223
  out.add(node.path);
17818
18224
  }
17819
18225
  }
@@ -17926,7 +18332,7 @@ function validateRoots(roots) {
17926
18332
  throw new Error(ORCHESTRATOR_TEXTS.runScanRootEmptyArray);
17927
18333
  }
17928
18334
  for (const root of roots) {
17929
- if (!existsSync23(root) || !statSync6(root).isDirectory()) {
18335
+ if (!existsSync24(root) || !statSync7(root).isDirectory()) {
17930
18336
  throw new Error(tx(ORCHESTRATOR_TEXTS.runScanRootMissing, { root }));
17931
18337
  }
17932
18338
  }
@@ -17934,8 +18340,8 @@ function validateRoots(roots) {
17934
18340
  function resolveActiveProviderOption(optionValue, roots, providers) {
17935
18341
  if (optionValue !== void 0) return optionValue;
17936
18342
  for (const root of roots) {
17937
- const absRoot = isAbsolute9(root) ? root : resolve30(root);
17938
- if (!existsSync23(absRoot)) continue;
18343
+ const absRoot = isAbsolute10(root) ? root : resolve31(root);
18344
+ if (!existsSync24(absRoot)) continue;
17939
18345
  const detected = detectProvidersFromFilesystem(absRoot, providers)[0] ?? null;
17940
18346
  if (detected !== null) return detected;
17941
18347
  }
@@ -17943,10 +18349,10 @@ function resolveActiveProviderOption(optionValue, roots, providers) {
17943
18349
  }
17944
18350
 
17945
18351
  // kernel/scan/watcher.ts
17946
- import { resolve as resolve31, relative as relative5, sep as sep5 } from "path";
18352
+ import { resolve as resolve32, relative as relative7, sep as sep5 } from "path";
17947
18353
  import chokidar from "chokidar";
17948
18354
  function createChokidarWatcher(opts) {
17949
- const absRoots = opts.roots.map((r) => resolve31(opts.cwd, r));
18355
+ const absRoots = opts.roots.map((r) => resolve32(opts.cwd, r));
17950
18356
  const ignoreFilterOpt = opts.ignoreFilter;
17951
18357
  const getFilter = ignoreFilterOpt === void 0 ? void 0 : typeof ignoreFilterOpt === "function" ? ignoreFilterOpt : () => ignoreFilterOpt;
17952
18358
  const ignored = getFilter ? (path) => {
@@ -18039,7 +18445,7 @@ function createChokidarWatcher(opts) {
18039
18445
  }
18040
18446
  function relativePathFromRoots2(absolute, absRoots) {
18041
18447
  for (const root of absRoots) {
18042
- const rel = relative5(root, absolute);
18448
+ const rel = relative7(root, absolute);
18043
18449
  if (rel === "" || rel === ".") return "";
18044
18450
  if (!rel.startsWith("..") && !rel.startsWith(`..${sep5}`)) {
18045
18451
  return rel.split(sep5).join("/");
@@ -18374,23 +18780,6 @@ var SCAN_RUNNER_TEXTS = {
18374
18780
  * operator notices a typo without the walker silently swallowing it.
18375
18781
  */
18376
18782
  referenceWalkMissingRoot: 'scan.referencePaths: configured path "{{path}}" does not exist; skipped.',
18377
- /**
18378
- * Active-provider bootstrap: filesystem auto-detect found no
18379
- * markers (`.claude/`, `.codex/`, `AGENTS.md`, `.cursor/`) anywhere
18380
- * under cwd or the effective scan roots. Plain-markdown projects
18381
- * keep scanning fine; provider-specific extractors silently no-op
18382
- * for this scan. Follows `context/cli-output-style.md` §3.1b
18383
- * (two-line block, glyph + dim hint):
18384
- * - line 1: `{{glyph}}` (yellow `⚠`) + headline naming the
18385
- * missing markers,
18386
- * - line 2 (indent 3): `{{hint}}`, dim, names the consequence
18387
- * and the actionable next step.
18388
- * Both the full block AND the bare hint are catalog-side so the
18389
- * caller can wrap the hint in `ansi.dim(...)` without splitting
18390
- * the template manually.
18391
- */
18392
- activeProviderNoMarkerWarning: "{{glyph}} No provider markers detected (.claude/, .codex/, AGENTS.md, .cursor/).\n {{hint}}\n",
18393
- activeProviderNoMarkerWarningHint: "Scanning as universal markdown only; provider-specific link types (e.g. claude @-directives, /-commands) will not appear. Set `activeProvider` in .skill-map/settings.json or install a provider plugin to enable them.",
18394
18783
  /**
18395
18784
  * Active-provider bootstrap: filesystem auto-detect found exactly
18396
18785
  * one marker and persisted the detected id to project settings.
@@ -18467,9 +18856,9 @@ function resolveScanRoots(inputs) {
18467
18856
  }
18468
18857
 
18469
18858
  // core/runtime/reference-paths-walker.ts
18470
- import { readdirSync as readdirSync8, statSync as statSync7 } from "fs";
18859
+ import { readdirSync as readdirSync9, statSync as statSync8 } from "fs";
18471
18860
  import { homedir as osHomedir2 } from "os";
18472
- import { isAbsolute as isAbsolute10, join as join14, resolve as resolve32 } from "path";
18861
+ import { isAbsolute as isAbsolute11, join as join14, resolve as resolve33 } from "path";
18473
18862
  var REFERENCE_WALK_MAX_FILES = 5e4;
18474
18863
  var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
18475
18864
  "node_modules",
@@ -18477,10 +18866,10 @@ var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
18477
18866
  SKILL_MAP_DIR
18478
18867
  ]);
18479
18868
  function resolveScanPath(raw, cwd) {
18480
- if (raw.startsWith("~/")) return resolve32(join14(osHomedir2(), raw.slice(2)));
18481
- if (raw === "~") return resolve32(osHomedir2());
18482
- if (isAbsolute10(raw)) return resolve32(raw);
18483
- return resolve32(cwd, raw);
18869
+ if (raw.startsWith("~/")) return resolve33(join14(osHomedir2(), raw.slice(2)));
18870
+ if (raw === "~") return resolve33(osHomedir2());
18871
+ if (isAbsolute11(raw)) return resolve33(raw);
18872
+ return resolve33(cwd, raw);
18484
18873
  }
18485
18874
  function walkReferencePaths(rawRoots, cwd) {
18486
18875
  const paths = /* @__PURE__ */ new Set();
@@ -18502,7 +18891,7 @@ function walkInto(dir, out) {
18502
18891
  if (out.size >= REFERENCE_WALK_MAX_FILES) return true;
18503
18892
  let entries;
18504
18893
  try {
18505
- entries = readdirSync8(dir, { withFileTypes: true });
18894
+ entries = readdirSync9(dir, { withFileTypes: true });
18506
18895
  } catch {
18507
18896
  return false;
18508
18897
  }
@@ -18521,15 +18910,15 @@ function walkInto(dir, out) {
18521
18910
  }
18522
18911
  function safeStat(path) {
18523
18912
  try {
18524
- return statSync7(path);
18913
+ return statSync8(path);
18525
18914
  } catch {
18526
18915
  return null;
18527
18916
  }
18528
18917
  }
18529
18918
 
18530
18919
  // core/runtime/active-provider-bootstrap.ts
18531
- import { createInterface as createInterface3 } from "readline";
18532
- import { isAbsolute as isAbsolute11, join as join15 } from "path";
18920
+ import { createInterface as createInterface4 } from "readline";
18921
+ import { isAbsolute as isAbsolute12, join as join15 } from "path";
18533
18922
  async function bootstrapActiveProvider(opts) {
18534
18923
  const fromCwd = resolveActiveProvider(opts.cwd, opts.providers);
18535
18924
  if (fromCwd.source === "config") {
@@ -18549,15 +18938,7 @@ async function bootstrapActiveProvider(opts) {
18549
18938
  opts.providers
18550
18939
  );
18551
18940
  if (detected.length === 0) {
18552
- const warnGlyph = opts.style?.warnGlyph ?? "\u26A0";
18553
- const dim = opts.style?.dim ?? ((s) => s);
18554
- opts.printer.warn(
18555
- tx(SCAN_RUNNER_TEXTS.activeProviderNoMarkerWarning, {
18556
- glyph: warnGlyph,
18557
- hint: dim(SCAN_RUNNER_TEXTS.activeProviderNoMarkerWarningHint)
18558
- })
18559
- );
18560
- return { kind: "ok", activeProvider: null, source: "none" };
18941
+ return { kind: "ok", activeProvider: MARKDOWN_LENS_ID, source: "default" };
18561
18942
  }
18562
18943
  if (detected.length === 1) {
18563
18944
  const picked2 = detected[0];
@@ -18588,7 +18969,7 @@ function aggregateDetected(cwd, effectiveRoots, cwdDetected, providers) {
18588
18969
  out.push(id);
18589
18970
  }
18590
18971
  for (const root of effectiveRoots) {
18591
- const absRoot = isAbsolute11(root) ? root : join15(cwd, root);
18972
+ const absRoot = isAbsolute12(root) ? root : join15(cwd, root);
18592
18973
  const r = resolveActiveProvider(absRoot, providers);
18593
18974
  for (const id of r.detected) {
18594
18975
  if (seen.has(id)) continue;
@@ -18620,7 +19001,13 @@ function handleDrift(opts, resolvedLens, currentMarkers) {
18620
19001
  backfillMarkersSnapshot(opts.cwd, currentMarkers);
18621
19002
  return;
18622
19003
  }
18623
- const diff = diffMarkers(snapshot, currentMarkers);
19004
+ const shipsDisabled = new Set(
19005
+ opts.providers.filter((p) => !installedDefaultEnabled(p.stability)).map((p) => p.id)
19006
+ );
19007
+ const diff = diffMarkers(
19008
+ snapshot.filter((id) => !shipsDisabled.has(id)),
19009
+ currentMarkers.filter((id) => !shipsDisabled.has(id))
19010
+ );
18624
19011
  if (diff.added.length === 0 && diff.removed.length === 0) return;
18625
19012
  emitDriftWarn(opts, resolvedLens, diff);
18626
19013
  }
@@ -18630,7 +19017,7 @@ function emitDriftWarn(opts, resolvedLens, diff) {
18630
19017
  const hint = tx(SCAN_RUNNER_TEXTS.activeProviderDriftWarnHint, {
18631
19018
  added: diff.added.length === 0 ? "(none)" : diff.added.join(", "),
18632
19019
  removed: diff.removed.length === 0 ? "(none)" : diff.removed.join(", "),
18633
- currentLens: resolvedLens ?? "(none)"
19020
+ currentLens: resolvedLens
18634
19021
  });
18635
19022
  opts.printer.warn(
18636
19023
  tx(SCAN_RUNNER_TEXTS.activeProviderDriftWarn, {
@@ -18662,7 +19049,6 @@ function diffMarkers(snapshot, current) {
18662
19049
  return { added, removed };
18663
19050
  }
18664
19051
  function warnIfLensPluginDisabled(args2) {
18665
- if (args2.activeProvider === null) return;
18666
19052
  if (args2.resolveEnabled(args2.activeProvider)) return;
18667
19053
  args2.printer.warn(
18668
19054
  tx(SCAN_RUNNER_TEXTS.activeProviderPluginDisabledWarning, {
@@ -18683,7 +19069,7 @@ async function promptForLens(detected, stdin, stderr, warnGlyph) {
18683
19069
  );
18684
19070
  }
18685
19071
  stderr.write(lines.join("\n") + "\n");
18686
- const rl = createInterface3({ input: stdin, output: stderr });
19072
+ const rl = createInterface4({ input: stdin, output: stderr });
18687
19073
  try {
18688
19074
  const answer = await new Promise(
18689
19075
  (resolveP) => rl.question(SCAN_RUNNER_TEXTS.activeProviderPromptInput, resolveP)
@@ -18701,8 +19087,8 @@ async function promptForLens(detected, stdin, stderr, warnGlyph) {
18701
19087
  }
18702
19088
 
18703
19089
  // core/sqlite/db-drift-reset.ts
18704
- import { existsSync as existsSync24 } from "fs";
18705
- import { createInterface as createInterface4 } from "readline";
19090
+ import { existsSync as existsSync25 } from "fs";
19091
+ import { createInterface as createInterface5 } from "readline";
18706
19092
  import { DatabaseSync as DatabaseSync8 } from "node:sqlite";
18707
19093
 
18708
19094
  // core/sqlite/i18n/db-drift.texts.ts
@@ -18753,7 +19139,7 @@ function detectDriftReason(dbPath, currentVersion) {
18753
19139
  return classifyFingerprint(dbPath).kind === "drift" ? "schema" : null;
18754
19140
  }
18755
19141
  function readScannedByVersion(dbPath) {
18756
- if (dbPath === ":memory:" || !existsSync24(dbPath)) return null;
19142
+ if (dbPath === ":memory:" || !existsSync25(dbPath)) return null;
18757
19143
  let raw = null;
18758
19144
  try {
18759
19145
  raw = new DatabaseSync8(dbPath, { readOnly: true });
@@ -18786,7 +19172,7 @@ async function askDriftReset(dbVersion, reason, policy) {
18786
19172
  hint: dim(DB_DRIFT_TEXTS.driftPromptHint)
18787
19173
  })
18788
19174
  );
18789
- const rl = createInterface4({ input: policy.stdin, output: policy.stderr });
19175
+ const rl = createInterface5({ input: policy.stdin, output: policy.stderr });
18790
19176
  try {
18791
19177
  const answer = await new Promise(
18792
19178
  (resolveP) => rl.question(DB_DRIFT_TEXTS.driftPromptQuestion, resolveP)
@@ -19145,7 +19531,7 @@ var INIT_TEXTS = {
19145
19531
  // cli/commands/init.ts
19146
19532
  var InitCommand = class extends SmCommand {
19147
19533
  static paths = [["init"]];
19148
- static usage = Command17.Usage({
19534
+ static usage = Command18.Usage({
19149
19535
  category: "Setup",
19150
19536
  description: "Bootstrap the current project: scaffold .skill-map/, provision DB, run first scan.",
19151
19537
  details: `
@@ -19164,16 +19550,16 @@ var InitCommand = class extends SmCommand {
19164
19550
  ["Preview what would be created", "$0 init --dry-run"]
19165
19551
  ]
19166
19552
  });
19167
- noScan = Option16.Boolean("--no-scan", false, {
19553
+ noScan = Option17.Boolean("--no-scan", false, {
19168
19554
  description: "Skip the first scan after scaffolding."
19169
19555
  });
19170
- force = Option16.Boolean("--force", false, {
19556
+ force = Option17.Boolean("--force", false, {
19171
19557
  description: "Overwrite an existing settings.json / settings.local.json / .skillmapignore."
19172
19558
  });
19173
- strict = Option16.Boolean("--strict", false, {
19559
+ strict = Option17.Boolean("--strict", false, {
19174
19560
  description: "Strict mode: fail on any layered-loader warning AND promote frontmatter warnings to errors during the first scan. Same flag as sm scan / sm config."
19175
19561
  });
19176
- dryRun = Option16.Boolean("-n,--dry-run", false, {
19562
+ dryRun = Option17.Boolean("-n,--dry-run", false, {
19177
19563
  description: "Preview the scope provisioning without touching the filesystem or the DB. Honours --force for the would-overwrite preview. Skips the first scan unconditionally; dry-run never persists."
19178
19564
  });
19179
19565
  // CLI orchestrator: paths setup + dry-run branch (delegated to
@@ -19426,7 +19812,7 @@ async function ensureGitignoreEntries(scopeRoot, entries) {
19426
19812
  }
19427
19813
 
19428
19814
  // cli/commands/history.ts
19429
- import { Command as Command18, Option as Option17 } from "clipanion";
19815
+ import { Command as Command19, Option as Option18 } from "clipanion";
19430
19816
 
19431
19817
  // cli/i18n/history.texts.ts
19432
19818
  var HISTORY_TEXTS = {
@@ -19544,7 +19930,7 @@ function parseStatuses(input, stderr, ansi) {
19544
19930
  }
19545
19931
  var HistoryCommand = class extends SmCommand {
19546
19932
  static paths = [["history"]];
19547
- static usage = Command18.Usage({
19933
+ static usage = Command19.Usage({
19548
19934
  category: "History",
19549
19935
  description: "Filter execution records. --json emits an array conforming to execution-record.schema.json.",
19550
19936
  details: `
@@ -19564,12 +19950,12 @@ var HistoryCommand = class extends SmCommand {
19564
19950
  ["Machine-readable, scoped to one node", "$0 history -n skills/foo.md --json"]
19565
19951
  ]
19566
19952
  });
19567
- node = Option17.String("-n", { required: false });
19568
- action = Option17.String("--action", { required: false });
19569
- status = Option17.String("--status", { required: false });
19570
- since = Option17.String("--since", { required: false });
19571
- until = Option17.String("--until", { required: false });
19572
- limit = Option17.String("--limit", { required: false });
19953
+ node = Option18.String("-n", { required: false });
19954
+ action = Option18.String("--action", { required: false });
19955
+ status = Option18.String("--status", { required: false });
19956
+ since = Option18.String("--since", { required: false });
19957
+ until = Option18.String("--until", { required: false });
19958
+ limit = Option18.String("--limit", { required: false });
19573
19959
  // CLI list verb: many optional filter flags (`--node`, `--action`,
19574
19960
  // `--status`, `--since`, `--until`, `--limit`, `--json`, `--quiet`)
19575
19961
  // each adding a guarded mutation to the filter or render path. Each
@@ -19620,7 +20006,7 @@ var HistoryCommand = class extends SmCommand {
19620
20006
  };
19621
20007
  var HistoryStatsCommand = class extends SmCommand {
19622
20008
  static paths = [["history", "stats"]];
19623
- static usage = Command18.Usage({
20009
+ static usage = Command19.Usage({
19624
20010
  category: "History",
19625
20011
  description: "Aggregate counts, tokens, periods, top nodes, and error rates over state_executions. --json conforms to history-stats.schema.json.",
19626
20012
  details: `
@@ -19638,10 +20024,10 @@ var HistoryStatsCommand = class extends SmCommand {
19638
20024
  ["Top 5 nodes, JSON", "$0 history stats --top 5 --json"]
19639
20025
  ]
19640
20026
  });
19641
- since = Option17.String("--since", { required: false });
19642
- until = Option17.String("--until", { required: false });
19643
- period = Option17.String("--period", { required: false });
19644
- top = Option17.String("--top", { required: false });
20027
+ since = Option18.String("--since", { required: false });
20028
+ until = Option18.String("--until", { required: false });
20029
+ period = Option18.String("--period", { required: false });
20030
+ top = Option18.String("--top", { required: false });
19645
20031
  // CLI stats verb: range parsing + window flags + period flag + JSON
19646
20032
  // branch + per-period iteration. Each branch is a single-purpose
19647
20033
  // gate; the data work lives in `aggregateHistoryStats`.
@@ -19942,20 +20328,20 @@ function trimMs(iso) {
19942
20328
 
19943
20329
  // cli/commands/jobs.ts
19944
20330
  import { unlink as unlink2 } from "fs/promises";
19945
- import { relative as relative6 } from "path";
19946
- import { Command as Command19, Option as Option18 } from "clipanion";
20331
+ import { relative as relative8 } from "path";
20332
+ import { Command as Command20, Option as Option19 } from "clipanion";
19947
20333
 
19948
20334
  // kernel/jobs/orphan-files.ts
19949
- import { readdirSync as readdirSync9, statSync as statSync8 } from "fs";
19950
- import { join as join17, resolve as resolve33 } from "path";
20335
+ import { readdirSync as readdirSync10, statSync as statSync9 } from "fs";
20336
+ import { join as join17, resolve as resolve34 } from "path";
19951
20337
  function findOrphanJobFiles(jobsDir, referencedPaths) {
19952
20338
  let entries;
19953
20339
  try {
19954
- const stat3 = statSync8(jobsDir);
20340
+ const stat3 = statSync9(jobsDir);
19955
20341
  if (!stat3.isDirectory()) {
19956
20342
  return { orphanFilePaths: [], referencedCount: referencedPaths.size };
19957
20343
  }
19958
- entries = readdirSync9(jobsDir, { withFileTypes: true });
20344
+ entries = readdirSync10(jobsDir, { withFileTypes: true });
19959
20345
  } catch {
19960
20346
  return { orphanFilePaths: [], referencedCount: referencedPaths.size };
19961
20347
  }
@@ -19965,7 +20351,7 @@ function findOrphanJobFiles(jobsDir, referencedPaths) {
19965
20351
  if (!entry.isFile()) continue;
19966
20352
  const name = entry.name;
19967
20353
  if (!name.endsWith(".md")) continue;
19968
- const abs = resolve33(join17(jobsDir, name));
20354
+ const abs = resolve34(join17(jobsDir, name));
19969
20355
  if (!referencedPaths.has(abs)) orphans.push(abs);
19970
20356
  }
19971
20357
  orphans.sort();
@@ -19994,7 +20380,7 @@ var JOBS_TEXTS = {
19994
20380
  // cli/commands/jobs.ts
19995
20381
  var JobPruneCommand = class extends SmCommand {
19996
20382
  static paths = [["job", "prune"]];
19997
- static usage = Command19.Usage({
20383
+ static usage = Command20.Usage({
19998
20384
  category: "Jobs",
19999
20385
  description: "Retention GC for completed / failed jobs (per config policy). --orphan-files removes MD files with no DB row.",
20000
20386
  details: `
@@ -20021,10 +20407,10 @@ var JobPruneCommand = class extends SmCommand {
20021
20407
  ["Preview without touching the DB", "$0 job prune --dry-run --json"]
20022
20408
  ]
20023
20409
  });
20024
- orphanFiles = Option18.Boolean("--orphan-files", false, {
20410
+ orphanFiles = Option19.Boolean("--orphan-files", false, {
20025
20411
  description: "Also remove MD files in .skill-map/jobs/ that have no matching state_jobs row."
20026
20412
  });
20027
- dryRun = Option18.Boolean("-n,--dry-run", false, {
20413
+ dryRun = Option19.Boolean("-n,--dry-run", false, {
20028
20414
  description: "Report what would be pruned without touching the DB or filesystem."
20029
20415
  });
20030
20416
  async run() {
@@ -20107,7 +20493,7 @@ var JobPruneCommand = class extends SmCommand {
20107
20493
  let removed = 0;
20108
20494
  for (const p of paths) {
20109
20495
  try {
20110
- assertContained(jobsDir, relative6(jobsDir, p));
20496
+ assertContained(jobsDir, relative8(jobsDir, p));
20111
20497
  } catch {
20112
20498
  continue;
20113
20499
  }
@@ -20162,7 +20548,7 @@ function formatPolicy(seconds) {
20162
20548
  }
20163
20549
 
20164
20550
  // cli/commands/list.ts
20165
- import { Command as Command20, Option as Option19 } from "clipanion";
20551
+ import { Command as Command21, Option as Option20 } from "clipanion";
20166
20552
 
20167
20553
  // cli/i18n/list.texts.ts
20168
20554
  var LIST_TEXTS = {
@@ -20197,7 +20583,7 @@ var SORT_BY = {
20197
20583
  var PATH_COL_MAX_WIDTH = 60;
20198
20584
  var ListCommand = class extends SmCommand {
20199
20585
  static paths = [["list"]];
20200
- static usage = Command20.Usage({
20586
+ static usage = Command21.Usage({
20201
20587
  category: "Browse",
20202
20588
  description: "Tabular listing of nodes. --json emits an array conforming to node.schema.json.",
20203
20589
  details: `
@@ -20221,11 +20607,11 @@ var ListCommand = class extends SmCommand {
20221
20607
  ["Filter by tag", "$0 list --tag urgent"]
20222
20608
  ]
20223
20609
  });
20224
- kind = Option19.String("--kind", { required: false });
20225
- issue = Option19.Boolean("--issue", false);
20226
- sortBy = Option19.String("--sort-by", { required: false });
20227
- limit = Option19.String("--limit", { required: false });
20228
- tag = Option19.String("--tag", { required: false });
20610
+ kind = Option20.String("--kind", { required: false });
20611
+ issue = Option20.Boolean("--issue", false);
20612
+ sortBy = Option20.String("--sort-by", { required: false });
20613
+ limit = Option20.String("--limit", { required: false });
20614
+ tag = Option20.String("--tag", { required: false });
20229
20615
  async run() {
20230
20616
  const stderrAnsi = this.ansiFor("stderr");
20231
20617
  const flags = this.#parseFlags(stderrAnsi);
@@ -20409,7 +20795,7 @@ function formatDataRow(r, w, ansi) {
20409
20795
  }
20410
20796
 
20411
20797
  // cli/commands/orphans.ts
20412
- import { Command as Command21, Option as Option20 } from "clipanion";
20798
+ import { Command as Command22, Option as Option21 } from "clipanion";
20413
20799
 
20414
20800
  // cli/i18n/orphans.texts.ts
20415
20801
  var ORPHANS_TEXTS = {
@@ -20492,7 +20878,7 @@ function isStringArray2(v) {
20492
20878
  }
20493
20879
  var OrphansCommand = class extends SmCommand {
20494
20880
  static paths = [["orphans"]];
20495
- static usage = Command21.Usage({
20881
+ static usage = Command22.Usage({
20496
20882
  category: "Browse",
20497
20883
  description: "List orphan / auto-rename issues from the last scan. --json emits an array conforming to issue.schema.json.",
20498
20884
  details: `
@@ -20507,7 +20893,7 @@ var OrphansCommand = class extends SmCommand {
20507
20893
  ["Just the ambiguous ones, JSON", "$0 orphans --kind ambiguous --json"]
20508
20894
  ]
20509
20895
  });
20510
- kind = Option20.String("--kind", { required: false });
20896
+ kind = Option21.String("--kind", { required: false });
20511
20897
  async run() {
20512
20898
  const stderrAnsi = this.ansiFor("stderr");
20513
20899
  let analyzerFilter = null;
@@ -20556,7 +20942,7 @@ var OrphansCommand = class extends SmCommand {
20556
20942
  };
20557
20943
  var OrphansReconcileCommand = class extends SmCommand {
20558
20944
  static paths = [["orphans", "reconcile"]];
20559
- static usage = Command21.Usage({
20945
+ static usage = Command22.Usage({
20560
20946
  category: "Browse",
20561
20947
  description: "Migrate state_* FKs from an orphan path to a live node, resolving the orphan issue.",
20562
20948
  details: `
@@ -20572,9 +20958,9 @@ var OrphansReconcileCommand = class extends SmCommand {
20572
20958
  ["Reattach orphan history", "$0 orphans reconcile skills/old.md --to skills/new.md"]
20573
20959
  ]
20574
20960
  });
20575
- orphanPath = Option20.String({ required: true });
20576
- to = Option20.String("--to", { required: true });
20577
- dryRun = Option20.Boolean("-n,--dry-run", false);
20961
+ orphanPath = Option21.String({ required: true });
20962
+ to = Option21.String("--to", { required: true });
20963
+ dryRun = Option21.Boolean("-n,--dry-run", false);
20578
20964
  async run() {
20579
20965
  const dbPath = resolveDbPath({ db: this.db, ...defaultRuntimeContext() });
20580
20966
  const exit = requireDbOrExit(dbPath, this.context.stderr);
@@ -20675,7 +21061,7 @@ var OrphansReconcileCommand = class extends SmCommand {
20675
21061
  };
20676
21062
  var OrphansUndoRenameCommand = class extends SmCommand {
20677
21063
  static paths = [["orphans", "undo-rename"]];
20678
- static usage = Command21.Usage({
21064
+ static usage = Command22.Usage({
20679
21065
  category: "Browse",
20680
21066
  description: "Reverse a medium- or ambiguous-confidence auto-rename. Migrates state_* FKs back, emits a new orphan on the prior path.",
20681
21067
  details: `
@@ -20695,10 +21081,10 @@ var OrphansUndoRenameCommand = class extends SmCommand {
20695
21081
  ["Undo an ambiguous, picking a candidate", "$0 orphans undo-rename skills/new.md --from skills/old-a.md"]
20696
21082
  ]
20697
21083
  });
20698
- newPath = Option20.String({ required: true });
20699
- from = Option20.String("--from", { required: false });
20700
- force = Option20.Boolean("--force", false);
20701
- dryRun = Option20.Boolean("-n,--dry-run", false);
21084
+ newPath = Option21.String({ required: true });
21085
+ from = Option21.String("--from", { required: false });
21086
+ force = Option21.Boolean("--force", false);
21087
+ dryRun = Option21.Boolean("-n,--dry-run", false);
20702
21088
  async run() {
20703
21089
  const dbPath = resolveDbPath({ db: this.db, ...defaultRuntimeContext() });
20704
21090
  const exit = requireDbOrExit(dbPath, this.context.stderr);
@@ -20921,7 +21307,7 @@ var ORPHANS_COMMANDS = [
20921
21307
  ];
20922
21308
 
20923
21309
  // cli/commands/plugins/list.ts
20924
- import { Command as Command22, Option as Option21 } from "clipanion";
21310
+ import { Command as Command23, Option as Option22 } from "clipanion";
20925
21311
 
20926
21312
  // cli/i18n/plugins.texts.ts
20927
21313
  var PLUGINS_TEXTS = {
@@ -21192,9 +21578,9 @@ function sortPluginsForPresentation(plugins) {
21192
21578
  }
21193
21579
 
21194
21580
  // cli/commands/plugins/shared.ts
21195
- import { resolve as resolve34 } from "path";
21581
+ import { resolve as resolve35 } from "path";
21196
21582
  function resolveSearchPaths2(opts, cwd) {
21197
- if (opts.pluginDir) return [resolve34(opts.pluginDir)];
21583
+ if (opts.pluginDir) return [resolve35(opts.pluginDir)];
21198
21584
  return [defaultProjectPluginsDir({ cwd })];
21199
21585
  }
21200
21586
  async function buildResolver() {
@@ -21312,7 +21698,7 @@ function wrapText(text, maxWidth) {
21312
21698
  // cli/commands/plugins/list.ts
21313
21699
  var PluginsListCommand = class extends SmCommand {
21314
21700
  static paths = [["plugins", "list"]];
21315
- static usage = Command22.Usage({
21701
+ static usage = Command23.Usage({
21316
21702
  category: "Plugins",
21317
21703
  description: "List discovered plugins, or one plugin's extensions.",
21318
21704
  details: `
@@ -21323,8 +21709,8 @@ var PluginsListCommand = class extends SmCommand {
21323
21709
  \`<plugin>/<ext>\` id is rejected with a redirect to \`sm plugins show\`.
21324
21710
  `
21325
21711
  });
21326
- id = Option21.String({ required: false });
21327
- pluginDir = Option21.String("--plugin-dir", { required: false });
21712
+ id = Option22.String({ required: false });
21713
+ pluginDir = Option22.String("--plugin-dir", { required: false });
21328
21714
  async run() {
21329
21715
  const plugins = await loadAll({ pluginDir: this.pluginDir });
21330
21716
  const resolveEnabled = await buildResolver();
@@ -21566,10 +21952,10 @@ function renderExtensionItems(items) {
21566
21952
  }
21567
21953
 
21568
21954
  // cli/commands/plugins/show.ts
21569
- import { Command as Command23, Option as Option22 } from "clipanion";
21955
+ import { Command as Command24, Option as Option23 } from "clipanion";
21570
21956
  var PluginsShowCommand = class extends SmCommand {
21571
21957
  static paths = [["plugins", "show"]];
21572
- static usage = Command23.Usage({
21958
+ static usage = Command24.Usage({
21573
21959
  category: "Plugins",
21574
21960
  description: "Show a single extension's detail.",
21575
21961
  details: `
@@ -21582,8 +21968,8 @@ var PluginsShowCommand = class extends SmCommand {
21582
21968
  accept resolves cleanly here too.
21583
21969
  `
21584
21970
  });
21585
- id = Option22.String({ required: true });
21586
- pluginDir = Option22.String("--plugin-dir", { required: false });
21971
+ id = Option23.String({ required: true });
21972
+ pluginDir = Option23.String("--plugin-dir", { required: false });
21587
21973
  async run() {
21588
21974
  const plugins = await loadAll({ pluginDir: this.pluginDir });
21589
21975
  const resolveEnabled = await buildResolver();
@@ -21724,7 +22110,7 @@ function renderExtensionFields(meta) {
21724
22110
  }
21725
22111
 
21726
22112
  // cli/commands/plugins/doctor.ts
21727
- import { Command as Command24, Option as Option23 } from "clipanion";
22113
+ import { Command as Command25, Option as Option24 } from "clipanion";
21728
22114
  var CONTRIB_ERROR_SAMPLE_CAP = 3;
21729
22115
  var STATUS_ORDER = [
21730
22116
  "enabled",
@@ -21737,12 +22123,12 @@ var STATUS_ORDER = [
21737
22123
  ];
21738
22124
  var PluginsDoctorCommand = class extends SmCommand {
21739
22125
  static paths = [["plugins", "doctor"]];
21740
- static usage = Command24.Usage({
22126
+ static usage = Command25.Usage({
21741
22127
  category: "Plugins",
21742
22128
  description: "Run the full load pass and summarise by failure mode.",
21743
22129
  details: "Exit code 0 when every plugin loads or is intentionally disabled; 1 when any plugin is in an error / incompat state."
21744
22130
  });
21745
- pluginDir = Option23.String("--plugin-dir", { required: false });
22131
+ pluginDir = Option24.String("--plugin-dir", { required: false });
21746
22132
  async run() {
21747
22133
  const plugins = await loadAll({ pluginDir: this.pluginDir });
21748
22134
  const resolveEnabled = await buildResolver();
@@ -22195,13 +22581,13 @@ function groupContributionErrorsByPlugin(errors) {
22195
22581
  }
22196
22582
 
22197
22583
  // cli/commands/plugins/toggle.ts
22198
- import { Command as Command25, Option as Option24 } from "clipanion";
22584
+ import { Command as Command26, Option as Option25 } from "clipanion";
22199
22585
  var TogglePluginsBase = class extends SmCommand {
22200
- all = Option24.Boolean("--all", false);
22201
- yes = Option24.Boolean("--yes,-y", false, {
22586
+ all = Option25.Boolean("--all", false);
22587
+ yes = Option25.Boolean("--yes,-y", false, {
22202
22588
  description: "Skip the interactive confirm when a bare plugin id (or --all) fans the toggle out across multiple extensions."
22203
22589
  });
22204
- ids = Option24.Rest({ name: "ids" });
22590
+ ids = Option25.Rest({ name: "ids" });
22205
22591
  async toggle(enabled) {
22206
22592
  const verb = enabled ? "enable" : "disable";
22207
22593
  const stderrAnsi = this.ansiFor("stderr");
@@ -22424,7 +22810,7 @@ async function purgeContributionsFor(adapter, id) {
22424
22810
  }
22425
22811
  var PluginsEnableCommand = class extends TogglePluginsBase {
22426
22812
  static paths = [["plugins", "enable"]];
22427
- static usage = Command25.Usage({
22813
+ static usage = Command26.Usage({
22428
22814
  category: "Plugins",
22429
22815
  description: "Enable one or more extensions (or --all). Persists in config_plugins.",
22430
22816
  details: `
@@ -22452,7 +22838,7 @@ var PluginsEnableCommand = class extends TogglePluginsBase {
22452
22838
  };
22453
22839
  var PluginsDisableCommand = class extends TogglePluginsBase {
22454
22840
  static paths = [["plugins", "disable"]];
22455
- static usage = Command25.Usage({
22841
+ static usage = Command26.Usage({
22456
22842
  category: "Plugins",
22457
22843
  description: "Disable one or more extensions (or --all). Persists in config_plugins; does not delete files.",
22458
22844
  details: `
@@ -22507,9 +22893,9 @@ function resolveBareToggle(id, catalogue) {
22507
22893
  }
22508
22894
 
22509
22895
  // cli/commands/plugins/create.ts
22510
- import { existsSync as existsSync25, mkdirSync as mkdirSync5, writeFileSync } from "fs";
22511
- import { dirname as dirname17, join as join18, resolve as resolve35 } from "path";
22512
- import { Command as Command26, Option as Option25 } from "clipanion";
22896
+ import { existsSync as existsSync26, mkdirSync as mkdirSync5, writeFileSync } from "fs";
22897
+ import { dirname as dirname18, join as join18, resolve as resolve36 } from "path";
22898
+ import { Command as Command27, Option as Option26 } from "clipanion";
22513
22899
 
22514
22900
  // cli/commands/plugins/scaffold/action.ts
22515
22901
  function indexStub(extId) {
@@ -22914,7 +23300,7 @@ function generateScaffold(kind, pluginId, specVersion) {
22914
23300
  // cli/commands/plugins/create.ts
22915
23301
  var PluginsCreateCommand = class extends SmCommand {
22916
23302
  static paths = [["plugins", "create"]];
22917
- static usage = Command26.Usage({
23303
+ static usage = Command27.Usage({
22918
23304
  category: "Plugins",
22919
23305
  description: "Scaffold a new plugin directory.",
22920
23306
  details: "Emits plugin.json + a per-kind extension stub + README. `<kind>` is one of: provider, extractor, analyzer, action, formatter, hook. The extractor stub ships one view contribution (slot `card.footer.left`) and one setting (`string-list`); edit to taste. Use `sm plugins slots list` to browse the slot / input-type catalog.",
@@ -22926,10 +23312,10 @@ var PluginsCreateCommand = class extends SmCommand {
22926
23312
  });
22927
23313
  // First positional: the extension kind (required). Declared before
22928
23314
  // `pluginId` so clipanion assigns it the first positional slot.
22929
- kind = Option25.String({ required: true, name: "kind" });
22930
- pluginId = Option25.String({ required: true, name: "plugin-id" });
22931
- at = Option25.String("--at", { required: false });
22932
- force = Option25.Boolean("--force", false);
23315
+ kind = Option26.String({ required: true, name: "kind" });
23316
+ pluginId = Option26.String({ required: true, name: "plugin-id" });
23317
+ at = Option26.String("--at", { required: false });
23318
+ force = Option26.Boolean("--force", false);
22933
23319
  async run() {
22934
23320
  const ansi = this.ansiFor("stderr");
22935
23321
  const errGlyph = ansi.red("\u2715");
@@ -22958,8 +23344,8 @@ var PluginsCreateCommand = class extends SmCommand {
22958
23344
  const kind = this.kind;
22959
23345
  const ctx = defaultRuntimeContext();
22960
23346
  const baseDir = defaultProjectPluginsDir(ctx);
22961
- const targetDir = this.at ? resolve35(this.at) : join18(baseDir, this.pluginId);
22962
- if (existsSync25(targetDir) && !this.force) {
23347
+ const targetDir = this.at ? resolve36(this.at) : join18(baseDir, this.pluginId);
23348
+ if (existsSync26(targetDir) && !this.force) {
22963
23349
  this.printer.error(
22964
23350
  tx(PLUGINS_TEXTS.createRefuseOverwrite, {
22965
23351
  glyph: errGlyph,
@@ -22973,7 +23359,7 @@ var PluginsCreateCommand = class extends SmCommand {
22973
23359
  const files = generateScaffold(kind, this.pluginId, specVersion);
22974
23360
  for (const file of files) {
22975
23361
  const abs = join18(targetDir, file.relPath);
22976
- mkdirSync5(dirname17(abs), { recursive: true });
23362
+ mkdirSync5(dirname18(abs), { recursive: true });
22977
23363
  writeFileSync(abs, file.contents);
22978
23364
  }
22979
23365
  const mainFile = `${kind}s/${this.pluginId}-${kind}/index.js`;
@@ -22988,7 +23374,7 @@ var PluginsCreateCommand = class extends SmCommand {
22988
23374
  };
22989
23375
 
22990
23376
  // cli/commands/plugins/slots.ts
22991
- import { Command as Command27 } from "clipanion";
23377
+ import { Command as Command28 } from "clipanion";
22992
23378
 
22993
23379
  // cli/commands/plugins/slots-catalog.ts
22994
23380
  var VIEW_SLOTS_CATALOG = [
@@ -23024,7 +23410,7 @@ var INPUT_TYPES_CATALOG = [
23024
23410
  // cli/commands/plugins/slots.ts
23025
23411
  var PluginsSlotsListCommand = class extends SmCommand {
23026
23412
  static paths = [["plugins", "slots", "list"]];
23027
- static usage = Command27.Usage({
23413
+ static usage = Command28.Usage({
23028
23414
  category: "Plugins",
23029
23415
  description: "Print the closed catalogs of view slots and input-types.",
23030
23416
  details: "Read-only. Use this when picking a slot / input-type for a new plugin."
@@ -23073,15 +23459,15 @@ var PluginsSlotsListCommand = class extends SmCommand {
23073
23459
  };
23074
23460
 
23075
23461
  // cli/commands/plugins/upgrade.ts
23076
- import { Command as Command28, Option as Option26 } from "clipanion";
23462
+ import { Command as Command29, Option as Option27 } from "clipanion";
23077
23463
  var PluginsUpgradeCommand = class extends SmCommand {
23078
23464
  static paths = [["plugins", "upgrade"]];
23079
- static usage = Command28.Usage({
23465
+ static usage = Command29.Usage({
23080
23466
  category: "Plugins",
23081
23467
  description: "Apply catalog migrations to plugin manifests.",
23082
23468
  details: "No migrations registered against catalog v1.0.0 yet; this verb is a no-op today. The structure exists so future slot renames / deprecations land without spec churn."
23083
23469
  });
23084
- pluginId = Option26.String({ required: false, name: "plugin-id" });
23470
+ pluginId = Option27.String({ required: false, name: "plugin-id" });
23085
23471
  async run() {
23086
23472
  this.printer.data(
23087
23473
  "sm plugins upgrade: no migrations registered for catalog v1.0.0.\n All loaded plugins are catalog-current.\n Run `sm plugins doctor` to surface any incompatible-catalog status.\n"
@@ -23091,7 +23477,7 @@ var PluginsUpgradeCommand = class extends SmCommand {
23091
23477
  };
23092
23478
 
23093
23479
  // cli/commands/plugins/config.ts
23094
- import { Command as Command29, Option as Option27 } from "clipanion";
23480
+ import { Command as Command30, Option as Option28 } from "clipanion";
23095
23481
 
23096
23482
  // cli/i18n/plugins-config.texts.ts
23097
23483
  var PLUGINS_CONFIG_TEXTS = {
@@ -23132,7 +23518,7 @@ var PLUGINS_CONFIG_TEXTS = {
23132
23518
  // cli/commands/plugins/config.ts
23133
23519
  var PluginsConfigCommand = class extends SmCommand {
23134
23520
  static paths = [["plugins", "config"]];
23135
- static usage = Command29.Usage({
23521
+ static usage = Command30.Usage({
23136
23522
  category: "Plugins",
23137
23523
  description: "Read or write an extension's declared settings.",
23138
23524
  details: `
@@ -23147,13 +23533,13 @@ var PluginsConfigCommand = class extends SmCommand {
23147
23533
  Secret values are shown as <redacted>. Run \`sm scan\` to apply.
23148
23534
  `
23149
23535
  });
23150
- id = Option27.String({ required: true });
23151
- settingId = Option27.String({ required: false });
23152
- value = Option27.String({ required: false });
23153
- reset = Option27.Boolean("--reset", false, {
23536
+ id = Option28.String({ required: true });
23537
+ settingId = Option28.String({ required: false });
23538
+ value = Option28.String({ required: false });
23539
+ reset = Option28.Boolean("--reset", false, {
23154
23540
  description: "Remove the override for <settingId> so the manifest default applies."
23155
23541
  });
23156
- pluginDir = Option27.String("--plugin-dir", { required: false });
23542
+ pluginDir = Option28.String("--plugin-dir", { required: false });
23157
23543
  // Read-only when listing; the write / reset paths emit their own
23158
23544
  // receipt. `sm config` exempts the config family from "done in <…>";
23159
23545
  // mirror that here for the read path. The write path keeps the line.
@@ -23474,8 +23860,8 @@ var PLUGIN_COMMANDS = [
23474
23860
 
23475
23861
  // cli/commands/refresh.ts
23476
23862
  import { readFile as readFile4 } from "fs/promises";
23477
- import { resolve as resolve36 } from "path";
23478
- import { Command as Command30, Option as Option28 } from "clipanion";
23863
+ import { resolve as resolve37 } from "path";
23864
+ import { Command as Command31, Option as Option29 } from "clipanion";
23479
23865
 
23480
23866
  // cli/i18n/refresh.texts.ts
23481
23867
  var REFRESH_TEXTS = {
@@ -23531,7 +23917,7 @@ var REFRESH_TEXTS = {
23531
23917
  // cli/commands/refresh.ts
23532
23918
  var RefreshCommand = class extends SmCommand {
23533
23919
  static paths = [["refresh"]];
23534
- static usage = Command30.Usage({
23920
+ static usage = Command31.Usage({
23535
23921
  category: "Scan",
23536
23922
  description: "Refresh enrichment rows: granular (single node) or batch (every stale row).",
23537
23923
  details: `
@@ -23553,11 +23939,11 @@ var RefreshCommand = class extends SmCommand {
23553
23939
  ["Refresh every node with stale enrichments", "$0 refresh --stale"]
23554
23940
  ]
23555
23941
  });
23556
- nodePath = Option28.String({ name: "node", required: false });
23557
- stale = Option28.Boolean("--stale", false, {
23942
+ nodePath = Option29.String({ name: "node", required: false });
23943
+ stale = Option29.Boolean("--stale", false, {
23558
23944
  description: "Refresh every node carrying a stale enrichment row (no-op in this revision; reserved for future Action-prob enrichments)."
23559
23945
  });
23560
- noPlugins = Option28.Boolean("--no-plugins", false, {
23946
+ noPlugins = Option29.Boolean("--no-plugins", false, {
23561
23947
  description: "Skip drop-in plugin discovery; use only the built-in extractor set."
23562
23948
  });
23563
23949
  // The remaining cyclomatic count comes from CLI ergonomics that don't
@@ -23782,7 +24168,7 @@ var RefreshCommand = class extends SmCommand {
23782
24168
  let body;
23783
24169
  try {
23784
24170
  assertContained(cwd, node.path);
23785
- const raw = await readFile4(resolve36(cwd, node.path), "utf8");
24171
+ const raw = await readFile4(resolve37(cwd, node.path), "utf8");
23786
24172
  body = stripFrontmatterFence(raw);
23787
24173
  } catch (err) {
23788
24174
  if (!this.json) {
@@ -23855,13 +24241,13 @@ var IntentionalFailCommand = class extends SmCommand {
23855
24241
  setTimeout(() => {
23856
24242
  throw new Error(INTENTIONAL_FAIL_TEXTS.errorMessage);
23857
24243
  }, 0);
23858
- await new Promise((resolve43) => setTimeout(resolve43, 5e3));
24244
+ await new Promise((resolve44) => setTimeout(resolve44, 5e3));
23859
24245
  return ExitCode.Issues;
23860
24246
  }
23861
24247
  };
23862
24248
 
23863
24249
  // cli/commands/scan.ts
23864
- import { Command as Command32, Option as Option30 } from "clipanion";
24250
+ import { Command as Command33, Option as Option31 } from "clipanion";
23865
24251
 
23866
24252
  // kernel/util/format-bytes.ts
23867
24253
  var UNITS = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"];
@@ -24020,10 +24406,10 @@ var SCAN_TEXTS = {
24020
24406
  };
24021
24407
 
24022
24408
  // cli/commands/watch.ts
24023
- import { Command as Command31, Option as Option29 } from "clipanion";
24409
+ import { Command as Command32, Option as Option30 } from "clipanion";
24024
24410
 
24025
24411
  // core/watcher/runtime.ts
24026
- import { dirname as dirname18, isAbsolute as isAbsolute12, relative as relative7, resolve as resolve37, sep as sep6 } from "path";
24412
+ import { dirname as dirname19, isAbsolute as isAbsolute13, relative as relative9, resolve as resolve38, sep as sep6 } from "path";
24027
24413
 
24028
24414
  // core/runtime/fresh-resolver.ts
24029
24415
  async function buildFreshResolver(deps) {
@@ -24069,7 +24455,7 @@ function applyPriorStateToRunOptions(runOptions, priorState, changedPaths) {
24069
24455
  }
24070
24456
  }
24071
24457
  function toIncrementalPaths(events, roots, cwd) {
24072
- const absRoots = roots.map((r) => isAbsolute12(r) ? r : resolve37(cwd, r));
24458
+ const absRoots = roots.map((r) => isAbsolute13(r) ? r : resolve38(cwd, r));
24073
24459
  const changed = /* @__PURE__ */ new Set();
24074
24460
  const removed = /* @__PURE__ */ new Set();
24075
24461
  for (const ev of events) {
@@ -24083,12 +24469,15 @@ function toIncrementalPaths(events, roots, cwd) {
24083
24469
  }
24084
24470
  function relativeFromRoots2(absolute, absRoots) {
24085
24471
  for (const root of absRoots) {
24086
- const rel = relative7(root, absolute);
24087
- if (rel === "" || rel.startsWith("..") || isAbsolute12(rel)) continue;
24472
+ const rel = relative9(root, absolute);
24473
+ if (rel === "" || rel.startsWith("..") || isAbsolute13(rel)) continue;
24088
24474
  return rel.split(sep6).join("/");
24089
24475
  }
24090
24476
  return null;
24091
24477
  }
24478
+ function resolveWatcherLens(cwd, composed) {
24479
+ return resolveActiveProvider(cwd, composed?.providers ?? []).resolved;
24480
+ }
24092
24481
  function createWatcherRuntime(opts) {
24093
24482
  const events = opts.events ?? {};
24094
24483
  const cwd = opts.runtimeContext.cwd;
@@ -24196,7 +24585,14 @@ function createWatcherRuntime(opts) {
24196
24585
  overrideScanCeiling: opts.maxScanOverride ?? null,
24197
24586
  maxRenderNodes: cfg.scan.maxNodes,
24198
24587
  overrideMaxRenderNodes: opts.maxNodesOverride ?? null,
24199
- maxFileSizeBytes: cfg.scan.maxFileSizeBytes
24588
+ maxFileSizeBytes: cfg.scan.maxFileSizeBytes,
24589
+ // Resolve the active lens from the persisted config (settings.json)
24590
+ // so a lens switched via `PATCH /api/active-provider` is honoured by
24591
+ // the next watcher batch. Without an explicit value the orchestrator
24592
+ // falls back to filesystem detection, which ignores the operator's
24593
+ // choice (e.g. selecting `markdown` while `.claude/` is still on disk
24594
+ // would re-detect `claude` and silently overwrite the chosen lens).
24595
+ activeProvider: resolveWatcherLens(cwd, composed)
24200
24596
  };
24201
24597
  if (cfg.scan.referencePaths.length > 0) {
24202
24598
  const walk3 = walkReferencePaths(cfg.scan.referencePaths, cwd);
@@ -24317,7 +24713,7 @@ function createWatcherRuntime(opts) {
24317
24713
  roots: [
24318
24714
  cwd,
24319
24715
  // parent of `.skillmapignore`
24320
- dirname18(settingsPath)
24716
+ dirname19(settingsPath)
24321
24717
  // parent of `.skill-map/settings.json`
24322
24718
  ],
24323
24719
  cwd,
@@ -24607,7 +25003,7 @@ async function runWatchLoop(opts) {
24607
25003
  }
24608
25004
  var WatchCommand = class extends SmCommand {
24609
25005
  static paths = [["watch"]];
24610
- static usage = Command31.Usage({
25006
+ static usage = Command32.Usage({
24611
25007
  category: "Scan",
24612
25008
  description: "Watch roots and run an incremental scan after each debounced batch of filesystem events.",
24613
25009
  details: `
@@ -24631,25 +25027,25 @@ var WatchCommand = class extends SmCommand {
24631
25027
  ["Stream ScanResult per batch as ndjson", "$0 watch --json"]
24632
25028
  ]
24633
25029
  });
24634
- roots = Option29.Rest({ name: "roots" });
24635
- noTokens = Option29.Boolean("--no-tokens", false, {
25030
+ roots = Option30.Rest({ name: "roots" });
25031
+ noTokens = Option30.Boolean("--no-tokens", false, {
24636
25032
  description: "Skip per-node token counts (cl100k_base BPE)."
24637
25033
  });
24638
- strict = Option29.Boolean("--strict", false, {
25034
+ strict = Option30.Boolean("--strict", false, {
24639
25035
  description: "Promote frontmatter-validation findings from warn to error inside each batch. Does not change the watcher exit code."
24640
25036
  });
24641
- noPlugins = Option29.Boolean("--no-plugins", false, {
25037
+ noPlugins = Option30.Boolean("--no-plugins", false, {
24642
25038
  description: "Skip drop-in plugin discovery for the watcher session."
24643
25039
  });
24644
- maxConsecutiveFailures = Option29.String("--max-consecutive-failures", {
25040
+ maxConsecutiveFailures = Option30.String("--max-consecutive-failures", {
24645
25041
  required: false,
24646
25042
  description: "Shut down with exit 2 after N consecutive batch failures (default 5; 0 disables the breaker)."
24647
25043
  });
24648
- maxScan = Option29.String("--max-scan", {
25044
+ maxScan = Option30.String("--max-scan", {
24649
25045
  required: false,
24650
25046
  description: "Per-batch override of scan.maxScan (default 50000), the WALK-INTAKE ceiling. The scan walks, parses, analyzes, and reference-validates the full corpus up to this number. Bidirectional: raises OR lowers the ceiling. When a batch hits it, additional files are dropped in stable order and the UI surfaces the persistent truncation banner. Validation: integer >= 1."
24651
25047
  });
24652
- maxNodes = Option29.String("--max-nodes", {
25048
+ maxNodes = Option30.String("--max-nodes", {
24653
25049
  required: false,
24654
25050
  description: "Per-batch override of scan.maxNodes (default 256), the MAP RENDER cap (pure metadata): it does NOT bound the scan, only the graph projection. Bidirectional: raises OR lowers the render cap. Validation: integer >= 1."
24655
25051
  });
@@ -24736,7 +25132,7 @@ function parseMaxNodesLimit(raw, stderr, noColor) {
24736
25132
  // cli/commands/scan.ts
24737
25133
  var ScanCommand = class extends SmCommand {
24738
25134
  static paths = [["scan"]];
24739
- static usage = Command32.Usage({
25135
+ static usage = Command33.Usage({
24740
25136
  category: "Scan",
24741
25137
  description: "Scan roots for markdown nodes, run extractors and analyzers.",
24742
25138
  details: `
@@ -24771,39 +25167,39 @@ var ScanCommand = class extends SmCommand {
24771
25167
  ["What would the next incremental scan persist?", "$0 scan --changed -n --json"]
24772
25168
  ]
24773
25169
  });
24774
- roots = Option30.Rest({ name: "roots" });
24775
- noBuiltIns = Option30.Boolean("--no-built-ins", false, {
25170
+ roots = Option31.Rest({ name: "roots" });
25171
+ noBuiltIns = Option31.Boolean("--no-built-ins", false, {
24776
25172
  description: "Skip the built-in extension set. Yields a zero-filled ScanResult (kernel-empty-boot parity); skips DB persistence."
24777
25173
  });
24778
- noPlugins = Option30.Boolean("--no-plugins", false, {
25174
+ noPlugins = Option31.Boolean("--no-plugins", false, {
24779
25175
  description: "Skip drop-in plugin discovery. Only the built-in set runs. Combine with --no-built-ins for a fully empty pipeline."
24780
25176
  });
24781
- noTokens = Option30.Boolean("--no-tokens", false, {
25177
+ noTokens = Option31.Boolean("--no-tokens", false, {
24782
25178
  description: "Skip per-node token counts (cl100k_base BPE). Leaves node.tokens undefined; spec-valid since the field is optional."
24783
25179
  });
24784
- dryRun = Option30.Boolean("-n,--dry-run", false, {
25180
+ dryRun = Option31.Boolean("-n,--dry-run", false, {
24785
25181
  description: "Run the scan in memory and skip every DB write. Combined with --changed, still opens the DB read-side to load the prior snapshot."
24786
25182
  });
24787
- changed = Option30.Boolean("--changed", false, {
25183
+ changed = Option31.Boolean("--changed", false, {
24788
25184
  description: "Incremental scan: reuse unchanged nodes from the persisted prior snapshot. Degrades to a full scan if no prior snapshot exists."
24789
25185
  });
24790
- allowEmpty = Option30.Boolean("--allow-empty", false, {
25186
+ allowEmpty = Option31.Boolean("--allow-empty", false, {
24791
25187
  description: "Allow a zero-result scan to wipe an already-populated DB (replace-all replace by zero rows). Off by default to avoid the typo-trap where an invalid root silently clears your data."
24792
25188
  });
24793
- strict = Option30.Boolean("--strict", false, {
25189
+ strict = Option31.Boolean("--strict", false, {
24794
25190
  description: "Promote frontmatter-validation findings from warn to error (exit code 1 on any violation). Overrides scan.strict from config when both are set."
24795
25191
  });
24796
- watch = Option30.Boolean("--watch", false, {
25192
+ watch = Option31.Boolean("--watch", false, {
24797
25193
  description: "Long-running mode: watch the roots and trigger an incremental scan after each debounced batch of filesystem events. Alias of `sm watch`."
24798
25194
  });
24799
- yes = Option30.Boolean("--yes", false, {
25195
+ yes = Option31.Boolean("--yes", false, {
24800
25196
  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."
24801
25197
  });
24802
- maxScan = Option30.String("--max-scan", {
25198
+ maxScan = Option31.String("--max-scan", {
24803
25199
  required: false,
24804
25200
  description: "Per-invocation override of `scan.maxScan` (default 50000). The WALK-INTAKE ceiling: the scan walks, parses, analyzes, and reference-validates the full corpus up to this number. Bidirectional: raises OR lowers the ceiling. When the walker hits it, additional files are dropped in stable order and the scan is marked truncated in scan_meta (the UI raises a persistent banner pointing at the .skillmapignore editor in Settings \u2192 Project). Validation: integer >= 1."
24805
25201
  });
24806
- maxNodes = Option30.String("--max-nodes", {
25202
+ maxNodes = Option31.String("--max-nodes", {
24807
25203
  required: false,
24808
25204
  description: "Per-invocation override of `scan.maxNodes` (default 256). The MAP RENDER cap (pure metadata): it does NOT bound the scan, only how many nodes the graph view projects onto the canvas. Bidirectional: raises OR lowers the render cap. Validation: integer >= 1."
24809
25205
  });
@@ -25164,10 +25560,10 @@ function capOverrides(caps) {
25164
25560
 
25165
25561
  // cli/commands/scan-compare.ts
25166
25562
  import { access, readFile as readFile5 } from "fs/promises";
25167
- import { Command as Command33, Option as Option31 } from "clipanion";
25563
+ import { Command as Command34, Option as Option32 } from "clipanion";
25168
25564
  var ScanCompareCommand = class extends SmCommand {
25169
25565
  static paths = [["scan", "compare-with"]];
25170
- static usage = Command33.Usage({
25566
+ static usage = Command34.Usage({
25171
25567
  category: "Scan",
25172
25568
  description: "Run a fresh scan in memory and emit a delta against the saved ScanResult dump at <dump>. Read-only.",
25173
25569
  details: `
@@ -25195,15 +25591,15 @@ var ScanCompareCommand = class extends SmCommand {
25195
25591
  ["JSON output for tooling", "$0 scan compare-with baseline.json --json"]
25196
25592
  ]
25197
25593
  });
25198
- dump = Option31.String({ required: true });
25199
- roots = Option31.Rest({ name: "roots" });
25200
- noTokens = Option31.Boolean("--no-tokens", false, {
25594
+ dump = Option32.String({ required: true });
25595
+ roots = Option32.Rest({ name: "roots" });
25596
+ noTokens = Option32.Boolean("--no-tokens", false, {
25201
25597
  description: "Skip per-node token counts during the fresh scan."
25202
25598
  });
25203
- strict = Option31.Boolean("--strict", false, {
25599
+ strict = Option32.Boolean("--strict", false, {
25204
25600
  description: "Promote layered-config warnings and frontmatter-validation findings from warn to error."
25205
25601
  });
25206
- noPlugins = Option31.Boolean("--no-plugins", false, {
25602
+ noPlugins = Option32.Boolean("--no-plugins", false, {
25207
25603
  description: "Skip drop-in plugin discovery."
25208
25604
  });
25209
25605
  // Cyclomatic count comes from CLI ergonomics: 3 distinct try/catch
@@ -25409,13 +25805,13 @@ function renderDeltaIssues(issues) {
25409
25805
 
25410
25806
  // cli/commands/serve.ts
25411
25807
  import { spawn as spawn2 } from "child_process";
25412
- import { existsSync as existsSync31 } from "fs";
25413
- import { Command as Command34, Option as Option32 } from "clipanion";
25808
+ import { existsSync as existsSync32 } from "fs";
25809
+ import { Command as Command35, Option as Option33 } from "clipanion";
25414
25810
 
25415
25811
  // kernel/util/dev-mode.ts
25416
25812
  import { sep as sep7 } from "path";
25417
- import { fileURLToPath as fileURLToPath5 } from "url";
25418
- var SELF_PATH = fileURLToPath5(import.meta.url);
25813
+ import { fileURLToPath as fileURLToPath6 } from "url";
25814
+ var SELF_PATH = fileURLToPath6(import.meta.url);
25419
25815
  var IS_DEV_BUILD = isDevBuildFromPath(SELF_PATH, sep7);
25420
25816
  function isDevBuildFromPath(filePath, separator = sep7) {
25421
25817
  return !filePath.includes(`${separator}node_modules${separator}`);
@@ -26371,7 +26767,7 @@ function contentTypeFor(format) {
26371
26767
  }
26372
26768
 
26373
26769
  // server/health.ts
26374
- import { existsSync as existsSync26 } from "fs";
26770
+ import { existsSync as existsSync27 } from "fs";
26375
26771
  var FALLBACK_SCHEMA_VERSION = "1";
26376
26772
  function buildHealth(deps) {
26377
26773
  const dev = isDevBuild();
@@ -26380,7 +26776,7 @@ function buildHealth(deps) {
26380
26776
  schemaVersion: FALLBACK_SCHEMA_VERSION,
26381
26777
  specVersion: deps.specVersion,
26382
26778
  implVersion: VERSION,
26383
- db: existsSync26(deps.dbPath) ? "present" : "missing",
26779
+ db: existsSync27(deps.dbPath) ? "present" : "missing",
26384
26780
  cwd: deps.cwd,
26385
26781
  dbPath: deps.dbPath,
26386
26782
  // Only emit when truthy so a published install keeps the wire
@@ -26513,9 +26909,9 @@ import { HTTPException as HTTPException8 } from "hono/http-exception";
26513
26909
  // server/node-body.ts
26514
26910
  import { constants as fsConstants2 } from "fs";
26515
26911
  import { open } from "fs/promises";
26516
- import { isAbsolute as isAbsolute13, resolve as resolvePath2, relative as relativePath, sep as sep8 } from "path";
26912
+ import { isAbsolute as isAbsolute14, resolve as resolvePath2, relative as relativePath, sep as sep8 } from "path";
26517
26913
  async function readNodeBody(cwd, relPath) {
26518
- if (isAbsolute13(relPath)) return null;
26914
+ if (isAbsolute14(relPath)) return null;
26519
26915
  const absRoot = resolvePath2(cwd);
26520
26916
  const absFile = resolvePath2(absRoot, relPath);
26521
26917
  const rel = relativePath(absRoot, absFile);
@@ -27502,12 +27898,12 @@ var parsePatchBody2 = makeBodyValidator(PATCH_BODY_SCHEMA, {
27502
27898
  import { HTTPException as HTTPException12 } from "hono/http-exception";
27503
27899
 
27504
27900
  // server/util/skillmapignore-io.ts
27505
- import { existsSync as existsSync27, readFileSync as readFileSync18, writeFileSync as writeFileSync2 } from "fs";
27506
- import { resolve as resolve38 } from "path";
27901
+ import { existsSync as existsSync28, readFileSync as readFileSync18, writeFileSync as writeFileSync2 } from "fs";
27902
+ import { resolve as resolve39 } from "path";
27507
27903
  var IGNORE_FILENAME2 = ".skillmapignore";
27508
27904
  function readPatterns(cwd) {
27509
- const path = resolve38(cwd, IGNORE_FILENAME2);
27510
- if (!existsSync27(path)) return [];
27905
+ const path = resolve39(cwd, IGNORE_FILENAME2);
27906
+ if (!existsSync28(path)) return [];
27511
27907
  let raw;
27512
27908
  try {
27513
27909
  raw = readFileSync18(path, "utf8");
@@ -27517,8 +27913,8 @@ function readPatterns(cwd) {
27517
27913
  return raw.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
27518
27914
  }
27519
27915
  function writePatterns(cwd, nextPatterns) {
27520
- const path = resolve38(cwd, IGNORE_FILENAME2);
27521
- const prior = existsSync27(path) ? safeRead(path) : "";
27916
+ const path = resolve39(cwd, IGNORE_FILENAME2);
27917
+ const prior = existsSync28(path) ? safeRead(path) : "";
27522
27918
  const content = buildContent(prior, nextPatterns);
27523
27919
  writeFileSync2(path, content, "utf8");
27524
27920
  }
@@ -27679,7 +28075,7 @@ var parsePatchBody3 = makeBodyValidator(PATCH_BODY_SCHEMA2, {
27679
28075
  });
27680
28076
 
27681
28077
  // server/routes/project-preferences.ts
27682
- import { statSync as statSync9 } from "fs";
28078
+ import { statSync as statSync10 } from "fs";
27683
28079
  import { HTTPException as HTTPException13 } from "hono/http-exception";
27684
28080
  function registerProjectPreferencesRoute(app, deps) {
27685
28081
  app.get("/api/project-preferences", (c) => {
@@ -27855,7 +28251,7 @@ function formatPathDetail(path, cwd) {
27855
28251
  function isExistingDirectory(entry, cwd) {
27856
28252
  const abs = resolveScanPath(entry, cwd);
27857
28253
  try {
27858
- return statSync9(abs).isDirectory();
28254
+ return statSync10(abs).isDirectory();
27859
28255
  } catch {
27860
28256
  return false;
27861
28257
  }
@@ -27897,7 +28293,7 @@ var parsePatchBody4 = makeBodyValidator(PATCH_BODY_SCHEMA3, {
27897
28293
  });
27898
28294
 
27899
28295
  // server/routes/active-provider.ts
27900
- import { existsSync as existsSync28 } from "fs";
28296
+ import { existsSync as existsSync29 } from "fs";
27901
28297
  import { HTTPException as HTTPException14 } from "hono/http-exception";
27902
28298
  function registerActiveProviderRoute(app, deps) {
27903
28299
  app.get("/api/active-provider", async (c) => {
@@ -27927,7 +28323,6 @@ async function resolveSelectableProviders(deps) {
27927
28323
  });
27928
28324
  const selectable = /* @__PURE__ */ new Set();
27929
28325
  for (const provider of deps.providers) {
27930
- if (provider.presentation?.comingSoon === true) continue;
27931
28326
  if (isPluginExtensionEnabled(provider, resolveEnabled)) {
27932
28327
  selectable.add(provider.id);
27933
28328
  }
@@ -27946,7 +28341,7 @@ function applyLensSwitch(deps, newValue) {
27946
28341
  });
27947
28342
  }
27948
28343
  const dbPath = resolveDbPath({ db: void 0, cwd });
27949
- if (!existsSync28(dbPath)) return { dropped: null };
28344
+ if (!existsSync29(dbPath)) return { dropped: null };
27950
28345
  const dropResult = dropScanZone(dbPath);
27951
28346
  return {
27952
28347
  dropped: {
@@ -27976,7 +28371,7 @@ var parsePatchBody5 = makeBodyValidator(PATCH_BODY_SCHEMA4, {
27976
28371
 
27977
28372
  // server/routes/actions.ts
27978
28373
  import { HTTPException as HTTPException16 } from "hono/http-exception";
27979
- import { resolve as resolve39 } from "path";
28374
+ import { resolve as resolve40 } from "path";
27980
28375
 
27981
28376
  // server/routes/node-loader.ts
27982
28377
  import { HTTPException as HTTPException15 } from "hono/http-exception";
@@ -28036,7 +28431,7 @@ function registerActionsRoutes(app, deps) {
28036
28431
  let absPath;
28037
28432
  try {
28038
28433
  assertContained(deps.runtimeContext.cwd, node.path);
28039
- absPath = resolve39(deps.runtimeContext.cwd, node.path);
28434
+ absPath = resolve40(deps.runtimeContext.cwd, node.path);
28040
28435
  } catch (err) {
28041
28436
  throw new HTTPException16(400, { message: formatErrorMessage(err) });
28042
28437
  }
@@ -28144,14 +28539,14 @@ async function withScanMutex(fn) {
28144
28539
  if (inFlight !== null) {
28145
28540
  throw new ScanBusyError();
28146
28541
  }
28147
- let resolve43;
28542
+ let resolve44;
28148
28543
  inFlight = new Promise((r) => {
28149
- resolve43 = r;
28544
+ resolve44 = r;
28150
28545
  });
28151
28546
  try {
28152
28547
  return await fn();
28153
28548
  } finally {
28154
- resolve43();
28549
+ resolve44();
28155
28550
  inFlight = null;
28156
28551
  }
28157
28552
  }
@@ -28667,7 +29062,7 @@ function registerUpdateStatusRoute(app, deps) {
28667
29062
  }
28668
29063
 
28669
29064
  // server/static.ts
28670
- import { existsSync as existsSync29 } from "fs";
29065
+ import { existsSync as existsSync30 } from "fs";
28671
29066
  import { readFile as readFile6 } from "fs/promises";
28672
29067
  import { extname, join as join19 } from "path";
28673
29068
  import { serveStatic } from "@hono/node-server/serve-static";
@@ -28722,7 +29117,7 @@ function createSpaFallback(opts) {
28722
29117
  if (c.req.method !== "GET" && c.req.method !== "HEAD") return c.notFound();
28723
29118
  if (opts.uiDist === null) return htmlResponse(c, placeholder);
28724
29119
  const indexPath = join19(opts.uiDist, INDEX_HTML);
28725
- if (!existsSync29(indexPath)) return htmlResponse(c, placeholder);
29120
+ if (!existsSync30(indexPath)) return htmlResponse(c, placeholder);
28726
29121
  return fileResponse(c, indexPath);
28727
29122
  };
28728
29123
  }
@@ -29271,7 +29666,6 @@ function buildProviderRegistry(providers) {
29271
29666
  if (ui.emoji !== void 0) entry.emoji = ui.emoji;
29272
29667
  if (ui.icon !== void 0) entry.icon = ui.icon;
29273
29668
  if (ui.hideChip !== void 0) entry.hideChip = ui.hideChip;
29274
- if (ui.comingSoon !== void 0) entry.comingSoon = ui.comingSoon;
29275
29669
  registry[provider.id] = entry;
29276
29670
  }
29277
29671
  return registry;
@@ -29449,9 +29843,9 @@ function validateNoUi(noUi, uiDist) {
29449
29843
  }
29450
29844
 
29451
29845
  // server/paths.ts
29452
- import { existsSync as existsSync30, statSync as statSync10 } from "fs";
29453
- import { dirname as dirname19, isAbsolute as isAbsolute14, join as join20, resolve as resolve40 } from "path";
29454
- import { fileURLToPath as fileURLToPath6 } from "url";
29846
+ import { existsSync as existsSync31, statSync as statSync11 } from "fs";
29847
+ import { dirname as dirname20, isAbsolute as isAbsolute15, join as join20, resolve as resolve41 } from "path";
29848
+ import { fileURLToPath as fileURLToPath7 } from "url";
29455
29849
  var DEFAULT_UI_REL = join20("ui", "dist", "ui", "browser");
29456
29850
  var PACKAGE_UI_REL = "ui";
29457
29851
  var INDEX_HTML2 = "index.html";
@@ -29461,13 +29855,13 @@ function resolveDefaultUiDist(ctx) {
29461
29855
  return walkUpForUi(ctx.cwd);
29462
29856
  }
29463
29857
  function resolveExplicitUiDist(ctx, raw) {
29464
- return isAbsolute14(raw) ? raw : resolve40(ctx.cwd, raw);
29858
+ return isAbsolute15(raw) ? raw : resolve41(ctx.cwd, raw);
29465
29859
  }
29466
29860
  function isUiBundleDir(path) {
29467
- if (!existsSync30(path)) return false;
29861
+ if (!existsSync31(path)) return false;
29468
29862
  try {
29469
- if (!statSync10(path).isDirectory()) return false;
29470
- return existsSync30(join20(path, INDEX_HTML2));
29863
+ if (!statSync11(path).isDirectory()) return false;
29864
+ return existsSync31(join20(path, INDEX_HTML2));
29471
29865
  } catch {
29472
29866
  return false;
29473
29867
  }
@@ -29475,7 +29869,7 @@ function isUiBundleDir(path) {
29475
29869
  function resolvePackageBundledUi() {
29476
29870
  let here;
29477
29871
  try {
29478
- here = dirname19(fileURLToPath6(import.meta.url));
29872
+ here = dirname20(fileURLToPath7(import.meta.url));
29479
29873
  } catch {
29480
29874
  return null;
29481
29875
  }
@@ -29488,18 +29882,18 @@ function resolvePackageBundledUiFrom(here) {
29488
29882
  if (isUiBundleDir(candidate)) return candidate;
29489
29883
  const distHere = join20(current, "dist", PACKAGE_UI_REL);
29490
29884
  if (isUiBundleDir(distHere)) return distHere;
29491
- const parent = dirname19(current);
29885
+ const parent = dirname20(current);
29492
29886
  if (parent === current) return null;
29493
29887
  current = parent;
29494
29888
  }
29495
29889
  return null;
29496
29890
  }
29497
29891
  function walkUpForUi(startDir) {
29498
- let current = resolve40(startDir);
29892
+ let current = resolve41(startDir);
29499
29893
  for (let i = 0; i < 64; i++) {
29500
29894
  const candidate = join20(current, DEFAULT_UI_REL);
29501
29895
  if (isUiBundleDir(candidate)) return candidate;
29502
- const parent = dirname19(current);
29896
+ const parent = dirname20(current);
29503
29897
  if (parent === current) return null;
29504
29898
  current = parent;
29505
29899
  }
@@ -29795,169 +30189,10 @@ var SERVE_TEXTS = {
29795
30189
  driftDeclined: "{{glyph}} sm serve: cache rebuild declined; the {{dbVersion}} cache cannot be reused on {{currentVersion}} ({{reason}}).\n {{hint}}\n"
29796
30190
  };
29797
30191
 
29798
- // cli/util/serve-banner.ts
29799
- import { relative as relative8, isAbsolute as isAbsolute15 } from "path";
29800
- var ESC2 = {
29801
- reset: "\x1B[0m",
29802
- bold: "\x1B[1m",
29803
- dim: "\x1B[2m",
29804
- underline: "\x1B[4m",
29805
- /** 256-color violet (xterm 141). */
29806
- violet: "\x1B[38;5;141m",
29807
- /** 256-color green (xterm 42). */
29808
- green: "\x1B[38;5;42m",
29809
- /** 256-color yellow (xterm 214), matches `cli/util/ansi.ts:yellow`. */
29810
- yellow: "\x1B[38;5;214m"
29811
- };
29812
- var LOGO_LINES = [
29813
- " ____ _ _ _ _ __ __ ",
29814
- " / ___|| | _(_) | | | \\/ | __ _ _ __ ",
29815
- " \\___ \\| |/ / | | | | |\\/| |/ _` | '_ \\ ",
29816
- " ___) | <| | | | | | | | (_| | |_) |",
29817
- " |____/|_|\\_\\_|_|_| |_| |_|\\__,_| .__/ ",
29818
- " |_| "
29819
- ];
29820
- var LOGO_WIDTH = 40;
29821
- function renderBanner(input) {
29822
- const url = `http://${input.host}:${input.port}`;
29823
- const dbDisplay = formatDbPath(input.dbPath, input.cwd);
29824
- const browserLine = input.openBrowser ? "Opening browser\u2026 Press Ctrl+C to stop." : `Visit ${url}/ in your browser. Press Ctrl+C to stop.`;
29825
- if (!input.isTTY) {
29826
- return renderFlat({
29827
- host: input.host,
29828
- port: input.port,
29829
- dbPath: input.dbPath,
29830
- openBrowser: input.openBrowser,
29831
- dev: input.dev === true
29832
- });
29833
- }
29834
- return renderFiglet({
29835
- version: input.version,
29836
- url,
29837
- dbDisplay,
29838
- pathDisplay: formatCwdPath(input.cwd),
29839
- browserLine,
29840
- colorEnabled: input.colorEnabled,
29841
- referencePaths: input.referencePaths ?? [],
29842
- dev: input.dev === true
29843
- });
29844
- }
29845
- function resolveColorEnabled(opts) {
29846
- if (opts.noColorFlag) return false;
29847
- const noColor = opts.env["NO_COLOR"];
29848
- if (noColor !== void 0 && noColor !== "") return false;
29849
- const forceColor = opts.env["FORCE_COLOR"];
29850
- if (forceColor !== void 0 && forceColor !== "") return true;
29851
- return opts.isTTY;
29852
- }
29853
- function renderFlat(input) {
29854
- const safeHost = sanitizeForTerminal(input.host);
29855
- const safeDb = sanitizeForTerminal(input.dbPath);
29856
- const url = `http://${safeHost}:${input.port}`;
29857
- const devSuffix = input.dev ? " [dev]" : "";
29858
- const linesOut = [];
29859
- linesOut.push(`sm serve${devSuffix}: listening on ${url} (db=${safeDb})`);
29860
- if (input.openBrowser) {
29861
- linesOut.push(`sm serve: opening ${url}/ in your browser. Press Ctrl+C to stop.`);
29862
- } else {
29863
- linesOut.push(`sm serve: visit ${url}/ in your browser. Press Ctrl+C to stop.`);
29864
- }
29865
- return linesOut.join("\n") + "\n";
29866
- }
29867
- function renderLogoBlock(input) {
29868
- const { dimOpen, dimClose, violetOpen, violetClose } = resolveAnsi(input.colorEnabled);
29869
- const logoLines = LOGO_LINES.map((line) => `${violetOpen}${line}${violetClose}`);
29870
- const versionText = `v${input.version}`;
29871
- const versionPad = Math.max(0, LOGO_WIDTH - versionText.length);
29872
- const versionLine = `${" ".repeat(versionPad)}${dimOpen}${versionText}${dimClose}`;
29873
- return `${logoLines.join("\n")}
29874
-
29875
- ${versionLine}
29876
-
29877
- `;
29878
- }
29879
- function renderFiglet(input) {
29880
- const {
29881
- dimOpen,
29882
- dimClose,
29883
- greenUnderline,
29884
- greenUnderlineClose,
29885
- violetOpen,
29886
- violetClose,
29887
- yellowOpen,
29888
- yellowClose
29889
- } = resolveAnsi(input.colorEnabled);
29890
- const logoLines = LOGO_LINES.map((line) => `${violetOpen}${line}${violetClose}`);
29891
- const versionText = `v${input.version}`;
29892
- const devText = "[dev]";
29893
- const versionWidth = input.dev ? devText.length : versionText.length;
29894
- const versionPad = Math.max(0, LOGO_WIDTH - versionWidth);
29895
- const versionLine = input.dev ? `${" ".repeat(versionPad)}${yellowOpen}${devText}${yellowClose}` : `${" ".repeat(versionPad)}${dimOpen}${versionText}${dimClose}`;
29896
- const lines = [];
29897
- lines.push(...logoLines);
29898
- lines.push("");
29899
- lines.push(versionLine);
29900
- lines.push("");
29901
- lines.push(` ${dimOpen}Server${dimClose} ${greenUnderline}${input.url}${greenUnderlineClose}`);
29902
- lines.push(` ${dimOpen}Path${dimClose} ${input.pathDisplay}`);
29903
- lines.push(` ${dimOpen}DB${dimClose} ${input.dbDisplay}`);
29904
- lines.push(...renderListRows("Refs", input.referencePaths, dimOpen, dimClose));
29905
- lines.push("");
29906
- lines.push(` ${dimOpen}${input.browserLine}${dimClose}`);
29907
- lines.push("");
29908
- return lines.join("\n") + "\n";
29909
- }
29910
- function renderListRows(label, values, dimOpen, dimClose) {
29911
- if (values.length === 0) return [];
29912
- const out = [];
29913
- const labelPad = " ".repeat(Math.max(1, 9 - label.length));
29914
- const continuationPad = " ".repeat(11);
29915
- out.push(` ${dimOpen}${label}${dimClose}${labelPad}${sanitizeForTerminal(values[0])}`);
29916
- for (let i = 1; i < values.length; i += 1) {
29917
- out.push(`${continuationPad}${sanitizeForTerminal(values[i])}`);
29918
- }
29919
- return out;
29920
- }
29921
- var EMPTY_ANSI = {
29922
- dimOpen: "",
29923
- dimClose: "",
29924
- greenUnderline: "",
29925
- greenUnderlineClose: "",
29926
- violetOpen: "",
29927
- violetClose: "",
29928
- yellowOpen: "",
29929
- yellowClose: ""
29930
- };
29931
- var ENABLED_ANSI = {
29932
- dimOpen: ESC2.dim,
29933
- dimClose: ESC2.reset,
29934
- greenUnderline: `${ESC2.green}${ESC2.underline}`,
29935
- greenUnderlineClose: ESC2.reset,
29936
- violetOpen: ESC2.violet,
29937
- violetClose: ESC2.reset,
29938
- yellowOpen: ESC2.yellow,
29939
- yellowClose: ESC2.reset
29940
- };
29941
- function resolveAnsi(colorEnabled) {
29942
- return colorEnabled ? ENABLED_ANSI : EMPTY_ANSI;
29943
- }
29944
- function formatDbPath(dbPath, cwd) {
29945
- const safe = sanitizeForTerminal(dbPath);
29946
- if (!isAbsolute15(safe)) return safe;
29947
- const rel = relative8(cwd, safe);
29948
- if (rel === "" || rel.startsWith("..") || isAbsolute15(rel)) {
29949
- return safe;
29950
- }
29951
- return rel;
29952
- }
29953
- function formatCwdPath(cwd) {
29954
- return sanitizeForTerminal(cwd);
29955
- }
29956
-
29957
30192
  // cli/commands/serve.ts
29958
30193
  var ServeCommand = class extends SmCommand {
29959
30194
  static paths = [["serve"]];
29960
- static usage = Command34.Usage({
30195
+ static usage = Command35.Usage({
29961
30196
  category: "Setup",
29962
30197
  description: "Start the Hono BFF (single-port: REST + WebSocket + SPA bundle).",
29963
30198
  details: `
@@ -29981,18 +30216,18 @@ var ServeCommand = class extends SmCommand {
29981
30216
  ["Point at a pre-built UI bundle", "$0 serve --ui-dist ./ui/dist/browser"]
29982
30217
  ]
29983
30218
  });
29984
- port = Option32.String("--port", {
30219
+ port = Option33.String("--port", {
29985
30220
  required: false,
29986
30221
  description: "Listening port (default 4242). 0 = OS-assigned."
29987
30222
  });
29988
- host = Option32.String("--host", {
30223
+ host = Option33.String("--host", {
29989
30224
  required: false,
29990
30225
  description: "Listening host (default 127.0.0.1). Loopback-only enforced when --dev-cors is set."
29991
30226
  });
29992
- noBuiltIns = Option32.Boolean("--no-built-ins", false, {
30227
+ noBuiltIns = Option33.Boolean("--no-built-ins", false, {
29993
30228
  description: "Skip built-in plugin registration (parity with sm scan --no-built-ins)."
29994
30229
  });
29995
- noPlugins = Option32.Boolean("--no-plugins", false, {
30230
+ noPlugins = Option33.Boolean("--no-plugins", false, {
29996
30231
  description: "Skip drop-in plugin discovery."
29997
30232
  });
29998
30233
  // `Option.Boolean('--open', true)`, Clipanion's parser auto-derives
@@ -30002,35 +30237,35 @@ var ServeCommand = class extends SmCommand {
30002
30237
  // two registrations for the same flag and rejects the invocation
30003
30238
  // with "Ambiguous Syntax Error". Same convention shipped by every
30004
30239
  // other `--no-...` flag in the CLI tree.
30005
- open = Option32.Boolean("--open", true, {
30240
+ open = Option33.Boolean("--open", true, {
30006
30241
  description: "Auto-open the SPA in the user's default browser after listen. --no-open opts out."
30007
30242
  });
30008
- devCors = Option32.Boolean("--dev-cors", false, {
30243
+ devCors = Option33.Boolean("--dev-cors", false, {
30009
30244
  description: "Enable permissive CORS for the Angular dev-server proxy workflow."
30010
30245
  });
30011
30246
  // `--ui-dist` is intentionally undocumented in the Usage block above
30012
30247
  // (the demo build pipeline + tests rely on it; everyday users never
30013
30248
  // need it). Clipanion still exposes it on the parser; the Usage
30014
30249
  // omission is the "hidden" contract per the 14.1 brief.
30015
- uiDist = Option32.String("--ui-dist", { required: false, hidden: true });
30016
- noUi = Option32.Boolean("--no-ui", false, {
30250
+ uiDist = Option33.String("--ui-dist", { required: false, hidden: true });
30251
+ noUi = Option33.Boolean("--no-ui", false, {
30017
30252
  description: "Don't serve the Angular UI bundle. Use this when running the BFF alongside `ui:dev` (Angular dev server with HMR). The root `/` then renders an inline placeholder pointing the user at the dev server."
30018
30253
  });
30019
- noWatcher = Option32.Boolean("--no-watcher", false, {
30254
+ noWatcher = Option33.Boolean("--no-watcher", false, {
30020
30255
  description: "Disable the chokidar-fed scan-and-broadcast loop. Use only for CI / read-only deployments."
30021
30256
  });
30022
- yes = Option32.Boolean("--yes", false, {
30257
+ yes = Option33.Boolean("--yes", false, {
30023
30258
  description: "Skip the interactive prompt and rebuild the local cache when the on-disk DB has drifted (version skew or an inline schema change). Non-TTY invocations rebuild without asking regardless of this flag."
30024
30259
  });
30025
30260
  // `--watcher-debounce-ms` is undocumented sugar for advanced users
30026
30261
  // who want to tighten / relax the watcher's batching window without
30027
30262
  // editing settings.json. Hidden flag, the Usage block omits it.
30028
- watcherDebounceMs = Option32.String("--watcher-debounce-ms", { required: false, hidden: true });
30029
- maxScan = Option32.String("--max-scan", {
30263
+ watcherDebounceMs = Option33.String("--watcher-debounce-ms", { required: false, hidden: true });
30264
+ maxScan = Option33.String("--max-scan", {
30030
30265
  required: false,
30031
30266
  description: "Per-invocation override of scan.maxScan (default 50000), the WALK-INTAKE ceiling. The scan walks, parses, analyzes, and reference-validates the full corpus up to this number. Bidirectional: raises OR lowers the ceiling. Applies to every scan the server runs (initial watcher pass, debounced batches, POST /api/scan, GET /api/scan?fresh=1). Same flag is honoured on the bare `sm` invocation, which routes to `sm serve`."
30032
30267
  });
30033
- maxNodes = Option32.String("--max-nodes", {
30268
+ maxNodes = Option33.String("--max-nodes", {
30034
30269
  required: false,
30035
30270
  description: "Per-invocation override of scan.maxNodes (default 256), the MAP RENDER cap (pure metadata): it does NOT bound the scan, only how many nodes the graph view projects onto the canvas. Bidirectional: raises OR lowers the render cap. Same flag is honoured on the bare `sm` invocation, which routes to `sm serve`."
30036
30271
  });
@@ -30060,7 +30295,7 @@ var ServeCommand = class extends SmCommand {
30060
30295
  return ExitCode.Error;
30061
30296
  }
30062
30297
  const dbPath = resolveDbPath({ db: this.db, ...runtimeCtx });
30063
- if (this.db !== void 0 && !existsSync31(dbPath)) {
30298
+ if (this.db !== void 0 && !existsSync32(dbPath)) {
30064
30299
  this.printer.info(
30065
30300
  tx(SERVE_TEXTS.dbNotFound, {
30066
30301
  glyph: errGlyph,
@@ -30400,7 +30635,7 @@ function tryOpenBrowser(url, stderr, warnGlyph) {
30400
30635
  }
30401
30636
 
30402
30637
  // cli/commands/show.ts
30403
- import { Command as Command35, Option as Option33 } from "clipanion";
30638
+ import { Command as Command36, Option as Option34 } from "clipanion";
30404
30639
 
30405
30640
  // cli/i18n/show.texts.ts
30406
30641
  var SHOW_TEXTS = {
@@ -30451,7 +30686,7 @@ var SHOW_TEXTS = {
30451
30686
  // cli/commands/show.ts
30452
30687
  var ShowCommand = class extends SmCommand {
30453
30688
  static paths = [["show"]];
30454
- static usage = Command35.Usage({
30689
+ static usage = Command36.Usage({
30455
30690
  category: "Browse",
30456
30691
  description: "Node detail: weight, frontmatter, links, issues.",
30457
30692
  details: `
@@ -30467,7 +30702,7 @@ var ShowCommand = class extends SmCommand {
30467
30702
  ["Machine-readable detail", "$0 show .claude/agents/architect.md --json"]
30468
30703
  ]
30469
30704
  });
30470
- nodePath = Option33.String({ required: true });
30705
+ nodePath = Option34.String({ required: true });
30471
30706
  async run() {
30472
30707
  const dbPath = resolveDbPath({ db: this.db, ...defaultRuntimeContext() });
30473
30708
  const exit = requireDbOrExit(dbPath, this.context.stderr);
@@ -30708,8 +30943,8 @@ function rankConfidenceForGrouping(c) {
30708
30943
 
30709
30944
  // cli/commands/sidecar.ts
30710
30945
  import { unlink as unlink3 } from "fs/promises";
30711
- import { resolve as resolve41 } from "path";
30712
- import { Command as Command36, Option as Option34 } from "clipanion";
30946
+ import { resolve as resolve42 } from "path";
30947
+ import { Command as Command37, Option as Option35 } from "clipanion";
30713
30948
 
30714
30949
  // cli/i18n/sidecar.texts.ts
30715
30950
  var SIDECAR_TEXTS = {
@@ -30790,7 +31025,7 @@ async function runWithSidecarConsent(bag, ansi, dispatch) {
30790
31025
  }
30791
31026
  var SidecarRefreshCommand = class extends SmCommand {
30792
31027
  static paths = [["sidecar", "refresh"]];
30793
- static usage = Command36.Usage({
31028
+ static usage = Command37.Usage({
30794
31029
  category: "Actions",
30795
31030
  description: "Refresh a sidecar's `for.{bodyHash, frontmatterHash}` to match the live node. Does NOT bump the version.",
30796
31031
  details: `
@@ -30807,8 +31042,8 @@ var SidecarRefreshCommand = class extends SmCommand {
30807
31042
  ["Refresh a node's sidecar hashes", "$0 sidecar refresh .claude/agents/architect.md"]
30808
31043
  ]
30809
31044
  });
30810
- nodePath = Option34.String({ required: true });
30811
- yes = Option34.Boolean("--yes", false, {
31045
+ nodePath = Option35.String({ required: true });
31046
+ yes = Option35.Boolean("--yes", false, {
30812
31047
  description: "Confirm writing .sm sidecar files in this project (sets allowEditSmFiles=true on first run)."
30813
31048
  });
30814
31049
  async run() {
@@ -30866,7 +31101,7 @@ var SidecarRefreshCommand = class extends SmCommand {
30866
31101
  let absPath;
30867
31102
  try {
30868
31103
  assertContained(ctx.cwd, node.path);
30869
- absPath = resolve41(ctx.cwd, node.path);
31104
+ absPath = resolve42(ctx.cwd, node.path);
30870
31105
  } catch (err) {
30871
31106
  this.printer.error(
30872
31107
  tx(SIDECAR_TEXTS.refreshFailed, { glyph: errGlyph, message: formatErrorMessage(err) })
@@ -30930,7 +31165,7 @@ var SidecarRefreshCommand = class extends SmCommand {
30930
31165
  };
30931
31166
  var SidecarPruneCommand = class extends SmCommand {
30932
31167
  static paths = [["sidecar", "prune"]];
30933
- static usage = Command36.Usage({
31168
+ static usage = Command37.Usage({
30934
31169
  category: "Actions",
30935
31170
  description: "Delete orphan .sm files (sidecars whose accompanying .md no longer exists).",
30936
31171
  details: `
@@ -30952,8 +31187,8 @@ var SidecarPruneCommand = class extends SmCommand {
30952
31187
  ["Delete every orphan .sm file (non-interactive)", "$0 sidecar prune --yes"]
30953
31188
  ]
30954
31189
  });
30955
- dryRun = Option34.Boolean("-n,--dry-run", false);
30956
- yes = Option34.Boolean("--yes,--force", false, {
31190
+ dryRun = Option35.Boolean("-n,--dry-run", false);
31191
+ yes = Option35.Boolean("--yes,--force", false, {
30957
31192
  description: "Skip the interactive confirmation prompt. Required for non-interactive callers (CI, pre-commit hooks)."
30958
31193
  });
30959
31194
  // Complexity is from per-orphan handling, empty-set / dry-run /
@@ -31073,7 +31308,7 @@ var SidecarPruneCommand = class extends SmCommand {
31073
31308
  };
31074
31309
  var SidecarAnnotateCommand = class extends SmCommand {
31075
31310
  static paths = [["sidecar", "annotate"]];
31076
- static usage = Command36.Usage({
31311
+ static usage = Command37.Usage({
31077
31312
  category: "Actions",
31078
31313
  description: "Scaffold an empty `<basename>.sm` next to a node ready for editing.",
31079
31314
  details: `
@@ -31091,9 +31326,9 @@ var SidecarAnnotateCommand = class extends SmCommand {
31091
31326
  ["Overwrite an existing one", "$0 sidecar annotate .claude/agents/architect.md --force"]
31092
31327
  ]
31093
31328
  });
31094
- nodePath = Option34.String({ required: true });
31095
- force = Option34.Boolean("--force", false);
31096
- yes = Option34.Boolean("--yes", false, {
31329
+ nodePath = Option35.String({ required: true });
31330
+ force = Option35.Boolean("--force", false);
31331
+ yes = Option35.Boolean("--yes", false, {
31097
31332
  description: "Confirm writing .sm sidecar files in this project (sets allowEditSmFiles=true on first run)."
31098
31333
  });
31099
31334
  async run() {
@@ -31150,7 +31385,7 @@ var SidecarAnnotateCommand = class extends SmCommand {
31150
31385
  let absPath;
31151
31386
  try {
31152
31387
  assertContained(ctx.cwd, node.path);
31153
- absPath = resolve41(ctx.cwd, node.path);
31388
+ absPath = resolve42(ctx.cwd, node.path);
31154
31389
  } catch (err) {
31155
31390
  this.printer.error(
31156
31391
  tx(SIDECAR_TEXTS.annotateFailed, { glyph: errGlyph, message: formatErrorMessage(err) })
@@ -31232,7 +31467,7 @@ var SIDECAR_COMMANDS = [
31232
31467
  ];
31233
31468
 
31234
31469
  // cli/commands/stubs.ts
31235
- import { Command as Command37, Option as Option35 } from "clipanion";
31470
+ import { Command as Command38, Option as Option36 } from "clipanion";
31236
31471
 
31237
31472
  // cli/i18n/stubs.texts.ts
31238
31473
  var STUBS_TEXTS = {
@@ -31258,7 +31493,7 @@ var StubCommand = class extends SmCommand {
31258
31493
  };
31259
31494
  var DoctorCommand = class extends StubCommand {
31260
31495
  static paths = [["doctor"]];
31261
- static usage = Command37.Usage({
31496
+ static usage = Command38.Usage({
31262
31497
  category: "Setup",
31263
31498
  description: planned("Diagnostic report: DB integrity, pending migrations, orphan rows, plugin status, runner availability.")
31264
31499
  });
@@ -31266,18 +31501,18 @@ var DoctorCommand = class extends StubCommand {
31266
31501
  };
31267
31502
  var FindingsCommand = class extends StubCommand {
31268
31503
  static paths = [["findings"]];
31269
- static usage = Command37.Usage({
31504
+ static usage = Command38.Usage({
31270
31505
  category: "Browse",
31271
31506
  description: planned("Probabilistic findings: injection, stale summaries, low confidence.")
31272
31507
  });
31273
- kind = Option35.String("--kind", { required: false });
31274
- since = Option35.String("--since", { required: false });
31275
- threshold = Option35.String("--threshold", { required: false });
31508
+ kind = Option36.String("--kind", { required: false });
31509
+ since = Option36.String("--since", { required: false });
31510
+ threshold = Option36.String("--threshold", { required: false });
31276
31511
  verbName = "findings";
31277
31512
  };
31278
31513
  var ActionsListCommand = class extends StubCommand {
31279
31514
  static paths = [["actions", "list"]];
31280
- static usage = Command37.Usage({
31515
+ static usage = Command38.Usage({
31281
31516
  category: "Jobs",
31282
31517
  description: planned("Registered action types (manifest view).")
31283
31518
  });
@@ -31285,103 +31520,103 @@ var ActionsListCommand = class extends StubCommand {
31285
31520
  };
31286
31521
  var ActionsShowCommand = class extends StubCommand {
31287
31522
  static paths = [["actions", "show"]];
31288
- static usage = Command37.Usage({
31523
+ static usage = Command38.Usage({
31289
31524
  category: "Jobs",
31290
31525
  description: planned("Full action manifest, including preconditions and expected duration.")
31291
31526
  });
31292
- id = Option35.String({ required: true });
31527
+ id = Option36.String({ required: true });
31293
31528
  verbName = "actions show";
31294
31529
  };
31295
31530
  var JobSubmitCommand = class extends StubCommand {
31296
31531
  static paths = [["job", "submit"]];
31297
- static usage = Command37.Usage({
31532
+ static usage = Command38.Usage({
31298
31533
  category: "Jobs",
31299
31534
  description: planned("Enqueue a single job or fan out to every matching node (--all).")
31300
31535
  });
31301
- action = Option35.String({ required: true });
31302
- node = Option35.String("-n", { required: false });
31303
- all = Option35.Boolean("--all", false);
31536
+ action = Option36.String({ required: true });
31537
+ node = Option36.String("-n", { required: false });
31538
+ all = Option36.Boolean("--all", false);
31304
31539
  // CLI flag stays `--run`; field name is `runFlag` per the
31305
31540
  // shadow-avoidance convention documented on `SmCommand`.
31306
- runFlag = Option35.Boolean("--run", false);
31307
- force = Option35.Boolean("--force", false);
31308
- ttl = Option35.String("--ttl", { required: false });
31309
- priority = Option35.String("--priority", { required: false });
31541
+ runFlag = Option36.Boolean("--run", false);
31542
+ force = Option36.Boolean("--force", false);
31543
+ ttl = Option36.String("--ttl", { required: false });
31544
+ priority = Option36.String("--priority", { required: false });
31310
31545
  verbName = "job submit";
31311
31546
  };
31312
31547
  var JobListCommand = class extends StubCommand {
31313
31548
  static paths = [["job", "list"]];
31314
- static usage = Command37.Usage({ category: "Jobs", description: planned("List jobs.") });
31315
- status = Option35.String("--status", { required: false });
31316
- action = Option35.String("--action", { required: false });
31317
- node = Option35.String("--node", { required: false });
31549
+ static usage = Command38.Usage({ category: "Jobs", description: planned("List jobs.") });
31550
+ status = Option36.String("--status", { required: false });
31551
+ action = Option36.String("--action", { required: false });
31552
+ node = Option36.String("--node", { required: false });
31318
31553
  verbName = "job list";
31319
31554
  };
31320
31555
  var JobShowCommand = class extends StubCommand {
31321
31556
  static paths = [["job", "show"]];
31322
- static usage = Command37.Usage({ category: "Jobs", description: planned("Job detail: state, claim time, TTL, runner, content hash.") });
31323
- id = Option35.String({ required: true });
31557
+ static usage = Command38.Usage({ category: "Jobs", description: planned("Job detail: state, claim time, TTL, runner, content hash.") });
31558
+ id = Option36.String({ required: true });
31324
31559
  verbName = "job show";
31325
31560
  };
31326
31561
  var JobPreviewCommand = class extends StubCommand {
31327
31562
  static paths = [["job", "preview"]];
31328
- static usage = Command37.Usage({ category: "Jobs", description: planned("Render the job MD file without executing.") });
31329
- id = Option35.String({ required: true });
31563
+ static usage = Command38.Usage({ category: "Jobs", description: planned("Render the job MD file without executing.") });
31564
+ id = Option36.String({ required: true });
31330
31565
  verbName = "job preview";
31331
31566
  };
31332
31567
  var JobClaimCommand = class extends StubCommand {
31333
31568
  static paths = [["job", "claim"]];
31334
- static usage = Command37.Usage({
31569
+ static usage = Command38.Usage({
31335
31570
  category: "Jobs",
31336
31571
  description: planned("Atomic primitive: return next queued job id, mark it running.")
31337
31572
  });
31338
- filter = Option35.String("--filter", { required: false });
31573
+ filter = Option36.String("--filter", { required: false });
31339
31574
  verbName = "job claim";
31340
31575
  };
31341
31576
  var JobRunCommand = class extends StubCommand {
31342
31577
  static paths = [["job", "run"]];
31343
- static usage = Command37.Usage({
31578
+ static usage = Command38.Usage({
31344
31579
  category: "Jobs",
31345
31580
  description: planned("Full CLI-runner loop: claim + spawn + record.")
31346
31581
  });
31347
- all = Option35.Boolean("--all", false);
31348
- max = Option35.String("--max", { required: false });
31582
+ all = Option36.Boolean("--all", false);
31583
+ max = Option36.String("--max", { required: false });
31349
31584
  verbName = "job run";
31350
31585
  };
31351
31586
  var JobStatusCommand = class extends StubCommand {
31352
31587
  static paths = [["job", "status"]];
31353
- static usage = Command37.Usage({
31588
+ static usage = Command38.Usage({
31354
31589
  category: "Jobs",
31355
31590
  description: planned("Counts (per status) or single-job status.")
31356
31591
  });
31357
- id = Option35.String({ required: false });
31592
+ id = Option36.String({ required: false });
31358
31593
  verbName = "job status";
31359
31594
  };
31360
31595
  var JobCancelCommand = class extends StubCommand {
31361
31596
  static paths = [["job", "cancel"]];
31362
- static usage = Command37.Usage({
31597
+ static usage = Command38.Usage({
31363
31598
  category: "Jobs",
31364
31599
  description: planned("Force a running job to failed with reason user-cancelled.")
31365
31600
  });
31366
- id = Option35.String({ required: false });
31367
- all = Option35.Boolean("--all", false);
31601
+ id = Option36.String({ required: false });
31602
+ all = Option36.Boolean("--all", false);
31368
31603
  verbName = "job cancel";
31369
31604
  };
31370
31605
  var RecordCommand = class extends StubCommand {
31371
31606
  static paths = [["record"]];
31372
- static usage = Command37.Usage({
31607
+ static usage = Command38.Usage({
31373
31608
  category: "Jobs",
31374
31609
  description: planned("Close a running job with success or failure. Nonce is the sole credential.")
31375
31610
  });
31376
- id = Option35.String("--id", { required: true });
31377
- nonce = Option35.String("--nonce", { required: true });
31378
- status = Option35.String("--status", { required: true });
31379
- report = Option35.String("--report", { required: false });
31380
- tokensIn = Option35.String("--tokens-in", { required: false });
31381
- tokensOut = Option35.String("--tokens-out", { required: false });
31382
- durationMs = Option35.String("--duration-ms", { required: false });
31383
- model = Option35.String("--model", { required: false });
31384
- error = Option35.String("--error", { required: false });
31611
+ id = Option36.String("--id", { required: true });
31612
+ nonce = Option36.String("--nonce", { required: true });
31613
+ status = Option36.String("--status", { required: true });
31614
+ report = Option36.String("--report", { required: false });
31615
+ tokensIn = Option36.String("--tokens-in", { required: false });
31616
+ tokensOut = Option36.String("--tokens-out", { required: false });
31617
+ durationMs = Option36.String("--duration-ms", { required: false });
31618
+ model = Option36.String("--model", { required: false });
31619
+ error = Option36.String("--error", { required: false });
31385
31620
  verbName = "record";
31386
31621
  };
31387
31622
  var STUB_COMMANDS = [
@@ -31401,11 +31636,11 @@ var STUB_COMMANDS = [
31401
31636
  ];
31402
31637
 
31403
31638
  // cli/commands/tutorial.ts
31404
- import { cpSync as cpSync2, existsSync as existsSync32, mkdirSync as mkdirSync6, readdirSync as readdirSync10, rmSync as rmSync2, statSync as statSync11 } from "fs";
31405
- import { dirname as dirname20, join as join21, resolve as resolve42 } from "path";
31406
- import { createInterface as createInterface5 } from "readline";
31407
- import { fileURLToPath as fileURLToPath7 } from "url";
31408
- import { Command as Command38, Option as Option36 } from "clipanion";
31639
+ import { cpSync as cpSync3, existsSync as existsSync33, mkdirSync as mkdirSync6, rmSync as rmSync2, statSync as statSync12 } from "fs";
31640
+ import { dirname as dirname21, join as join21, resolve as resolve43 } from "path";
31641
+ import { createInterface as createInterface6 } from "readline";
31642
+ import { fileURLToPath as fileURLToPath8 } from "url";
31643
+ import { Command as Command39, Option as Option37 } from "clipanion";
31409
31644
 
31410
31645
  // cli/i18n/tutorial.texts.ts
31411
31646
  var TUTORIAL_TEXTS = {
@@ -31428,14 +31663,8 @@ var TUTORIAL_TEXTS = {
31428
31663
  // default).
31429
31664
  promptHeader: "{{glyph}} Which agent should host the tutorial skill?",
31430
31665
  promptOption: " {{index}}) {{label}}: {{skillDir}}{{marker}}",
31431
- // Coming-soon Providers are listed for visibility but cannot be picked
31432
- // (no skillDir, a `(coming soon)` tag instead of a target path).
31433
- promptOptionComingSoon: " {{index}}) {{label}} (coming soon)",
31434
31666
  promptDefaultMarker: " (default)",
31435
31667
  promptInput: " Enter the number or provider id [default {{index}}]: ",
31436
- // Shown (and the prompt re-asked) when the tester picks a coming-soon
31437
- // entry: it is visible but not selectable yet.
31438
- promptComingSoonNotice: " {{label}} is coming soon, not selectable yet. Pick {{defaultLabel}}.",
31439
31668
  // Prompt answer matched neither an index nor an id. Goes to stderr,
31440
31669
  // exit code 2. Mirrors the error shape: glyph + headline + dim hint.
31441
31670
  promptInvalid: "{{glyph}} sm tutorial: that is not one of the listed providers\n {{hint}}\n",
@@ -31473,7 +31702,7 @@ var TRIGGER_EN = "run the tutorial";
31473
31702
  var TRIGGER_ES = "ejecuta el tutorial";
31474
31703
  var TutorialCommand = class extends SmCommand {
31475
31704
  static paths = [["tutorial"]];
31476
- static usage = Command38.Usage({
31705
+ static usage = Command39.Usage({
31477
31706
  category: "Setup",
31478
31707
  description: "Materialize an interactive tester tutorial as a Claude Code skill folder under `<cwd>/.claude/skills/`.",
31479
31708
  details: `
@@ -31486,27 +31715,36 @@ var TutorialCommand = class extends SmCommand {
31486
31715
  Does NOT require an initialized .skill-map/ project. Refuses to
31487
31716
  overwrite the target directory unless --force is passed. Takes no
31488
31717
  positional argument.
31718
+
31719
+ By default only ready providers are offered as destinations. Pass
31720
+ --experimental to also offer experimental ones (e.g. agent-skills);
31721
+ they ship disabled, so enable the chosen one with
31722
+ \`sm plugins enable <id>\` before scanning under its lens.
31489
31723
  `,
31490
31724
  examples: [
31491
31725
  ["Materialize the tutorial skill in the cwd", "$0 tutorial"],
31492
- ["Overwrite an existing target directory", "$0 tutorial --force"]
31726
+ ["Overwrite an existing target directory", "$0 tutorial --force"],
31727
+ ["Offer experimental providers as destinations", "$0 tutorial --experimental"]
31493
31728
  ]
31494
31729
  });
31495
31730
  // Legacy positional catcher: the verb takes no positional argument any
31496
31731
  // more. Accept one so a stale `sm tutorial master` lands on a friendly
31497
31732
  // usage error (guarded in `run()`) instead of clipanion's generic
31498
31733
  // "extraneous argument" message.
31499
- legacyPositional = Option36.String({ required: false });
31734
+ legacyPositional = Option37.String({ required: false });
31500
31735
  // Named `forProvider`, NOT `for` (reserved word). The CLI surface stays
31501
31736
  // `--for`; selects the destination Provider whose `scaffold.skillDir`
31502
31737
  // the skill is materialised under, skipping the interactive prompt.
31503
- forProvider = Option36.String("--for", {
31738
+ forProvider = Option37.String("--for", {
31504
31739
  required: false,
31505
31740
  description: "Destination provider id (e.g. claude). Skips the prompt."
31506
31741
  });
31507
- force = Option36.Boolean("--force", false, {
31742
+ force = Option37.Boolean("--force", false, {
31508
31743
  description: "Overwrite an existing target directory without prompting."
31509
31744
  });
31745
+ experimental = Option37.Boolean("--experimental", false, {
31746
+ description: "Offer experimental providers (e.g. agent-skills) as destinations. They ship disabled; enable the chosen one with `sm plugins enable <id>`."
31747
+ });
31510
31748
  async run() {
31511
31749
  const ctx = defaultRuntimeContext();
31512
31750
  const stderr = this.context.stderr;
@@ -31532,7 +31770,7 @@ var TutorialCommand = class extends SmCommand {
31532
31770
  );
31533
31771
  return ExitCode.Error;
31534
31772
  }
31535
- const targets = listScaffoldTargets();
31773
+ const targets = listScaffoldTargets(this.experimental);
31536
31774
  const target = await this.resolveScaffoldTarget(targets, stderrAnsi, errGlyph);
31537
31775
  if (target === null) return ExitCode.Error;
31538
31776
  if (target.skillDir === void 0) {
@@ -31556,8 +31794,8 @@ var TutorialCommand = class extends SmCommand {
31556
31794
  }
31557
31795
  try {
31558
31796
  rmSync2(targetDir, { recursive: true, force: true });
31559
- mkdirSync6(dirname20(targetDir), { recursive: true });
31560
- cpSync2(sourceDir, targetDir, { recursive: true });
31797
+ mkdirSync6(dirname21(targetDir), { recursive: true });
31798
+ cpSync3(sourceDir, targetDir, { recursive: true });
31561
31799
  } catch (err) {
31562
31800
  this.printer.error(
31563
31801
  tx(TUTORIAL_TEXTS.writeFailed, {
@@ -31602,21 +31840,20 @@ var TutorialCommand = class extends SmCommand {
31602
31840
  * Returns `null` after printing an error (caller exits non-zero).
31603
31841
  */
31604
31842
  async resolveScaffoldTarget(targets, stderrAnsi, errGlyph) {
31605
- const selectable = selectableTargets(targets);
31606
- if (selectable.length === 0) {
31843
+ if (targets.length === 0) {
31607
31844
  this.printer.error(tx(TUTORIAL_TEXTS.noTargets, { glyph: errGlyph }));
31608
31845
  return null;
31609
31846
  }
31610
31847
  const requested = this.forProvider;
31611
31848
  if (requested !== void 0) {
31612
- const found = selectable.find((t) => t.id === requested);
31849
+ const found = targets.find((t) => t.id === requested);
31613
31850
  if (found === void 0) {
31614
31851
  this.printer.error(
31615
31852
  tx(TUTORIAL_TEXTS.forUnknown, {
31616
31853
  glyph: errGlyph,
31617
31854
  provider: requested,
31618
31855
  hint: stderrAnsi.dim(
31619
- tx(TUTORIAL_TEXTS.forUnknownHint, { ids: selectable.map((t) => t.id).join(", ") })
31856
+ tx(TUTORIAL_TEXTS.forUnknownHint, { ids: targets.map((t) => t.id).join(", ") })
31620
31857
  )
31621
31858
  })
31622
31859
  );
@@ -31624,7 +31861,7 @@ var TutorialCommand = class extends SmCommand {
31624
31861
  }
31625
31862
  return found;
31626
31863
  }
31627
- const def = selectable[0];
31864
+ const def = targets[0];
31628
31865
  const stdin = this.context.stdin;
31629
31866
  if (stdin.isTTY !== true) return def;
31630
31867
  const stderr = this.context.stderr;
@@ -31640,7 +31877,7 @@ var TutorialCommand = class extends SmCommand {
31640
31877
  tx(TUTORIAL_TEXTS.promptInvalid, {
31641
31878
  glyph: errGlyph,
31642
31879
  hint: stderrAnsi.dim(
31643
- tx(TUTORIAL_TEXTS.forUnknownHint, { ids: selectable.map((t) => t.id).join(", ") })
31880
+ tx(TUTORIAL_TEXTS.forUnknownHint, { ids: targets.map((t) => t.id).join(", ") })
31644
31881
  )
31645
31882
  })
31646
31883
  );
@@ -31649,37 +31886,25 @@ var TutorialCommand = class extends SmCommand {
31649
31886
  return picked;
31650
31887
  }
31651
31888
  };
31652
- function toScaffoldTarget(provider) {
31653
- const comingSoon = provider.presentation.comingSoon === true;
31654
- if (comingSoon) {
31655
- return {
31656
- id: provider.id,
31657
- label: provider.presentation.label,
31658
- aka: provider.scaffold?.aka ?? [],
31659
- comingSoon: true
31660
- };
31661
- }
31889
+ function toScaffoldTarget(provider, includeExperimental) {
31662
31890
  const scaffold = provider.scaffold;
31663
31891
  if (!scaffold || !scaffold.skillDir) return null;
31892
+ if (!installedDefaultEnabled(provider.stability) && !includeExperimental) return null;
31664
31893
  return {
31665
31894
  id: provider.id,
31666
31895
  label: provider.presentation.label,
31667
31896
  skillDir: scaffold.skillDir,
31668
- aka: scaffold.aka ?? [],
31669
- comingSoon: false
31897
+ aka: scaffold.aka ?? []
31670
31898
  };
31671
31899
  }
31672
- function listScaffoldTargets() {
31900
+ function listScaffoldTargets(includeExperimental = false) {
31673
31901
  const out = [];
31674
31902
  for (const provider of builtIns().providers) {
31675
- const target = toScaffoldTarget(provider);
31903
+ const target = toScaffoldTarget(provider, includeExperimental);
31676
31904
  if (target !== null) out.push(target);
31677
31905
  }
31678
31906
  return out;
31679
31907
  }
31680
- function selectableTargets(targets) {
31681
- return targets.filter((t) => !t.comingSoon);
31682
- }
31683
31908
  function labelWithAka(target) {
31684
31909
  return target.aka.length > 0 ? `${target.label} (${target.aka.join(", ")})` : target.label;
31685
31910
  }
@@ -31688,7 +31913,7 @@ function renderTargetLines(targets, def, glyph) {
31688
31913
  for (let i = 0; i < targets.length; i += 1) {
31689
31914
  const t = targets[i];
31690
31915
  lines.push(
31691
- t.comingSoon ? tx(TUTORIAL_TEXTS.promptOptionComingSoon, { index: i + 1, label: t.label }) : tx(TUTORIAL_TEXTS.promptOption, {
31916
+ tx(TUTORIAL_TEXTS.promptOption, {
31692
31917
  index: i + 1,
31693
31918
  label: labelWithAka(t),
31694
31919
  skillDir: `${t.skillDir}/`,
@@ -31707,56 +31932,35 @@ function classifyAnswer(trimmed, targets, def) {
31707
31932
  async function promptForTarget(targets, def, stdin, stderr, glyph) {
31708
31933
  stderr.write(renderTargetLines(targets, def, glyph) + "\n");
31709
31934
  const defIndex = targets.findIndex((t) => t.id === def.id);
31710
- const rl = createInterface5({ input: stdin, output: stderr });
31935
+ const rl = createInterface6({ input: stdin, output: stderr });
31711
31936
  try {
31712
31937
  for (let attempt = 0; attempt < 5; attempt += 1) {
31713
31938
  const answer = await new Promise(
31714
31939
  (resolveP) => rl.question(tx(TUTORIAL_TEXTS.promptInput, { index: defIndex + 1 }), resolveP)
31715
31940
  );
31716
31941
  const result = classifyAnswer(answer.trim(), targets, def);
31717
- if (result === null) continue;
31718
- if (!result.comingSoon) return result;
31719
- stderr.write(
31720
- tx(TUTORIAL_TEXTS.promptComingSoonNotice, {
31721
- label: result.label,
31722
- defaultLabel: def.label
31723
- }) + "\n"
31724
- );
31942
+ if (result !== null) return result;
31725
31943
  }
31726
31944
  return null;
31727
31945
  } finally {
31728
31946
  rl.close();
31729
31947
  }
31730
31948
  }
31731
- function displayCwd(cwd) {
31732
- const segments = cwd.split("/").filter((s) => s.length > 0);
31733
- if (segments.length === 0) return "./";
31734
- return `./${segments[segments.length - 1]}/`;
31735
- }
31736
- function isDirEmpty(dir) {
31737
- return readdirSync10(dir).length === 0;
31738
- }
31739
- function listCwdEntries(dir) {
31740
- const entries = readdirSync10(dir).sort();
31741
- const shown = entries.slice(0, 5);
31742
- const more = entries.length > shown.length ? ", ..." : "";
31743
- return shown.join(", ") + more;
31744
- }
31745
- var cachedSourceDir;
31949
+ var cachedSourceDir2;
31746
31950
  function resolveSkillSourceDir() {
31747
- if (cachedSourceDir !== void 0) return cachedSourceDir;
31748
- const here = dirname20(fileURLToPath7(import.meta.url));
31951
+ if (cachedSourceDir2 !== void 0) return cachedSourceDir2;
31952
+ const here = dirname21(fileURLToPath8(import.meta.url));
31749
31953
  const candidates = [
31750
31954
  // dev: src/cli/commands/ → repo-root .claude/skills/sm-tutorial/
31751
- resolve42(here, "../../..", SKILL_SOURCE_DIR),
31955
+ resolve43(here, "../../..", SKILL_SOURCE_DIR),
31752
31956
  // bundled: dist/cli.js → dist/cli/tutorial/sm-tutorial (sibling)
31753
- resolve42(here, "cli/tutorial", SKILL_SLUG),
31957
+ resolve43(here, "cli/tutorial", SKILL_SLUG),
31754
31958
  // bundled fallback: any-depth → cli/tutorial/sm-tutorial
31755
- resolve42(here, "../cli/tutorial", SKILL_SLUG)
31959
+ resolve43(here, "../cli/tutorial", SKILL_SLUG)
31756
31960
  ];
31757
31961
  for (const candidate of candidates) {
31758
- if (existsSync32(candidate) && statSync11(candidate).isDirectory()) {
31759
- cachedSourceDir = candidate;
31962
+ if (existsSync33(candidate) && statSync12(candidate).isDirectory()) {
31963
+ cachedSourceDir2 = candidate;
31760
31964
  return candidate;
31761
31965
  }
31762
31966
  }
@@ -31766,7 +31970,7 @@ function resolveSkillSourceDir() {
31766
31970
  }
31767
31971
 
31768
31972
  // cli/commands/version.ts
31769
- import { Command as Command39 } from "clipanion";
31973
+ import { Command as Command40 } from "clipanion";
31770
31974
 
31771
31975
  // cli/i18n/version.texts.ts
31772
31976
  var VERSION_TEXTS = {
@@ -31781,7 +31985,7 @@ var VERSION_TEXTS = {
31781
31985
  // cli/commands/version.ts
31782
31986
  var VersionCommand = class extends SmCommand {
31783
31987
  static paths = [["version"]];
31784
- static usage = Command39.Usage({
31988
+ static usage = Command40.Usage({
31785
31989
  category: "Introspection",
31786
31990
  description: "Print the CLI / spec / runtime / db-schema version matrix."
31787
31991
  });
@@ -31856,6 +32060,7 @@ cli.register(RootHelpCommand);
31856
32060
  cli.register(HelpCommand);
31857
32061
  cli.register(InitCommand);
31858
32062
  cli.register(TutorialCommand);
32063
+ cli.register(ExampleCommand);
31859
32064
  cli.register(IntentionalFailCommand);
31860
32065
  cli.register(ScanCommand);
31861
32066
  cli.register(ScanCompareCommand);
@@ -31888,7 +32093,7 @@ var logLevel = resolveLogLevel({
31888
32093
  errStream: process.stderr
31889
32094
  });
31890
32095
  configureLogger(new Logger({ level: logLevel, stream: process.stderr }));
31891
- var bareArgs = resolveBareInvocation(args);
32096
+ var bareArgs = await resolveBareInvocation(args);
31892
32097
  var routedArgs = routeHelpArgs(bareArgs ?? args, cli);
31893
32098
  var telemetryVerb = routedArgs[0];
31894
32099
  await maybeRunFirstRunPrompt();
@@ -31946,23 +32151,41 @@ await lifecycleDispatcher.dispatch(
31946
32151
  await closeSentryCli();
31947
32152
  await flushUsageCli();
31948
32153
  process.exit(exitCode);
31949
- function resolveBareInvocation(rawArgs) {
31950
- if (rawArgs.length === 0) return resolveBareDefault();
32154
+ async function resolveBareInvocation(rawArgs) {
32155
+ if (rawArgs.length === 0) return resolveNoArgsBare();
31951
32156
  const first = rawArgs[0];
31952
32157
  const passthrough = /* @__PURE__ */ new Set(["--help", "-h", "--version", "-V", "-v"]);
31953
32158
  if (first !== void 0 && first.startsWith("-") && !passthrough.has(first)) {
31954
32159
  const isSingleDashLong = !first.startsWith("--") && first.length > 2;
31955
32160
  if (isSingleDashLong) return null;
31956
- if (existsSync33(defaultProjectDbPath(defaultRuntimeContext()))) {
32161
+ if (existsSync34(defaultProjectDbPath(defaultRuntimeContext()))) {
31957
32162
  return ["serve", ...rawArgs];
31958
32163
  }
31959
32164
  return resolveBareDefault();
31960
32165
  }
31961
32166
  return null;
31962
32167
  }
32168
+ async function resolveNoArgsBare() {
32169
+ const ctx = defaultRuntimeContext();
32170
+ const stdin = process.stdin;
32171
+ const result = await decideBareNoArgs(
32172
+ {
32173
+ hasDb: existsSync34(defaultProjectDbPath(ctx)),
32174
+ isTty: stdin.isTTY === true,
32175
+ isEmptyDir: isDirEmpty(ctx.cwd)
32176
+ },
32177
+ () => {
32178
+ const stderr = process.stderr;
32179
+ const ansi = ansiFor({ isTTY: stderr.isTTY === true, noColorFlag: false });
32180
+ return promptEmptyFolderChoice(stdin, stderr, ansi);
32181
+ }
32182
+ );
32183
+ if (result.kind === "route") return result.argv;
32184
+ return resolveBareDefault();
32185
+ }
31963
32186
  function resolveBareDefault() {
31964
32187
  const ctx = defaultRuntimeContext();
31965
- if (existsSync33(defaultProjectDbPath(ctx))) {
32188
+ if (existsSync34(defaultProjectDbPath(ctx))) {
31966
32189
  return ["serve"];
31967
32190
  }
31968
32191
  const stderr = process.stderr;
@@ -31971,10 +32194,12 @@ function resolveBareDefault() {
31971
32194
  tx(ENTRY_TEXTS.bareNoProject, {
31972
32195
  glyph: ansi.red("\u2715"),
31973
32196
  cwd: ctx.cwd,
31974
- hint: ansi.dim(ENTRY_TEXTS.bareNoProjectHint)
32197
+ hint: ansi.dim(
32198
+ isDirEmpty(ctx.cwd) ? ENTRY_TEXTS.bareEmptyHint : ENTRY_TEXTS.bareNoProjectHint
32199
+ )
31975
32200
  })
31976
32201
  );
31977
32202
  process.exit(ExitCode.Error);
31978
32203
  }
31979
32204
  //# sourceMappingURL=cli.js.map
31980
- //# debugId=454166ed-eb8e-57bb-b412-c9c6e9ff30f4
32205
+ //# debugId=43bb2367-1e14-5df2-9221-a5e7a877a666