@skill-map/cli 0.62.1 → 0.63.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // cli/entry.ts
2
2
 
3
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="be3954fe-3e41-5dae-bb8d-1d8225ffd77a")}catch(e){}}();
3
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="09459eb1-3d6f-565d-9552-4819baae6f1a")}catch(e){}}();
4
4
  import { existsSync as existsSync33 } from "fs";
5
5
  import { Builtins, Cli as Cli2 } from "clipanion";
6
6
 
@@ -250,7 +250,7 @@ function bucketByKind(kind, instance, bag) {
250
250
  // package.json
251
251
  var package_default = {
252
252
  name: "@skill-map/cli",
253
- version: "0.62.1",
253
+ version: "0.63.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",
@@ -7659,7 +7671,7 @@ async function loadBranch(db, prefixes, limit) {
7659
7671
  }
7660
7672
  const paths = [...pathSet];
7661
7673
  const [linkRows, issueRows] = await Promise.all([
7662
- db.selectFrom("scan_links").selectAll().where("sourcePath", "in", paths).where("targetPath", "in", paths).execute(),
7674
+ db.selectFrom("scan_links").selectAll().where("sourcePath", "in", paths).where(sql2`coalesce(resolved_target, target_path)`, "in", paths).execute(),
7663
7675
  db.selectFrom("scan_issues").selectAll().where(
7664
7676
  ({ exists, selectFrom }) => exists(
7665
7677
  selectFrom(
@@ -11826,13 +11838,14 @@ function detectProvidersFromFilesystem(cwd, providers) {
11826
11838
  return out;
11827
11839
  }
11828
11840
  function isDetectableUnderCwd(cwd, provider) {
11829
- if (provider.presentation?.comingSoon === true) return false;
11841
+ if (!installedDefaultEnabled(provider.stability)) return false;
11830
11842
  const markers = provider.detect?.markers;
11831
11843
  if (!markers || markers.length === 0) return false;
11832
11844
  return markers.some((marker) => existsSync15(join10(cwd, marker)));
11833
11845
  }
11834
11846
 
11835
11847
  // core/config/active-provider.ts
11848
+ var MARKDOWN_LENS_ID = "markdown";
11836
11849
  function resolveActiveProvider(cwd, providers = []) {
11837
11850
  const detected = detectProvidersFromFilesystem(cwd, providers);
11838
11851
  const fromConfig = readConfigValue("activeProvider", { cwd });
@@ -11842,7 +11855,7 @@ function resolveActiveProvider(cwd, providers = []) {
11842
11855
  if (detected.length > 0) {
11843
11856
  return { resolved: detected[0], source: "autodetect", detected };
11844
11857
  }
11845
- return { resolved: null, source: "none", detected };
11858
+ return { resolved: MARKDOWN_LENS_ID, source: "default", detected };
11846
11859
  }
11847
11860
 
11848
11861
  // cli/util/path-display.ts
@@ -16290,8 +16303,7 @@ function matchesKindPrecondition(ex, kind) {
16290
16303
  function matchesProviderPrecondition(ex, activeProvider) {
16291
16304
  const providers = ex.precondition?.provider;
16292
16305
  if (!providers || providers.length === 0) return true;
16293
- if (activeProvider === null) return false;
16294
- return providers.includes(activeProvider);
16306
+ return activeProvider !== null && providers.includes(activeProvider);
16295
16307
  }
16296
16308
  function splitLegacy(applicableExtractors, applicableQualifiedIds, nodeHashCacheEligible) {
16297
16309
  const cachedQualifiedIds = /* @__PURE__ */ new Set();
@@ -17351,7 +17363,6 @@ function buildPriorMtimes(opts) {
17351
17363
  }
17352
17364
  function providerParticipates(provider, activeProvider) {
17353
17365
  if (!provider.gatedByActiveLens) return true;
17354
- if (activeProvider === null) return true;
17355
17366
  return provider.id === activeProvider;
17356
17367
  }
17357
17368
  function createWalkAccumulators() {
@@ -17773,8 +17784,7 @@ function buildPostWalkTransformCtx(providers, nodes, activeProvider) {
17773
17784
  const reservedNodePaths = buildReservedNodePaths(
17774
17785
  nodes,
17775
17786
  kindRegistry,
17776
- reservedNamesByProviderKind,
17777
- activeProvider
17787
+ reservedNamesByProviderKind
17778
17788
  );
17779
17789
  return { kindRegistry, providerResolution, activeProvider, reservedNodePaths };
17780
17790
  }
@@ -17805,15 +17815,14 @@ function indexReservedNames(provider, out) {
17805
17815
  }
17806
17816
  }
17807
17817
  }
17808
- function buildReservedNodePaths(nodes, kindRegistry, reservedNamesByProviderKind, activeProvider) {
17818
+ function buildReservedNodePaths(nodes, kindRegistry, reservedNamesByProviderKind) {
17809
17819
  const out = /* @__PURE__ */ new Set();
17810
17820
  for (const node of nodes) {
17811
17821
  const selfKey = `${node.provider}/${node.kind}`;
17812
17822
  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;
17823
+ if (!hasEntries(selfReserved)) continue;
17815
17824
  const ids = deriveNodeIdentifiers(node, kindRegistry.get(selfKey));
17816
- if (ids.some((id) => selfReserved?.has(id) === true || lensReserved?.has(id) === true)) {
17825
+ if (ids.some((id) => selfReserved?.has(id) === true)) {
17817
17826
  out.add(node.path);
17818
17827
  }
17819
17828
  }
@@ -18374,23 +18383,6 @@ var SCAN_RUNNER_TEXTS = {
18374
18383
  * operator notices a typo without the walker silently swallowing it.
18375
18384
  */
18376
18385
  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
18386
  /**
18395
18387
  * Active-provider bootstrap: filesystem auto-detect found exactly
18396
18388
  * one marker and persisted the detected id to project settings.
@@ -18549,15 +18541,7 @@ async function bootstrapActiveProvider(opts) {
18549
18541
  opts.providers
18550
18542
  );
18551
18543
  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" };
18544
+ return { kind: "ok", activeProvider: MARKDOWN_LENS_ID, source: "default" };
18561
18545
  }
18562
18546
  if (detected.length === 1) {
18563
18547
  const picked2 = detected[0];
@@ -18620,7 +18604,13 @@ function handleDrift(opts, resolvedLens, currentMarkers) {
18620
18604
  backfillMarkersSnapshot(opts.cwd, currentMarkers);
18621
18605
  return;
18622
18606
  }
18623
- const diff = diffMarkers(snapshot, currentMarkers);
18607
+ const shipsDisabled = new Set(
18608
+ opts.providers.filter((p) => !installedDefaultEnabled(p.stability)).map((p) => p.id)
18609
+ );
18610
+ const diff = diffMarkers(
18611
+ snapshot.filter((id) => !shipsDisabled.has(id)),
18612
+ currentMarkers.filter((id) => !shipsDisabled.has(id))
18613
+ );
18624
18614
  if (diff.added.length === 0 && diff.removed.length === 0) return;
18625
18615
  emitDriftWarn(opts, resolvedLens, diff);
18626
18616
  }
@@ -18630,7 +18620,7 @@ function emitDriftWarn(opts, resolvedLens, diff) {
18630
18620
  const hint = tx(SCAN_RUNNER_TEXTS.activeProviderDriftWarnHint, {
18631
18621
  added: diff.added.length === 0 ? "(none)" : diff.added.join(", "),
18632
18622
  removed: diff.removed.length === 0 ? "(none)" : diff.removed.join(", "),
18633
- currentLens: resolvedLens ?? "(none)"
18623
+ currentLens: resolvedLens
18634
18624
  });
18635
18625
  opts.printer.warn(
18636
18626
  tx(SCAN_RUNNER_TEXTS.activeProviderDriftWarn, {
@@ -18662,7 +18652,6 @@ function diffMarkers(snapshot, current) {
18662
18652
  return { added, removed };
18663
18653
  }
18664
18654
  function warnIfLensPluginDisabled(args2) {
18665
- if (args2.activeProvider === null) return;
18666
18655
  if (args2.resolveEnabled(args2.activeProvider)) return;
18667
18656
  args2.printer.warn(
18668
18657
  tx(SCAN_RUNNER_TEXTS.activeProviderPluginDisabledWarning, {
@@ -24089,6 +24078,9 @@ function relativeFromRoots2(absolute, absRoots) {
24089
24078
  }
24090
24079
  return null;
24091
24080
  }
24081
+ function resolveWatcherLens(cwd, composed) {
24082
+ return resolveActiveProvider(cwd, composed?.providers ?? []).resolved;
24083
+ }
24092
24084
  function createWatcherRuntime(opts) {
24093
24085
  const events = opts.events ?? {};
24094
24086
  const cwd = opts.runtimeContext.cwd;
@@ -24196,7 +24188,14 @@ function createWatcherRuntime(opts) {
24196
24188
  overrideScanCeiling: opts.maxScanOverride ?? null,
24197
24189
  maxRenderNodes: cfg.scan.maxNodes,
24198
24190
  overrideMaxRenderNodes: opts.maxNodesOverride ?? null,
24199
- maxFileSizeBytes: cfg.scan.maxFileSizeBytes
24191
+ maxFileSizeBytes: cfg.scan.maxFileSizeBytes,
24192
+ // Resolve the active lens from the persisted config (settings.json)
24193
+ // so a lens switched via `PATCH /api/active-provider` is honoured by
24194
+ // the next watcher batch. Without an explicit value the orchestrator
24195
+ // falls back to filesystem detection, which ignores the operator's
24196
+ // choice (e.g. selecting `markdown` while `.claude/` is still on disk
24197
+ // would re-detect `claude` and silently overwrite the chosen lens).
24198
+ activeProvider: resolveWatcherLens(cwd, composed)
24200
24199
  };
24201
24200
  if (cfg.scan.referencePaths.length > 0) {
24202
24201
  const walk3 = walkReferencePaths(cfg.scan.referencePaths, cwd);
@@ -27927,7 +27926,6 @@ async function resolveSelectableProviders(deps) {
27927
27926
  });
27928
27927
  const selectable = /* @__PURE__ */ new Set();
27929
27928
  for (const provider of deps.providers) {
27930
- if (provider.presentation?.comingSoon === true) continue;
27931
27929
  if (isPluginExtensionEnabled(provider, resolveEnabled)) {
27932
27930
  selectable.add(provider.id);
27933
27931
  }
@@ -29271,7 +29269,6 @@ function buildProviderRegistry(providers) {
29271
29269
  if (ui.emoji !== void 0) entry.emoji = ui.emoji;
29272
29270
  if (ui.icon !== void 0) entry.icon = ui.icon;
29273
29271
  if (ui.hideChip !== void 0) entry.hideChip = ui.hideChip;
29274
- if (ui.comingSoon !== void 0) entry.comingSoon = ui.comingSoon;
29275
29272
  registry[provider.id] = entry;
29276
29273
  }
29277
29274
  return registry;
@@ -31428,14 +31425,8 @@ var TUTORIAL_TEXTS = {
31428
31425
  // default).
31429
31426
  promptHeader: "{{glyph}} Which agent should host the tutorial skill?",
31430
31427
  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
31428
  promptDefaultMarker: " (default)",
31435
31429
  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
31430
  // Prompt answer matched neither an index nor an id. Goes to stderr,
31440
31431
  // exit code 2. Mirrors the error shape: glyph + headline + dim hint.
31441
31432
  promptInvalid: "{{glyph}} sm tutorial: that is not one of the listed providers\n {{hint}}\n",
@@ -31486,10 +31477,16 @@ var TutorialCommand = class extends SmCommand {
31486
31477
  Does NOT require an initialized .skill-map/ project. Refuses to
31487
31478
  overwrite the target directory unless --force is passed. Takes no
31488
31479
  positional argument.
31480
+
31481
+ By default only ready providers are offered as destinations. Pass
31482
+ --experimental to also offer experimental ones (e.g. agent-skills);
31483
+ they ship disabled, so enable the chosen one with
31484
+ \`sm plugins enable <id>\` before scanning under its lens.
31489
31485
  `,
31490
31486
  examples: [
31491
31487
  ["Materialize the tutorial skill in the cwd", "$0 tutorial"],
31492
- ["Overwrite an existing target directory", "$0 tutorial --force"]
31488
+ ["Overwrite an existing target directory", "$0 tutorial --force"],
31489
+ ["Offer experimental providers as destinations", "$0 tutorial --experimental"]
31493
31490
  ]
31494
31491
  });
31495
31492
  // Legacy positional catcher: the verb takes no positional argument any
@@ -31507,6 +31504,9 @@ var TutorialCommand = class extends SmCommand {
31507
31504
  force = Option36.Boolean("--force", false, {
31508
31505
  description: "Overwrite an existing target directory without prompting."
31509
31506
  });
31507
+ experimental = Option36.Boolean("--experimental", false, {
31508
+ description: "Offer experimental providers (e.g. agent-skills) as destinations. They ship disabled; enable the chosen one with `sm plugins enable <id>`."
31509
+ });
31510
31510
  async run() {
31511
31511
  const ctx = defaultRuntimeContext();
31512
31512
  const stderr = this.context.stderr;
@@ -31532,7 +31532,7 @@ var TutorialCommand = class extends SmCommand {
31532
31532
  );
31533
31533
  return ExitCode.Error;
31534
31534
  }
31535
- const targets = listScaffoldTargets();
31535
+ const targets = listScaffoldTargets(this.experimental);
31536
31536
  const target = await this.resolveScaffoldTarget(targets, stderrAnsi, errGlyph);
31537
31537
  if (target === null) return ExitCode.Error;
31538
31538
  if (target.skillDir === void 0) {
@@ -31602,21 +31602,20 @@ var TutorialCommand = class extends SmCommand {
31602
31602
  * Returns `null` after printing an error (caller exits non-zero).
31603
31603
  */
31604
31604
  async resolveScaffoldTarget(targets, stderrAnsi, errGlyph) {
31605
- const selectable = selectableTargets(targets);
31606
- if (selectable.length === 0) {
31605
+ if (targets.length === 0) {
31607
31606
  this.printer.error(tx(TUTORIAL_TEXTS.noTargets, { glyph: errGlyph }));
31608
31607
  return null;
31609
31608
  }
31610
31609
  const requested = this.forProvider;
31611
31610
  if (requested !== void 0) {
31612
- const found = selectable.find((t) => t.id === requested);
31611
+ const found = targets.find((t) => t.id === requested);
31613
31612
  if (found === void 0) {
31614
31613
  this.printer.error(
31615
31614
  tx(TUTORIAL_TEXTS.forUnknown, {
31616
31615
  glyph: errGlyph,
31617
31616
  provider: requested,
31618
31617
  hint: stderrAnsi.dim(
31619
- tx(TUTORIAL_TEXTS.forUnknownHint, { ids: selectable.map((t) => t.id).join(", ") })
31618
+ tx(TUTORIAL_TEXTS.forUnknownHint, { ids: targets.map((t) => t.id).join(", ") })
31620
31619
  )
31621
31620
  })
31622
31621
  );
@@ -31624,7 +31623,7 @@ var TutorialCommand = class extends SmCommand {
31624
31623
  }
31625
31624
  return found;
31626
31625
  }
31627
- const def = selectable[0];
31626
+ const def = targets[0];
31628
31627
  const stdin = this.context.stdin;
31629
31628
  if (stdin.isTTY !== true) return def;
31630
31629
  const stderr = this.context.stderr;
@@ -31640,7 +31639,7 @@ var TutorialCommand = class extends SmCommand {
31640
31639
  tx(TUTORIAL_TEXTS.promptInvalid, {
31641
31640
  glyph: errGlyph,
31642
31641
  hint: stderrAnsi.dim(
31643
- tx(TUTORIAL_TEXTS.forUnknownHint, { ids: selectable.map((t) => t.id).join(", ") })
31642
+ tx(TUTORIAL_TEXTS.forUnknownHint, { ids: targets.map((t) => t.id).join(", ") })
31644
31643
  )
31645
31644
  })
31646
31645
  );
@@ -31649,37 +31648,25 @@ var TutorialCommand = class extends SmCommand {
31649
31648
  return picked;
31650
31649
  }
31651
31650
  };
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
- }
31651
+ function toScaffoldTarget(provider, includeExperimental) {
31662
31652
  const scaffold = provider.scaffold;
31663
31653
  if (!scaffold || !scaffold.skillDir) return null;
31654
+ if (!installedDefaultEnabled(provider.stability) && !includeExperimental) return null;
31664
31655
  return {
31665
31656
  id: provider.id,
31666
31657
  label: provider.presentation.label,
31667
31658
  skillDir: scaffold.skillDir,
31668
- aka: scaffold.aka ?? [],
31669
- comingSoon: false
31659
+ aka: scaffold.aka ?? []
31670
31660
  };
31671
31661
  }
31672
- function listScaffoldTargets() {
31662
+ function listScaffoldTargets(includeExperimental = false) {
31673
31663
  const out = [];
31674
31664
  for (const provider of builtIns().providers) {
31675
- const target = toScaffoldTarget(provider);
31665
+ const target = toScaffoldTarget(provider, includeExperimental);
31676
31666
  if (target !== null) out.push(target);
31677
31667
  }
31678
31668
  return out;
31679
31669
  }
31680
- function selectableTargets(targets) {
31681
- return targets.filter((t) => !t.comingSoon);
31682
- }
31683
31670
  function labelWithAka(target) {
31684
31671
  return target.aka.length > 0 ? `${target.label} (${target.aka.join(", ")})` : target.label;
31685
31672
  }
@@ -31688,7 +31675,7 @@ function renderTargetLines(targets, def, glyph) {
31688
31675
  for (let i = 0; i < targets.length; i += 1) {
31689
31676
  const t = targets[i];
31690
31677
  lines.push(
31691
- t.comingSoon ? tx(TUTORIAL_TEXTS.promptOptionComingSoon, { index: i + 1, label: t.label }) : tx(TUTORIAL_TEXTS.promptOption, {
31678
+ tx(TUTORIAL_TEXTS.promptOption, {
31692
31679
  index: i + 1,
31693
31680
  label: labelWithAka(t),
31694
31681
  skillDir: `${t.skillDir}/`,
@@ -31714,14 +31701,7 @@ async function promptForTarget(targets, def, stdin, stderr, glyph) {
31714
31701
  (resolveP) => rl.question(tx(TUTORIAL_TEXTS.promptInput, { index: defIndex + 1 }), resolveP)
31715
31702
  );
31716
31703
  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
- );
31704
+ if (result !== null) return result;
31725
31705
  }
31726
31706
  return null;
31727
31707
  } finally {
@@ -31977,4 +31957,4 @@ function resolveBareDefault() {
31977
31957
  process.exit(ExitCode.Error);
31978
31958
  }
31979
31959
  //# sourceMappingURL=cli.js.map
31980
- //# debugId=be3954fe-3e41-5dae-bb8d-1d8225ffd77a
31960
+ //# debugId=09459eb1-3d6f-565d-9552-4819baae6f1a