@skill-map/spec 0.56.0 → 0.57.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/CHANGELOG.md +26 -0
- package/architecture.md +9 -10
- package/cli-contract.md +3 -2
- package/conformance/fixtures/plugin-missing-ui/.skill-map/settings.json +7 -0
- package/index.json +8 -7
- package/package.json +1 -1
- package/schemas/api/rest-envelope.schema.json +0 -4
- package/schemas/extensions/provider.schema.json +0 -4
- package/schemas/project-config.schema.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Spec changelog
|
|
2
2
|
|
|
3
|
+
## 0.57.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- The active provider lens no longer has an unlensed (permissive) state. A project with no marker now resolves to the universal `markdown` lens (never null, never persisted, so a later vendor marker still auto-detects) instead of running every provider at once. The Settings dropdown drops the dead `(none)` entry and keeps Markdown as a selectable neutral lens, and `sm serve` now re-scans under the chosen lens after a switch instead of re-detecting it from disk.
|
|
8
|
+
|
|
9
|
+
## User-facing
|
|
10
|
+
|
|
11
|
+
A repo with no `.claude/`, `.codex/`, or `.agents/` now opens in the Markdown view instead of mixing every platform together, with no warning. Pick Markdown anytime from Settings to see your files as plain markdown. The empty `(none)` option is gone.
|
|
12
|
+
|
|
13
|
+
- Removed the `comingSoon` provider flag: not-ready providers use `stability: 'experimental'`, shipping disabled by default (not classified, auto-detected, or selectable until enabled). `openai`, `antigravity`, `agent-skills` are experimental; `agent-skills` is gated to its own lens (only `core/markdown` stays universal). Antigravity reuses the agent-skills classifier, dropping the kernel's cross-provider reservedNames lens-scope. `sm tutorial --experimental` offers them as destinations.
|
|
14
|
+
|
|
15
|
+
## User-facing
|
|
16
|
+
|
|
17
|
+
The lens dropdown no longer shows "(coming soon)" rows. Not-ready providers (OpenAI Codex, Antigravity, Open Skills) are hidden until you enable them with `sm plugins enable <id>`; `sm tutorial --experimental` offers them as tutorial destinations.
|
|
18
|
+
|
|
19
|
+
## 0.56.1
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- The `/api/branch` map projection now keeps an edge when its RESOLVED target is a rendered node, not only when the raw authored target is. Trigger-style `invokes` / `mentions` links store the trigger (`/cmd`, `@agent`) in `target` and the real node path in `resolvedTarget`; the old filter matched the raw target alone, so every resolved trigger edge was dropped from the graph and the map showed only path-style `references`. Genuinely-broken links (no resolved node) stay excluded.
|
|
24
|
+
|
|
25
|
+
## User-facing
|
|
26
|
+
|
|
27
|
+
The graph map again draws `invokes` and `mentions` arrows (a command running a skill, an agent referenced by name), not just plain file references. A recent change had hidden every resolved trigger edge from the map.
|
|
28
|
+
|
|
3
29
|
## 0.56.0
|
|
4
30
|
|
|
5
31
|
### Minor Changes
|
package/architecture.md
CHANGED
|
@@ -70,9 +70,9 @@ The UI is **not** a driving adapter; it is an HTTP/WS client of the Server. Exac
|
|
|
70
70
|
|
|
71
71
|
A skill-map project sees its filesystem through exactly one **active provider lens** at any time: the provider whose extractors, classifiers, and resolution rules apply to the whole project during a scan. All other enabled providers stay registered but their provider-specific extractors are skipped.
|
|
72
72
|
|
|
73
|
-
The lens is project-scope state, living in `.skill-map/settings.json` as the `activeProvider` key (see [`project-config.schema.json`](./schemas/project-config.schema.json#/properties/activeProvider)). When absent, the kernel auto-detects on first scan from filesystem markers and persists the result; if the heuristic is ambiguous
|
|
73
|
+
The lens is project-scope state, living in `.skill-map/settings.json` as the `activeProvider` key (see [`project-config.schema.json`](./schemas/project-config.schema.json#/properties/activeProvider)). When absent, the kernel auto-detects on first scan from filesystem markers and persists the result; if the heuristic is ambiguous (several markers), the CLI and UI prompt the user to pick one enabled provider. There is no unlensed state: when no marker is present at all, the lens resolves to the universal `core/markdown` view (lens id `markdown`), which is NOT persisted, so a vendor marker added later still auto-detects on the next scan. **The marker set is provider-owned**: each Provider declares its detection markers in its manifest `detect.markers` block (see [`provider.schema.json`](./schemas/extensions/provider.schema.json#/properties/detect)), e.g. `claude` → `.claude/`, `openai` → `.codex/` or root `AGENTS.md`, `agent-skills` → `.agents/`. No central hardcoded detection table; the detectable set derives from registered Providers, so adding a Provider with a marker makes it auto-detectable without touching the resolver. When several markers match, the resolver returns the full candidate list in Provider iteration order, first match the default suggestion. A Provider with no `detect` block is never auto-suggested but can be selected manually. Google's Antigravity CLI (which replaced the retired Gemini CLI on 2026-05-19) adopted the open-standard `.agents/` rather than a vendor marker, so a Google project's `.agents/` files are owned by the `agent-skills` provider; the `antigravity` lens is set manually via `sm config set activeProvider antigravity`. Both `agent-skills` and `antigravity` ship `experimental` (disabled by default, see below), so neither auto-detects until enabled.
|
|
74
74
|
|
|
75
|
-
**
|
|
75
|
+
**Not-ready Providers ship disabled.** A Provider that is registered but not yet ready for end users declares `stability: 'experimental'` (see [`base.schema.json`](./schemas/extensions/base.schema.json#/properties/stability)), which ships it **disabled by default**: it does not classify, does not register, is never auto-detected, and is absent from the `selectable` set served by `GET /api/active-provider` until the operator opts in (`sm plugins enable <id>`, the Settings toggle, or a config override). There is no separate `comingSoon` flag: enabled/disabled is the single availability axis, and `stability: 'experimental'` is just the installed default flipped off. Today `openai`, `antigravity`, and `agent-skills` are experimental, so only `claude` is selectable by default; enabling one (e.g. for a demo or the tutorial's `--experimental` flow) makes it both selectable and classifying. This is distinct from `hideChip`, which only suppresses the per-card badge.
|
|
76
76
|
|
|
77
77
|
### Consequence: one graph per project at a time
|
|
78
78
|
|
|
@@ -92,13 +92,13 @@ A provider plugin MAY declare it reads source files belonging to ANOTHER provide
|
|
|
92
92
|
|
|
93
93
|
The lens does NOT gate the universal extractors under `core/` (markdown links, code-region file paths, external URLs, sidecar annotations); their semantics are provider-agnostic, so they run regardless of the active provider. Provider-specific extractors (Claude's `@`-directive parser, Cursor's picker-derived references, the future Codex AGENTS.md walker, future Antigravity parsers) declare `precondition: { provider: '<id>' }` on their manifest; the orchestrator invokes them on every node visited during the scan as long as the **active lens** is in the declared provider list, regardless of which provider's `classify()` claimed the node.
|
|
94
94
|
|
|
95
|
-
The gate is the active lens, not the node's provider. A `@handle` token in `CLAUDE.md` or `notes/todo.md` (files the `claude` provider disclaims to `core/markdown`) still gets parsed by `claude/at-directive` under the `claude` lens, because the lens represents the runtime grammar and the runtime reads markdown across the whole project, not only files it owns. The earlier double-check ("node's provider matches AND the lens") silently dropped that surface; dropping the node side restores it. Cross-lens isolation holds via the lens half alone: under `openai`, claude extractors are silent on every node (including `.claude/*`) because lens authorisation is missing.
|
|
95
|
+
The gate is the active lens, not the node's provider. A `@handle` token in `CLAUDE.md` or `notes/todo.md` (files the `claude` provider disclaims to `core/markdown`) still gets parsed by `claude/at-directive` under the `claude` lens, because the lens represents the runtime grammar and the runtime reads markdown across the whole project, not only files it owns. The earlier double-check ("node's provider matches AND the lens") silently dropped that surface; dropping the node side restores it. Cross-lens isolation holds via the lens half alone: under `openai`, claude extractors are silent on every node (including `.claude/*`) because lens authorisation is missing. Under the universal `core/markdown` default lens (a project with no vendor marker), every provider-gated extractor stays silent because the lens never matches their declared provider allowlist.
|
|
96
96
|
|
|
97
97
|
### Active-lens scope for providers (classification gate)
|
|
98
98
|
|
|
99
|
-
The active lens also gates **classification**. Each Provider declares `gatedByActiveLens` on its manifest (`extensions/provider.schema.json#/properties/gatedByActiveLens`, mirrored at `IProvider.gatedByActiveLens`). Vendor providers (`claude`, `openai`, `antigravity`) set it `true`; their `classify()` only runs (and the walker only iterates their territory) when `provider.id === activeProvider`.
|
|
99
|
+
The active lens also gates **classification**. Each Provider declares `gatedByActiveLens` on its manifest (`extensions/provider.schema.json#/properties/gatedByActiveLens`, mirrored at `IProvider.gatedByActiveLens`). Vendor providers (`claude`, `openai`, `antigravity`) and the open-standard `agent-skills` provider set it `true`; their `classify()` only runs (and the walker only iterates their territory) when `provider.id === activeProvider`. The markdown fallback `core/markdown` (and any future format-based fallback) leaves the flag `false` (default) and runs on every scan as the single universal base. So a project's `.agents/skills/*` is classified as `skill` ONLY under the `agent-skills` lens; under any other lens (including `markdown`) those files fall through to `core/markdown`.
|
|
100
100
|
|
|
101
|
-
Filtering happens in `walkAndExtract` (kernel, `src/kernel/orchestrator/walk.ts`) at the provider-iteration level: a gated-off Provider does NOT walk its territory at all (the cheap path). The predicate: include the Provider when `!gatedByActiveLens ||
|
|
101
|
+
Filtering happens in `walkAndExtract` (kernel, `src/kernel/orchestrator/walk.ts`) at the provider-iteration level: a gated-off Provider does NOT walk its territory at all (the cheap path). The predicate: include the Provider when `!gatedByActiveLens || provider.id === activeProvider`. There is no unlensed branch: the resolver always yields a concrete lens (a vendor id, or `markdown` as the universal default when no marker is present), so a gated vendor Provider participates only when it is the active lens, and the `markdown` default keeps only the universal Providers running.
|
|
102
102
|
|
|
103
103
|
Consequence: under `activeProvider = 'claude'`, a `.codex/agents/foo.toml` is not classified by the `openai` Provider (gated off); whether it becomes a node depends on whether a universal Provider claims its extension. Today no universal claims `.toml`, so the file is silently absent, matching runtime reality (Claude Code never consumes `.codex/`). The same path under `activeProvider = 'openai'` becomes `openai/agent`. The `core/markdown` fallback claims every unclaimed `.md` regardless of lens, so a `.claude/agents/foo.md` under `openai` lens reverts to `markdown` (no claude territory under that lens).
|
|
104
104
|
|
|
@@ -371,7 +371,7 @@ The rules below describe both halves together. The kernel seeds `confidence: 1.0
|
|
|
371
371
|
|
|
372
372
|
The matrix is **per-link-kind, per-Provider**, strict: a `claude` Provider declaring `resolution: { mentions: ['agent'], invokes: ['command', 'skill'] }` does NOT resolve a `/foo` slash matching an agent named `foo` (slash → agent is a kind mismatch surfaced by `link-kind-conflict` / `kind-mismatch` analyzers, not silently treated as a resolution). The strictness is the load-bearing difference from the kind-agnostic `core/reference-broken`: `broken-ref`'s scope is "the name exists somewhere", post-walk resolution is "the name exists AS A VALID resolution for this link.kind". The `not-broken` + `not-resolved` combination is the documented edge case: the trigger resolves to a real node but the link's kind cannot legitimately point there, so no built-in detector touches it and it keeps the 1.0 baseline.
|
|
373
373
|
|
|
374
|
-
The lookup uses the ACTIVE PROVIDER LENS deliberately, mirroring the extractor gate (§Universal extractors and per-provider extractors): the lens grammar applies across the project's surface, not only files the matching `classify()` claimed. A `@handle` in `notes/todo.md` (classified by `core/markdown`) under the `claude` lens parses as a claude mention (extractor gate authorises it) and resolves against claude's `resolution.mentions` (resolver gate mirrors the authority). The same body under `openai` follows openai's resolution map, or short-circuits if openai declares no entry for that `link.kind`.
|
|
374
|
+
The lookup uses the ACTIVE PROVIDER LENS deliberately, mirroring the extractor gate (§Universal extractors and per-provider extractors): the lens grammar applies across the project's surface, not only files the matching `classify()` claimed. A `@handle` in `notes/todo.md` (classified by `core/markdown`) under the `claude` lens parses as a claude mention (extractor gate authorises it) and resolves against claude's `resolution.mentions` (resolver gate mirrors the authority). The same body under `openai` follows openai's resolution map, or short-circuits if openai declares no entry for that `link.kind`. Under the `markdown` default lens (a project with no vendor marker), the name path short-circuits uniformly because `core/markdown` declares no resolution map; path-match still applies.
|
|
375
375
|
|
|
376
376
|
**Distinct from the Signal IR `resolverRules` (§Resolver phase).** `resolverRules` rank candidates INSIDE a Signal (Phase 3+, no Provider declares it today); `resolution` runs against the merged Link graph post-walk and is the contract Extractors EMITTING Links rely on. The two surfaces share no mechanism and do not compose; when a Signal IR materialises into a Link, the `resolution` matrix runs unchanged against the resulting Link.
|
|
377
377
|
|
|
@@ -379,12 +379,11 @@ The lookup uses the ACTIVE PROVIDER LENS deliberately, mirroring the extractor g
|
|
|
379
379
|
|
|
380
380
|
Each Provider MAY declare an optional `reservedNames: Record<kind, string[]>` map listing, for each `node.kind` the runtime owns, the invocation names the runtime itself consumes. Anthropic's Claude CLI reserves `/help`, `/clear`, `/init`, `/agents`, `/model`, `/cost`, `/compact`, `/login`, `/logout`, … under `command`, and `general-purpose`, `output-style-setup`, `statusline-setup` under `agent`; a user-authored `.claude/commands/help.md` is silently shadowed at runtime (the built-in runs, the file is ignored).
|
|
381
381
|
|
|
382
|
-
The kernel intersects each Provider's `reservedNames[kind]` catalog with the scanned graph at orchestrator time. For every node the post-walk pipeline derives its normalised identifiers via the §Provider · kind identifiers contract, then tests them against
|
|
382
|
+
The kernel intersects each Provider's `reservedNames[kind]` catalog with the scanned graph at orchestrator time. For every node the post-walk pipeline derives its normalised identifiers via the §Provider · kind identifiers contract, then tests them against the reserved set of the node's OWN Provider (**self scope**): `reservedNames[node.kind]` of `node.provider`. Claude classifies `.claude/commands/help.md` as `claude`/`command` and reserves `help` under `command`, so the file is flagged.
|
|
383
383
|
|
|
384
|
-
|
|
385
|
-
2. **Lens scope.** When a Provider is the active lens (`activeProvider === provider.id`) it ALSO lends its catalog to nodes a *universal* (ungated) Provider classified, matched by `node.kind`. Required by runtimes adopting the open `.agents/skills/` standard instead of a vendor directory: their user invocables are owned by the neutral `agent-skills` Provider (`kind: skill`), not the vendor Provider, so self scope alone would never reach them. Google's Antigravity is exactly this shape: metadata-only (classifies nothing), reserving its `agy` built-in slash commands under `skill`; when `activeProvider === 'antigravity'`, a user `.agents/skills/goal/SKILL.md` is flagged because `/goal` is a built-in. Lens scope is skipped when `node.provider === activeProvider` (it would duplicate self scope) and when no lens is resolved (`activeProvider === null`).
|
|
384
|
+
A runtime that adopts the open `.agents/skills/` standard instead of a vendor directory **reuses the `agent-skills` classifier + `skill` kind in its OWN manifest** (plain manifest composition, no kernel rule). Under that runtime's lens the skills are classified with ITS provider id, so self scope reaches them directly. Google's Antigravity is exactly this shape: it reuses the open-standard classifier and reserves its `agy` built-in slash commands under `skill`, so when `activeProvider === 'antigravity'` a user `.agents/skills/goal/SKILL.md` (classified as `antigravity`/`skill`) is flagged because `/goal` is a built-in. There is no cross-provider "lens scope": each lens classifies its own territory and self scope covers it.
|
|
386
385
|
|
|
387
|
-
A node
|
|
386
|
+
A node landing in the reserved set joins a per-scan `Set<nodePath>` consumed by the score-phase `core/name-reserved` analyzer, which co-locates two effects in one pass (detection still lives in the orchestrator, so the same set drives both):
|
|
388
387
|
|
|
389
388
|
1. **It projects one `warn` issue per reserved-shadow node** (`severity: 'warn'`, message points at the offending file and suggests renaming).
|
|
390
389
|
|
package/cli-contract.md
CHANGED
|
@@ -107,7 +107,7 @@ The project sees its filesystem through exactly one **active provider lens** at
|
|
|
107
107
|
|
|
108
108
|
CLI surfaces:
|
|
109
109
|
|
|
110
|
-
- **Auto-detect on first scan**: when `activeProvider` is absent, `sm scan` and `sm watch` run a filesystem heuristic driven by each Provider's manifest `detect.markers` (e.g. `.claude/` → `claude`, `.codex/` or root `AGENTS.md` → `openai`, `.agents/` → `agent-skills`). The marker set is provider-owned, not hardcoded. On unambiguous match, the result is persisted to `settings.json` and the scan proceeds; on no match, the
|
|
110
|
+
- **Auto-detect on first scan**: when `activeProvider` is absent, `sm scan` and `sm watch` run a filesystem heuristic driven by each Provider's manifest `detect.markers` (e.g. `.claude/` → `claude`, `.codex/` or root `AGENTS.md` → `openai`, `.agents/` → `agent-skills`). The marker set is provider-owned, not hardcoded. On unambiguous match, the result is persisted to `settings.json` and the scan proceeds; on no match, the lens defaults to the universal `core/markdown` view (id `markdown`) without persisting it, and the scan proceeds silently (a vendor marker added later still auto-detects on the next scan); on ambiguous match (multiple detected), it prompts interactively (or fails with exit code 2 under `--yes` if no default is configured). Google's Antigravity CLI declares no vendor-specific marker (it adopted the open-standard `.agents/` layout, auto-detected as `agent-skills`); the `antigravity` lens is set manually via `sm config set activeProvider antigravity`.
|
|
111
111
|
- **Manual override**: `sm config set activeProvider <id>` switches the lens, drops the `scan_*` zone atomically (see [`db-schema.md`](./db-schema.md#zones)), and triggers an immediate rescan under the new lens. `state_*` and `config_*` zones survive.
|
|
112
112
|
- **No per-scan flag**: there is no `sm scan --provider=<id>` flag. The lens is a project-level decision; the drop+rescan cost makes per-invocation switching the wrong default UX.
|
|
113
113
|
|
|
@@ -189,6 +189,7 @@ The destination is the selected Provider's `scaffold.skillDir` (e.g. `.claude/sk
|
|
|
189
189
|
- Without `--for`, the default is the first scaffold-capable Provider in catalog order (Claude). The verb requires an empty cwd (see below), so there is no marker to detect: provider auto-detection does not apply.
|
|
190
190
|
- Without `--for`, on interactive stdin the verb prompts with a numbered list of Providers declaring `scaffold.skillDir`, marking the default (Claude); an empty answer accepts it. Each option shows the Provider label plus any `scaffold.aka` agents in parentheses (e.g. the open standard lists Antigravity and OpenAI Codex). The `aka` strings are display-only, NOT accepted by `--for`.
|
|
191
191
|
- Without `--for`, on non-interactive stdin (pipes, CI) the verb selects the default without prompting, staying scriptable.
|
|
192
|
+
- `--experimental` includes Providers flagged `stability: 'experimental'` (today `openai`, `agent-skills`) as scaffold destinations and enables them in the seeded fixture so the demo scan classifies their nodes. Without it, experimental Providers are omitted from the prompt and `--for <experimental-id>` is a usage error (they ship disabled by default). Default behaviour offers only stable Providers (Claude).
|
|
192
193
|
|
|
193
194
|
Behaviour:
|
|
194
195
|
|
|
@@ -603,7 +604,7 @@ The reference implementation ships a Hono BFF rooted at `src/server/`. One Node
|
|
|
603
604
|
| `GET /api/links?kind=&from=&to=` | implemented | `RestEnvelope` (`kind: 'links'`), list of links. Filters: `kind` (CSV whitelist of `link.kind`), `from` (exact match on `link.source`), `to` (exact match on `link.target`). No pagination at v14.2. |
|
|
604
605
|
| `GET /api/issues?severity=&analyzerId=&node=` | implemented | `RestEnvelope` (`kind: 'issues'`), list of issues. Filters: `severity` (CSV from `error\|warn\|info`), `analyzerId` (CSV; qualified or short suffix per `sm check --analyzers`), `node` (filter to issues whose `nodeIds` includes the path). No pagination at v14.2. |
|
|
605
606
|
| `GET /api/folders` | implemented | `RestEnvelope` (`kind: 'folders'`), lightweight full-corpus projection: one entry per scanned node `{ path, kind, linksInCount, linksOutCount, tokensTotal, modifiedAtMs, errorCount, warnCount, sidecarStatus }`. Only cheap scalar columns of `scan_nodes` (no frontmatter, body, links, signals, or contributions), so the SPA folders tree renders the whole corpus (up to `scan.maxScan`) with per-node data columns + per-folder issue badges without hydrating the full `ScanResult`. `tokensTotal` / `modifiedAtMs` are nullable (tokenization disabled / unknown mtime). `errorCount` / `warnCount` are the count of error / warn issues whose `nodeIds` include that path (the same incidence the tree rolls up across descendants). `sidecarStatus` is the node's sidecar drift status (`scan_nodes.sidecar_status`), `null` when there is no parseable sidecar, so the folders tree can flag per-row staleness without hydrating the branch payload. No pagination (the complete tree is the point; the corpus is already bounded by `scan.maxScan`). DB absent → zero items. |
|
|
606
|
-
| `GET /api/branch?path=<prefix>&path=<prefix>&limit=<n>` | implemented | Branch projection for the map. `path` is **repeatable**: the response is the UNION of the subtrees under every given prefix (forward-slash; a node matches a prefix when its path equals it or starts with `<prefix>/`). No `path` (or a single empty one) = the whole corpus. The union is capped at `limit` nodes (default and effective max = the scan's `maxRenderNodes`), so the response stays bounded regardless of how many prefixes are sent. Direct shape (no envelope wrap, like `/api/scan`): `{ schemaVersion, kind: 'branch', branch: { paths, total, rendered, truncated, cap }, nodes: Node[], links: Link[], issues: Issue[] }`, where `paths` echoes the requested prefixes. `nodes` is the first `rendered` nodes of the union in stable path order; `links` carries only edges whose source AND target are in `nodes
|
|
607
|
+
| `GET /api/branch?path=<prefix>&path=<prefix>&limit=<n>` | implemented | Branch projection for the map. `path` is **repeatable**: the response is the UNION of the subtrees under every given prefix (forward-slash; a node matches a prefix when its path equals it or starts with `<prefix>/`). No `path` (or a single empty one) = the whole corpus. The union is capped at `limit` nodes (default and effective max = the scan's `maxRenderNodes`), so the response stays bounded regardless of how many prefixes are sent. Direct shape (no envelope wrap, like `/api/scan`): `{ schemaVersion, kind: 'branch', branch: { paths, total, rendered, truncated, cap }, nodes: Node[], links: Link[], issues: Issue[] }`, where `paths` echoes the requested prefixes. `nodes` is the first `rendered` nodes of the union in stable path order; `links` carries only edges whose source AND **resolved target** are in `nodes` (the resolved target is `resolvedTarget`, the node a trigger-style `invokes` / `mentions` link points to, falling back to the raw `target` for path-style links; a genuinely-broken link whose target resolves to no node is excluded); `issues` carries those touching `nodes`. `truncated` is `total > cap`. Lets the SPA render a multi-folder selection without hydrating the full `ScanResult`. DB absent → empty branch (zero nodes). Validation: `limit` integer ≥ 1 else 400 `bad-query`. |
|
|
607
608
|
| `GET /api/graph?format=ascii\|json\|md` | implemented | formatter-rendered graph. `Content-Type` per format: `text/plain` (ascii), `application/json` (json), `text/markdown` (md / mermaid). Default `format=ascii`. Unknown format → 400 `bad-query`. |
|
|
608
609
|
| `GET /api/config` | implemented | `RestEnvelope` (`kind: 'config'`), merged effective config (defaults → user → user-local → project → project-local → override). |
|
|
609
610
|
| `GET /api/plugins` | implemented | `RestEnvelope` (`kind: 'plugins'`), list of installed plugins (built-in + drop-in) with status. Item shape: `{ id, version, kinds, status, reason, source: 'built-in'\|'project', description?: string, locked?: boolean, startsAsDisabled?: boolean, extensions?: Array<{ id, kind, version, enabled, description?: string, stability?: 'experimental'\|'beta'\|'stable'\|'deprecated', locked?: boolean }> }`. The plugin row has no granular toggle axis; its `status` aggregates the children (`'enabled'` when at least one extension is enabled, else `'disabled'`). The `description` carries the manifest-declared description (built-ins: hardcoded on `IBuiltInPlugin`; drop-ins: `plugin.json#/description`); each `extensions[]` entry carries its manifest's `description` per `IExtensionBase` (`extensions/base.schema.json#/properties/description`), plus the optional `stability` lifecycle label per `extensions/base.schema.json#/properties/stability` (omitted when undeclared; missing means `stable`. The SPA badges only non-default values, `experimental` / `beta` / `deprecated`, next to the extension row; `stable` renders nothing. Presentation-only EXCEPT `experimental` and `deprecated`, which each flip the extension's installed default to disabled). The SPA's Settings list renders descriptions as muted secondary text and indexes them for substring search alongside the ids. The `extensions` array is present whenever the plugin declares any extension AND loaded successfully. Each entry's `enabled` reflects the per-extension override resolution (DB > settings.json > installed default, where the default is `false` for `experimental` and `deprecated` extensions and `true` otherwise). The optional `locked: true` flag is stamped when the plugin id (or qualified extension id) appears in the host's lock-list (`src/server/locked-plugins.ts`); locked items render the toggle disabled in the SPA and any `PATCH` returns `403 locked`. Omitted when false. The optional `startsAsDisabled: true` flag is stamped on drop-in plugins (never built-ins) whose discovery-time `status` was `'disabled'`, i.e. every extension was disabled in `config_plugins` / `settings.json` at `sm serve` boot, so the handlers were never bucketed into the runtime. The SPA renders a per-row hint when this flag is set AND the user re-enables at least one of the plugin's extensions in the buffered state, since re-enabling requires `sm serve` restart (the rest of the toggle pipeline applies live). Omitted when false. |
|
package/index.json
CHANGED
|
@@ -174,14 +174,14 @@
|
|
|
174
174
|
}
|
|
175
175
|
]
|
|
176
176
|
},
|
|
177
|
-
"specPackageVersion": "0.
|
|
177
|
+
"specPackageVersion": "0.57.0",
|
|
178
178
|
"integrity": {
|
|
179
179
|
"algorithm": "sha256",
|
|
180
180
|
"files": {
|
|
181
|
-
"CHANGELOG.md": "
|
|
181
|
+
"CHANGELOG.md": "009253ec6623f1d6932eb0f3e7b1b9b0f0f6c765c9989be1c00818e8db54a216",
|
|
182
182
|
"README.md": "a790cd010b46d47883d1f37e3893cea9d7aa69ec4750c0202e6a0c99991e7980",
|
|
183
|
-
"architecture.md": "
|
|
184
|
-
"cli-contract.md": "
|
|
183
|
+
"architecture.md": "04583ed36a5336d14b27e6fe73368455867fd0856bb4199b489fb471f7edd646",
|
|
184
|
+
"cli-contract.md": "97dda0c49ef3e3666806dc0b709b248f2fc479cbedab01f407db58309651bbb5",
|
|
185
185
|
"conformance/README.md": "dcbef7249f161acf597552a05dcadc813cd0ced430dcd3f813fcf5e1c876335d",
|
|
186
186
|
"conformance/cases/backtick-path-extraction.json": "4620e7f8bc161fc57cb44001e9d99879c7e22b4865a0c27a20dc28969cd936d9",
|
|
187
187
|
"conformance/cases/extractor-collision-detection.json": "179a02c61892f0d26492de0c4e2c327fa6b4986d1265a8f119e871df6afe4658",
|
|
@@ -204,6 +204,7 @@
|
|
|
204
204
|
"conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/kinds/markdown/schema.json": "42795e7f1759fa25115a426edf5cd1b0c91b091b408aeee3f4f9fbc8f89f32bc",
|
|
205
205
|
"conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/plugin.json": "15164a6bc9e3ad21cefa532af3d4edff1b10cf6140d7f576332dc38800512e35",
|
|
206
206
|
"conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/providers/bad-provider/index.js": "df0d9f200e401b6b13115d6e97e5a00a78074229bd86c22ec5321accf5673c01",
|
|
207
|
+
"conformance/fixtures/plugin-missing-ui/.skill-map/settings.json": "3d9c3e4f9f6c1222b1c85be2709e3be92b3a68f192c3877aaeefe9a4cbc576df",
|
|
207
208
|
"conformance/fixtures/plugin-missing-ui/notes/example.md": "55767f0aa1b6774546a99f28c58e7b732aa9cfa5dfce8d0326470f7f622f577e",
|
|
208
209
|
"conformance/fixtures/preamble-v1.txt": "1e0aeef224b64477bdc13a949c3ad402e68249caf499ecdba1302371677c068b",
|
|
209
210
|
"conformance/fixtures/score-phase-confidence/.skill-map/plugins/link-scorer/analyzers/demote/index.js": "713ea8ea9585e966fe07ccab281be426cbee08223d60af427ca808581787de60",
|
|
@@ -238,7 +239,7 @@
|
|
|
238
239
|
"plugin-quickstart.md": "19092b278d80df357ea623dc3bd9f833d059582ee1356f317621913d91e50512",
|
|
239
240
|
"prompt-preamble.md": "5d0f836688aa23eafc32104c3174132340b268361f6060326eec84da17c6ad6d",
|
|
240
241
|
"schemas/annotations.schema.json": "09fcebc86e3b793bf9f03a35b38e5ca2a08d79ac3504f6f03895ac2ae1c2aded",
|
|
241
|
-
"schemas/api/rest-envelope.schema.json": "
|
|
242
|
+
"schemas/api/rest-envelope.schema.json": "8eeb1c2d79fb69eaef23737a2231d48d67e59b8b19aad816239ab4680e2c4752",
|
|
242
243
|
"schemas/bump-report.schema.json": "c763e1f89f2665c479d6a4985c1d324c65e5278331ebab82220287a07e4c4429",
|
|
243
244
|
"schemas/conformance-case.schema.json": "958b316d646d0c64a715a7a28cee66d2c2d2498a60dbfc5ae8970687c2a96954",
|
|
244
245
|
"schemas/conformance-result.schema.json": "14f983a8f4e62cd4ff964688c9b2b026a3bee3a0b762b64091c8c34db5b75777",
|
|
@@ -250,7 +251,7 @@
|
|
|
250
251
|
"schemas/extensions/formatter.schema.json": "880dc379ad545a62404403533a01eda5171edba0390561fc46ec6e986e0b9bd3",
|
|
251
252
|
"schemas/extensions/hook.schema.json": "f56aef59e9986ffdf7d86aa2e048dccccf217000a358b8c64737cbd911c48dad",
|
|
252
253
|
"schemas/extensions/provider-kind.schema.json": "499b2418bbe6d8a84a1608e26c56b52c2652a30ce314bc2989094418797dc1e6",
|
|
253
|
-
"schemas/extensions/provider.schema.json": "
|
|
254
|
+
"schemas/extensions/provider.schema.json": "30f1f001192b3ca9fc1e3aa383b23419f8d6c179d0239f54cb7f41910126a6bb",
|
|
254
255
|
"schemas/frontmatter/base.schema.json": "cff81510ed94824dfd12ab8b30ce9fbac65e42d61ae0edf3fbb6bbb6bb8bcb8c",
|
|
255
256
|
"schemas/history-stats.schema.json": "436aa0ffe744bdb699000447e86b45724fbd2cc4642781074eb1527522b9058c",
|
|
256
257
|
"schemas/input-types.schema.json": "93b27a1cbd1f131d42730eb9a89cf3af6889e9f17b20a48ce36133885503e01b",
|
|
@@ -260,7 +261,7 @@
|
|
|
260
261
|
"schemas/node.schema.json": "1ebba38e0c0ae022fccbc0cdf7c298da1720a68d4cb375f0baf9f0847998a0d8",
|
|
261
262
|
"schemas/plugins-doctor.schema.json": "03e2dc51c052a09bf0198c80e2c26e6129734ada4a748e483245de3dd8576c42",
|
|
262
263
|
"schemas/plugins-registry.schema.json": "211d081691fc83526e1593c79ed9741ad8a5dbd4db1a756f72141b0cced2ea15",
|
|
263
|
-
"schemas/project-config.schema.json": "
|
|
264
|
+
"schemas/project-config.schema.json": "1c1775de3c8859bf04792eec6b104896a0f0dbf0d5c1a8af4edf01d468c03e31",
|
|
264
265
|
"schemas/refresh-report.schema.json": "47184d4f6b15e9b7671dc178b3b3886a64422da198898508ecdb2cb27876db04",
|
|
265
266
|
"schemas/report-base-deterministic.schema.json": "59785fe6f3ceb34814bbbd03d10fa7336a32835ce598946f2923d469b32aa32a",
|
|
266
267
|
"schemas/report-base.schema.json": "e4d25f055e24f18ae0f77c24661c1bddc87ff2e43b001b6a827fcb14f9753f44",
|
package/package.json
CHANGED
|
@@ -174,10 +174,6 @@
|
|
|
174
174
|
"hideChip": {
|
|
175
175
|
"type": "boolean",
|
|
176
176
|
"description": "When `true`, the UI suppresses this Provider's per-card chip (reserved for the universal `markdown` fallback). The Provider still appears in the lens dropdown and the topbar lens chip."
|
|
177
|
-
},
|
|
178
|
-
"comingSoon": {
|
|
179
|
-
"type": "boolean",
|
|
180
|
-
"description": "When `true`, this Provider is published in the registry but not yet selectable as the active lens (dropped from `GET /api/active-provider`'s `selectable` set and skipped by auto-detect). The UI renders it greyed with a `(coming soon)` suffix. Mirror of `extensions/provider.schema.json#/properties/presentation/properties/comingSoon`."
|
|
181
177
|
}
|
|
182
178
|
}
|
|
183
179
|
}
|
|
@@ -71,10 +71,6 @@
|
|
|
71
71
|
"hideChip": {
|
|
72
72
|
"type": "boolean",
|
|
73
73
|
"description": "When `true`, the UI does NOT paint this Provider's chip on node cards. Reserved for the universal fallback Provider (`markdown`): the majority of nodes in any project carry it, so badging every generic `.md` would be visual noise and dilute the chip's purpose (signalling when a node came from a NON-default platform). The Provider still appears in the active-lens dropdown and the topbar lens chip; only the per-card badge is suppressed. Defaults to `false` (chip shown)."
|
|
74
|
-
},
|
|
75
|
-
"comingSoon": {
|
|
76
|
-
"type": "boolean",
|
|
77
|
-
"description": "When `true`, this Provider is not yet selectable as the active lens: it is published in the registry (so node chips still render and the UI can show it) but it can never be picked. The auto-detect heuristic skips it (`detectProvidersFromFilesystem`), so a coming-soon Provider's markers never produce a candidate or an ambiguous prompt; the BFF drops it from the `selectable` set served by `GET /api/active-provider`; and the UI renders it greyed with a `(coming soon)` suffix in the lens dropdown. Use this for vendor Providers whose support is registered but not yet ready for end users (today: `openai`, `antigravity`, `agent-skills`). Defaults to `false` (selectable). Distinct from `hideChip` (which only suppresses the per-card badge) and from operator-driven disable via `plugins[<id>].enabled = false` (a reversible config toggle, not a product-availability statement)."
|
|
78
74
|
}
|
|
79
75
|
}
|
|
80
76
|
},
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"activeProvider": {
|
|
20
20
|
"type": "string",
|
|
21
|
-
"description": "The active provider lens for this project. Exactly one provider id (from the registered providers) sees the project at any time. All extractors, classifiers, and resolution rules belonging to other providers are skipped during scan. Changing this triggers an atomic drop of the `scan_*` DB zone followed by a fresh scan under the new lens; `state_*` and `config_*` zones survive the switch. When absent on a fresh project, the kernel auto-detects from filesystem (presence of `.claude/`, `.codex/`, AGENTS.md, `.cursor/`, etc.) and prompts via the CLI / UI
|
|
21
|
+
"description": "The active provider lens for this project. Exactly one provider id (from the registered providers) sees the project at any time. All extractors, classifiers, and resolution rules belonging to other providers are skipped during scan. Changing this triggers an atomic drop of the `scan_*` DB zone followed by a fresh scan under the new lens; `state_*` and `config_*` zones survive the switch. When absent on a fresh project, the kernel auto-detects from filesystem markers (presence of `.claude/`, `.codex/`, AGENTS.md, `.cursor/`, etc.) and prompts via the CLI / UI when several markers match; when no marker is present the lens resolves to the universal `core/markdown` view (id `markdown`) without persisting it, so a vendor marker added later still auto-detects. The field stays optional on disk (a fresh project may omit it); the runtime always resolves it to a concrete lens, never null. Google's Antigravity CLI has no vendor-specific marker and is selected manually. Stability: experimental."
|
|
22
22
|
},
|
|
23
23
|
"activeProviderMarkers": {
|
|
24
24
|
"type": "array",
|