@skill-map/spec 0.37.0 → 0.39.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 +56 -0
- package/architecture.md +5 -3
- package/cli-contract.md +5 -1
- package/conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/providers/bad-provider/index.js +4 -0
- package/index.json +9 -9
- package/package.json +1 -1
- package/schemas/api/rest-envelope.schema.json +56 -11
- package/schemas/extensions/provider.schema.json +79 -1
- package/schemas/project-config.schema.json +5 -0
- package/schemas/scan-result.schema.json +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
1
|
# Spec changelog
|
|
2
2
|
|
|
3
|
+
## 0.39.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- f2b59c5: Makes the registered Provider set the single source of truth for the UI's provider surfaces (active-lens dropdown, topbar lens chip, per-node provider chip) and for active-lens auto-detection. Removes four divergent hardcoded provider lists that no longer matched the real built-in Providers (the lens dropdown offered phantom `gemini` / `cursor` entries and hid the real `antigravity` / `agent-skills`; the card chip did not know `openai` / `antigravity`; the detection table still listed `cursor`).
|
|
8
|
+
|
|
9
|
+
**Spec (`spec/schemas/extensions/provider.schema.json`)**: Providers gain a required `presentation` block (`label`, `color`, optional `colorDark` / `icon` / `emoji` / `hideChip`) describing the Provider's own identity, and an optional `detect.markers` array. Named `presentation` (not `ui`) because the shared extension `ui` key is the view-contributions map. Pre-1.0 breaking change for external Provider plugins: a manifest without `presentation` is rejected at load with a clear `must have required property 'presentation'` diagnostic (locked by the `plugin-missing-ui-rejected` conformance case).
|
|
10
|
+
|
|
11
|
+
**Spec (`spec/schemas/api/rest-envelope.schema.json`)**: new required `providerRegistry` field on every payload-bearing envelope (list / single / config), sibling of `kindRegistry`. Keyed by Provider id. Sentinel / action-result / catalog envelopes stay exempt.
|
|
12
|
+
|
|
13
|
+
**Kernel (`src/kernel/extensions/provider.ts`)**: `IProvider` gains `presentation: IProviderUi` and `detect?: { markers }`. The five built-in Providers declare both (`claude` → `.claude`, `openai` → `.codex` / `AGENTS.md`, `agent-skills` → `.agents`; `antigravity` / `markdown` carry no marker; `markdown` sets `hideChip`).
|
|
14
|
+
|
|
15
|
+
**Detection (`src/core/config/active-provider.ts`)**: the hardcoded `DETECTION_RULES` table is gone; `resolveActiveProvider` now reads markers off the provider list threaded by each caller (orchestrator, scan-runner bootstrap, BFF route, `sm config`). The detectable set derives from the registered Providers, so a Provider with a marker is auto-detectable without touching the resolver.
|
|
16
|
+
|
|
17
|
+
**BFF (`src/server/provider-registry.ts`, `envelope.ts`, `index.ts`, routes)**: new `buildProviderRegistry(providers)` assembles the catalog at boot (same discipline as `buildKindRegistry`); every payload-bearing route embeds it. The raw provider list is threaded to `/api/active-provider` for marker-based detection (the wire registry omits markers).
|
|
18
|
+
|
|
19
|
+
**SPA (`ui/src/services/provider-registry.ts`)**: new `ProviderRegistryService` (signal-backed) ingested by the REST + static data sources, replacing the static `provider-ui.ts` dictionary. The lens dropdown options, the topbar lens chip, and the per-node card chip all read from it. The demo dataset generator bakes a parallel `DEMO_PROVIDER_REGISTRY`.
|
|
20
|
+
|
|
21
|
+
**Tests**: `provider-registry.spec.ts` (kernel-side build + SPA service), updated active-provider bootstrap/drift specs to pass provider markers, updated data-source specs for the new constructor arg.
|
|
22
|
+
|
|
23
|
+
## User-facing
|
|
24
|
+
|
|
25
|
+
The lens picker in **Settings → Project** now lists exactly the providers installed in your project. The phantom **Gemini** and **Cursor** options are gone, and **OpenAI Codex** / **Antigravity** now show their correct name and colour on node cards.
|
|
26
|
+
|
|
27
|
+
## 0.38.0
|
|
28
|
+
|
|
29
|
+
### Minor Changes
|
|
30
|
+
|
|
31
|
+
- d3c47b2: Adds a hard cap on the number of files `sm scan` and `sm watch` accept after `.skillmapignore` filtering, plus a persistent UI banner that fires when the graph crosses the recommended limit. Default cap is **256 nodes**. Override per invocation with `--max-nodes <N>` (bidirectional: raises OR lowers the cap).
|
|
32
|
+
|
|
33
|
+
**Spec (`spec/schemas/project-config.schema.json`)**: new `scan.maxNodes` integer field (default 256, minimum 1). Documented in `spec/cli-contract.md` §Scan / Node cap.
|
|
34
|
+
|
|
35
|
+
**Spec (`spec/schemas/scan-result.schema.json`)**: ScanResult envelope gains two optional fields, `recommendedNodeLimit` (effective cap that produced this scan) and `overrideMaxNodes` (per-invocation override or `null`). Absent on legacy / synthetic fixtures.
|
|
36
|
+
|
|
37
|
+
**Kernel walker (`src/kernel/orchestrator/walk.ts`)**: `walkAndExtract` accepts `recommendedNodeLimit` + `overrideMaxNodes` and stops accepting classified nodes once `accum.nodes.length >= effectiveLimit`. Result envelope echoes both values plus a `capReached: boolean` derived signal so callers can phrase a "scan capped" notice without re-deriving it.
|
|
38
|
+
|
|
39
|
+
**DB schema (`src/migrations/001_initial.sql`)**: `scan_meta` gains two columns, `recommended_node_limit` and `override_max_nodes` (nullable). Edited inline per the project's greenfield rule, no new migration file. The persistence layer (`scan-persistence.ts`, `scan-load.ts`) serialises / deserialises both columns; synthetic envelopes default `recommendedNodeLimit` to 256.
|
|
40
|
+
|
|
41
|
+
**CLI surface (`src/cli/commands/scan.ts`, `src/cli/commands/watch.ts`, `src/core/runtime/scan-runner.ts`, `src/core/watcher/runtime.ts`)**: new `--max-nodes <N>` flag on `sm scan` and `sm watch` (and the alias `sm scan --watch`). Validates integer ≥ 1, anything else exits 2 with a §3.1b two-line block. When a real scan caps, the CLI prints a yellow notice naming both escape routes the user has: edit `.skillmapignore` (preferred) or re-run with a higher `--max-nodes`. `sm refresh` operates on a single already-classified node, so the cap does not apply there.
|
|
42
|
+
|
|
43
|
+
**BFF (`src/server/routes/scan.ts`, `src/kernel/adapters/sqlite/scan-load.ts`)**: `GET /api/scan` and `POST /api/scan` propagate the two new fields verbatim from `scan_meta`. The empty-DB fallback returns the design default (256) and a `null` override so the SPA reads the same field shape on cold boot as on populated DBs.
|
|
44
|
+
|
|
45
|
+
**SPA (`ui/src/app/components/oversized-banner/`)**: new `<sm-oversized-banner>` component mounted in the shell next to `<sm-demo-banner>`. Visibility is purely derived from the loaded `ScanResult`, three render modes drive the body copy:
|
|
46
|
+
|
|
47
|
+
- **capped** (red), `stats.filesWalked > effectiveLimit`. Files were dropped.
|
|
48
|
+
- **overLimit** (yellow), `nodesCount > recommendedNodeLimit` with an override above the recommendation. Graph is bigger than recommended, allowed through.
|
|
49
|
+
- **atLimit** (yellow), `nodesCount >= recommendedNodeLimit` without an override above. Soft warning at the recommended cap.
|
|
50
|
+
|
|
51
|
+
The CTA opens Settings → Project (Ignored patterns section) so the operator can trim `.skillmapignore` without leaving the SPA. No dismiss state, the banner stays until a re-scan brings the graph back under the recommended limit.
|
|
52
|
+
|
|
53
|
+
**Tests**: new unit tests for the walker cap (`walk-node-cap.spec.ts`, 4 cases covering default cap fire, override above, override below, and project-below-limit) and for the banner (`oversized-banner.spec.ts`, 6 cases covering all three modes + hide + CTA emit). Existing `buildScan` helpers in three integration specs now reset `cmd.maxNodes` so the Clipanion marker object does not leak into manually-instantiated commands.
|
|
54
|
+
|
|
55
|
+
## User-facing
|
|
56
|
+
|
|
57
|
+
New `--max-nodes <N>` on `sm scan` / `sm watch` / `sm serve` caps how many files the walker accepts (default 256, bidirectional). Past the limit, a persistent banner links to **Settings → Project** to trim `.skillmapignore`.
|
|
58
|
+
|
|
3
59
|
## 0.37.0
|
|
4
60
|
|
|
5
61
|
### Minor Changes
|
package/architecture.md
CHANGED
|
@@ -43,7 +43,7 @@ Any conforming implementation, reference or third-party, MUST respect these boun
|
|
|
43
43
|
|
|
44
44
|
A skill-map project sees its filesystem through exactly one **active provider lens** at any time. The lens is 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.
|
|
45
45
|
|
|
46
|
-
The lens is project-scope state. It lives 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
|
|
46
|
+
The lens is project-scope state. It lives 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 or yields no result, the CLI and UI prompt the user to pick one of the enabled providers. **The marker set is provider-owned**: each Provider declares its own 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/`. There is no central hardcoded detection table; the detectable set derives from the 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 and the first match is the default suggestion. A Provider with no `detect` block is never auto-suggested but can still 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-specific marker, so a Google project auto-detects as the universal `agent-skills` lens and the `antigravity` lens is set manually via `sm config set activeProvider antigravity`.
|
|
47
47
|
|
|
48
48
|
### Consequence: one graph per project at a time
|
|
49
49
|
|
|
@@ -271,7 +271,9 @@ Each `kinds[*].ui` entry declares how the UI renders nodes of that kind:
|
|
|
271
271
|
|
|
272
272
|
The `ui` block is required (not optional) by design: making it optional would force the UI to invent visuals for missing entries, silently collapsing unknown kinds to a default rendering and hiding manifest gaps. Forcing the Provider to declare presentation up-front means the UI never guesses.
|
|
273
273
|
|
|
274
|
-
The kernel ships every Provider's `ui` block to the BFF at boot; the BFF aggregates them into a `kindRegistry` map and embeds it in every payload-bearing REST envelope (see [`cli-contract.md` §Server](./cli-contract.md#server)). The UI consumes `kindRegistry` directly, built-in and user-plugin kinds render identically.
|
|
274
|
+
The kernel ships every Provider's per-kind `ui` block to the BFF at boot; the BFF aggregates them into a `kindRegistry` map and embeds it in every payload-bearing REST envelope (see [`cli-contract.md` §Server](./cli-contract.md#server)). The UI consumes `kindRegistry` directly, built-in and user-plugin kinds render identically.
|
|
275
|
+
|
|
276
|
+
Each Provider ALSO declares a top-level `presentation` block (`provider.schema.json#/properties/presentation`: `label`, `color`, optional `colorDark` / `icon` / `emoji` / `hideChip`) describing the Provider's own identity, distinct from its kinds' visuals. (It is named `presentation`, not `ui`, because the shared extension `ui` key is the view-contributions map declared only by extractor / analyzer kinds.) The BFF aggregates these into a sibling `providerRegistry` map (keyed by Provider id) embedded on the same envelopes. The UI consumes `providerRegistry` to render the active-lens dropdown, the topbar lens chip, and the per-node provider chip on cards from the real registered-Provider set, never a hardcoded list. `hideChip: true` (set by the universal `markdown` fallback) suppresses only the per-card chip; the Provider still appears in the lens surfaces. Unlike kind colors (normalised across Providers so every `agent` paints the same), Provider colors are deliberately distinct so the chip tells the user at a glance which platform a node came from.
|
|
275
277
|
|
|
276
278
|
### Provider · dispatch order and the universal markdown fallback
|
|
277
279
|
|
|
@@ -806,7 +808,7 @@ Endpoints under `/api/contributions/*`:
|
|
|
806
808
|
|
|
807
809
|
Plus catalog embedding into every payload-bearing envelope:
|
|
808
810
|
|
|
809
|
-
- `kindRegistry` and `contributionsRegistry` are siblings on the envelope (see schema). Built once per server boot, embedded into list (`nodes` / `links` / `issues` / `plugins`), single (`node`), and value (`config`) envelopes. Sentinel envelopes (`health` / `scan` / `graph`) and action-result envelopes (`sidecar.bumped`) and the catalog envelopes themselves (`annotations.registered` / `contributions.registered`) carry
|
|
811
|
+
- `kindRegistry`, `providerRegistry`, and `contributionsRegistry` are siblings on the envelope (see schema). Built once per server boot, embedded into list (`nodes` / `links` / `issues` / `plugins`), single (`node`), and value (`config`) envelopes. Sentinel envelopes (`health` / `scan` / `graph`) and action-result envelopes (`sidecar.bumped`) and the catalog envelopes themselves (`annotations.registered` / `contributions.registered`) carry none of them. `providerRegistry` is the static boot catalog of registered Providers' identity; the dynamic active lens (current value + filesystem-detected candidates) is served separately by `GET /api/active-provider`.
|
|
810
812
|
|
|
811
813
|
Plus per-node embedding on node responses:
|
|
812
814
|
|
package/cli-contract.md
CHANGED
|
@@ -82,7 +82,7 @@ The project sees its filesystem through exactly one **active provider lens** at
|
|
|
82
82
|
|
|
83
83
|
CLI surfaces:
|
|
84
84
|
|
|
85
|
-
- **Auto-detect on first scan**: when `activeProvider` is absent, `sm scan` and `sm watch` run a filesystem heuristic (`.claude/` → `claude`, `.codex/` or root `AGENTS.md` → `openai`, `.
|
|
85
|
+
- **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 a central hardcoded table, so the detectable set derives from the registered Providers. On unambiguous match, the result is persisted to `settings.json` and the scan proceeds; on no match, the CLI exits non-zero with a "no provider detected, set `activeProvider` in settings or install a provider plugin" message. On ambiguous match (multiple providers detected), the CLI prompts the user interactively (or fails with exit code 2 under `--yes` if no default is configured). Google's Antigravity CLI does not declare a vendor-specific marker (it adopted the open-standard `.agents/` layout, which auto-detects as the universal `agent-skills` lens); the `antigravity` lens is set manually via `sm config set activeProvider antigravity`.
|
|
86
86
|
- **Manual override**: `sm config set activeProvider <id>` switches the lens. The verb 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.
|
|
87
87
|
- **No per-scan flag**: there is no `sm scan --provider=<id>` flag. The lens is a project-level decision, not a per-invocation parameter. The drop+rescan cost makes per-invocation switching the wrong default UX.
|
|
88
88
|
|
|
@@ -283,6 +283,8 @@ The three privacy-sensitive keys above PLUS `allowEditSmFiles` are members of `P
|
|
|
283
283
|
|
|
284
284
|
The watcher subscribes to the same roots that `sm scan` walks and respects `.skillmapignore` plus `config.ignore` exactly as the one-shot scan does. Filesystem events are grouped using `scan.watch.debounceMs` (default 300ms) before the watcher re-runs the incremental scan and persists. `SIGINT` / `SIGTERM` close the watcher cleanly. Exit code on clean shutdown is 0.
|
|
285
285
|
|
|
286
|
+
**Node cap** (`--max-nodes <N>`): on `sm scan` and `sm watch` (alias `sm scan --watch`), a hard cap on the number of files the walker accepts after `.skillmapignore` filtering, before extractors run. Default comes from `scan.maxNodes` (default 256). The flag is a full override of the setting and is **bidirectional**: it can raise the cap (`--max-nodes 1000` on a 312-file repo) or lower it (`--max-nodes 100` cuts deeper than the default). When the walker reaches the cap, additional files are dropped in stable provider-walker order and the scan is marked oversized in `scan_meta` (columns `recommended_node_limit` and `override_max_nodes`), the resulting `ScanResult` envelope carries `recommendedNodeLimit` and `overrideMaxNodes` so the UI raises a persistent banner pointing at the `.skillmapignore` editor in Settings → Project. The CLI prints a human-mode notice naming both escapes: edit `.skillmapignore` (preferred, trims permanently) or re-run with `--max-nodes <N>` (force, graph quality may degrade past the recommended limit). `sm refresh` operates on a single already-classified node, so the cap does not apply there. Validation: integer ≥ 1, anything else exits `2` operational.
|
|
287
|
+
|
|
286
288
|
Exit: 0 on clean (or clean watcher shutdown), 1 if error-severity issues exist (one-shot scan only, the watcher does not flip exit code based on per-batch issues), 2 on operational error.
|
|
287
289
|
|
|
288
290
|
---
|
|
@@ -579,6 +581,8 @@ List endpoints conform to [`schemas/api/rest-envelope.schema.json`](schemas/api/
|
|
|
579
581
|
|
|
580
582
|
**`kindRegistry` envelope field.** Every payload-bearing variant of the REST envelope (`nodes` / `links` / `issues` / `plugins` lists, the `node` single, the `config` value envelope) embeds a required `kindRegistry: { [kindName]: { providerId, label, color, colorDark?, emoji?, icon? } }` field. Sentinel envelopes (`health`, `scan`, `graph`) are exempt, they carry no payload at the wire level. The BFF assembles the registry once at boot from EVERY built-in Provider's `kinds[*].ui` block (regardless of the boot-time enabled verdict, their module code is statically imported by `built-in-bundles.ts` and always in memory) PLUS every drop-in user Provider that loaded successfully at boot (see [`architecture.md` §Provider · `ui` presentation](architecture.md#provider--ui-presentation)). The registry is then attached to every applicable response. Built-ins are listed unconditionally because a user re-enabling one mid-session expects its kinds to render on the next scan; the runtime enabled/disabled axis is enforced at SCAN-TIME by `composeScanExtensions` reading the fresh resolver, not by hiding kinds from the registry. Drop-ins that loaded as `disabled` carry `startsAsDisabled: true` on `GET /api/plugins` and need `sm serve` restart to register, their module code was never imported. The UI consumes `kindRegistry` directly to render kind palettes, list rows, and inspector headers, built-in and user-plugin kinds render identically. A kind appearing in a response payload (e.g. `node.kind`) without a matching `kindRegistry` entry is a contract violation; the kernel rejects Providers without a `ui` block at load time so the registry is always complete for whatever kinds appear in the response.
|
|
581
583
|
|
|
584
|
+
**`providerRegistry` envelope field.** The same payload-bearing envelopes also embed a required `providerRegistry: { [providerId]: { label, color, colorDark?, emoji?, icon?, hideChip? } }` field (sibling of `kindRegistry`). Sentinel envelopes (`health`, `scan`, `graph`), action-result envelopes (`sidecar.bumped`), and the catalog envelopes (`annotations.registered`, `contributions.registered`) are exempt. Same boot-time assembly discipline as `kindRegistry`: assembled once from EVERY built-in Provider's top-level `presentation` block (regardless of enabled verdict) PLUS every drop-in user Provider that loaded at boot. The UI consumes `providerRegistry` to render the active-lens dropdown, the topbar lens chip, and the per-node provider chip from the real registered-Provider set, never a hardcoded list; `hideChip: true` (the universal `markdown` fallback) suppresses only the per-card chip. This is the static boot catalog of Provider identity; the dynamic active lens (current value + filesystem-detected candidates) is served separately by `GET /api/active-provider`.
|
|
585
|
+
|
|
582
586
|
**`contributionsRegistry` envelope field.** Same payload-bearing envelopes also embed `contributionsRegistry: { "<pluginId>/<extensionId>/<contributionId>": { pluginId, extensionId, contributionId, slot, label?, tooltip?, icon?, emptyText?, emitWhenEmpty } }`. Same boot-time assembly discipline as `kindRegistry`: ALL built-in declarations are listed regardless of enabled state (so re-enabling a built-in mid-session renders correctly on the next scan), plus drop-in user plugins that loaded at boot. The `slot` value comes from the closed catalog in `spec/schemas/view-slots.schema.json`. A view contribution emitted by an extension whose qualified id is missing from the registry is dropped by the UI's slot host (mirrors the kindRegistry contract, `startsAsDisabled` drop-ins illustrate the absence path).
|
|
583
587
|
|
|
584
588
|
**Error envelope** (mirrors `§Machine-readable output analyzers`):
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
export default {
|
|
11
11
|
version: '0.1.0',
|
|
12
12
|
description: 'provider whose markdown kind is missing the ui block',
|
|
13
|
+
// Provider-level `presentation` is present and valid, so the loader
|
|
14
|
+
// gets past manifest validation and fails specifically on the KIND's
|
|
15
|
+
// missing `ui` block (the focus of this case).
|
|
16
|
+
presentation: { label: 'Bad', color: '#000000' },
|
|
13
17
|
async *walk() {},
|
|
14
18
|
classify() {
|
|
15
19
|
return 'markdown';
|
package/index.json
CHANGED
|
@@ -174,14 +174,14 @@
|
|
|
174
174
|
}
|
|
175
175
|
]
|
|
176
176
|
},
|
|
177
|
-
"specPackageVersion": "0.
|
|
177
|
+
"specPackageVersion": "0.39.0",
|
|
178
178
|
"integrity": {
|
|
179
179
|
"algorithm": "sha256",
|
|
180
180
|
"files": {
|
|
181
|
-
"CHANGELOG.md": "
|
|
181
|
+
"CHANGELOG.md": "0c399a380a0e7a23fdaa09853107f3b515a8506047d7937ca4c7bf1414d0c41b",
|
|
182
182
|
"README.md": "1c4b0ea58c4324f301043e9f5c36976a382d0bd2bc405a2e4e18463b0c50d946",
|
|
183
|
-
"architecture.md": "
|
|
184
|
-
"cli-contract.md": "
|
|
183
|
+
"architecture.md": "d4b5e0a24e04cc545823ea26cdd4ad37e4509b07221d550ac15088d0adc980ef",
|
|
184
|
+
"cli-contract.md": "3cc513b596e5b679c047b1984695591b968cb9fdf231d664ae71ad77e94bd53e",
|
|
185
185
|
"conformance/README.md": "6871dde25b5770ed945284c9e0f749e0768ec3f5ba4966bdb215985789e43887",
|
|
186
186
|
"conformance/cases/extractor-emits-signal.json": "34b4808c232d66a0eea0f5db7632a746681432b4f0995b6bf39e8d675538451c",
|
|
187
187
|
"conformance/cases/kernel-empty-boot.json": "2a5be9c93143d07a16d998df09dcc8fa4ea2d2f9a0bff6417573ed5a770352c1",
|
|
@@ -196,7 +196,7 @@
|
|
|
196
196
|
"conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/kinds/markdown/kind.json": "6676a89bae5197e23cf50f1c11d596db558ac80f7334a7208fe57d8b92422251",
|
|
197
197
|
"conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/kinds/markdown/schema.json": "42795e7f1759fa25115a426edf5cd1b0c91b091b408aeee3f4f9fbc8f89f32bc",
|
|
198
198
|
"conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/plugin.json": "15164a6bc9e3ad21cefa532af3d4edff1b10cf6140d7f576332dc38800512e35",
|
|
199
|
-
"conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/providers/bad-provider/index.js": "
|
|
199
|
+
"conformance/fixtures/plugin-missing-ui/.skill-map/plugins/bad-provider/providers/bad-provider/index.js": "df0d9f200e401b6b13115d6e97e5a00a78074229bd86c22ec5321accf5673c01",
|
|
200
200
|
"conformance/fixtures/plugin-missing-ui/notes/example.md": "55767f0aa1b6774546a99f28c58e7b732aa9cfa5dfce8d0326470f7f622f577e",
|
|
201
201
|
"conformance/fixtures/preamble-v1.txt": "1e0aeef224b64477bdc13a949c3ad402e68249caf499ecdba1302371677c068b",
|
|
202
202
|
"conformance/fixtures/sidecar-end-to-end/.claude/agents/orphan.sm": "3102ff10a0f08f60c014f82409d45ad4faf2cefa04d652a87676d3557ad64944",
|
|
@@ -216,7 +216,7 @@
|
|
|
216
216
|
"plugin-kv-api.md": "1acc69ed82433a74e35ada61d63a6d7379fb61046ff83de1e0facbe884c64704",
|
|
217
217
|
"prompt-preamble.md": "9dd4f6d1bc6a425f8782fcee10cbe75909e8d64e28781fda56c2fae909b02f40",
|
|
218
218
|
"schemas/annotations.schema.json": "e39990d47f53e25a1b3a5587a5714486d0b819b8eeaac10d42783a675296aee1",
|
|
219
|
-
"schemas/api/rest-envelope.schema.json": "
|
|
219
|
+
"schemas/api/rest-envelope.schema.json": "7d673d651eeb208133bbfcaba2e093d8dc1b5aef8667852665e42320c8d2bca2",
|
|
220
220
|
"schemas/bump-report.schema.json": "2e6daff2435ef1a4af6c2c911fd3ce2a30ace40c8910dd6f675dd187fd40a99b",
|
|
221
221
|
"schemas/conformance-case.schema.json": "f6d4c9fb92e79cb516eeeb9d042223572a3bd5ff8e7871a0becce13916f20cf6",
|
|
222
222
|
"schemas/conformance-result.schema.json": "426998e4f5cb079778ca7d0233634667d4fbc5a7e399cc41211fabd768db8ee0",
|
|
@@ -228,7 +228,7 @@
|
|
|
228
228
|
"schemas/extensions/formatter.schema.json": "d6d417df20260e5ddfe71f104b11a45873869706f86372c3c3c78c583e06e8d5",
|
|
229
229
|
"schemas/extensions/hook.schema.json": "76bf2c07f9e689b3fd1c67cbad4516a4df10604f07103759e82670e5213ddcdf",
|
|
230
230
|
"schemas/extensions/provider-kind.schema.json": "add3c5648721e67887eb971a76b39319628effac6315cffd51f7dcf679810740",
|
|
231
|
-
"schemas/extensions/provider.schema.json": "
|
|
231
|
+
"schemas/extensions/provider.schema.json": "67181554fb3d5e5c6da81a29caeb81f23388418074089a3d3a7cf94247fcd9c9",
|
|
232
232
|
"schemas/frontmatter/base.schema.json": "df0056a9478514a0db7a705e59868fa4f67673ac1cc9c9da979de4237cdd62a1",
|
|
233
233
|
"schemas/history-stats.schema.json": "5170dec0299f3d04382a38079a27b1f26300a6b95fdb1ea0fae11050ad9f0574",
|
|
234
234
|
"schemas/input-types.schema.json": "c713b768d0b0e3d0c764afb401189f7fb624a82b4e988b73aab015cf9c67c01f",
|
|
@@ -238,11 +238,11 @@
|
|
|
238
238
|
"schemas/node.schema.json": "4d7c107ed9cd2f1b7cc4d716c547c06a00ed776bd6092d3979cac634cb5326a5",
|
|
239
239
|
"schemas/plugins-doctor.schema.json": "bb03eedf5b462b661938a44fe48635705ec13e30a2381ba122c60412416b507d",
|
|
240
240
|
"schemas/plugins-registry.schema.json": "bca763ec61419e001a5c0a4b00a2da77996bfd8113be3fcf8aaa2fa70ff65fef",
|
|
241
|
-
"schemas/project-config.schema.json": "
|
|
241
|
+
"schemas/project-config.schema.json": "253991718f714bf9409279410067b9c0c0da71e32a1b889d2f30559a0030c369",
|
|
242
242
|
"schemas/refresh-report.schema.json": "54519b8caf86ba84c182f9565be9b5084bc1631ae05e9217ee18f34c0039fff3",
|
|
243
243
|
"schemas/report-base-deterministic.schema.json": "9d318d0181d121097c906ef3af1c52d71c782740bd04cf23418d7627ce2c3ed5",
|
|
244
244
|
"schemas/report-base.schema.json": "a1021e9a59b4df9f99cd92454d797e88469766e7d49f52d231c4645ffdfdad8f",
|
|
245
|
-
"schemas/scan-result.schema.json": "
|
|
245
|
+
"schemas/scan-result.schema.json": "2ac5edeb607e0f7c26036c65066effefcbaa5a6d8534b2f42fa4e44a3964a9f6",
|
|
246
246
|
"schemas/sidecar.schema.json": "8856c387477340efbdd0a585d74bfb07a99ba15b9ce593cc67d9efebc67c6bfc",
|
|
247
247
|
"schemas/signal.schema.json": "7a9d36f13ee6fa269da7ab97e45d9831d10e0570e3f61005617128b423a4d4d8",
|
|
248
248
|
"schemas/summaries/agent.schema.json": "bf540f9a804f2b43756ab33b7deb0462620d26e88cc9379c75a5f87d3b1b47d8",
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://skill-map.dev/spec/v0/api/rest-envelope.schema.json",
|
|
4
4
|
"title": "RestEnvelope",
|
|
5
|
-
"description": "Wrapper shape for REST responses under `/api/*` (Step 14.2). Five variants distinguished by the `kind` discriminator and which payload field is present (`items` for list kinds AND `'annotations.registered'`, `item` for single-resource kinds, `value` for `kind: 'config'` and `'sidecar.bumped'`). The `/api/scan` and `/api/health` responses are exempt, they carry the underlying `ScanResult` / `IHealthResponse` shape directly. The `/api/graph` response is also exempt, it returns the formatter's native textual output (text/plain or text/markdown). Step 14.5.d adds the required `kindRegistry` field on every payload-bearing list / single / config variant so the UI can render Provider-declared kinds (label, color, icon) without hardcoding visuals;
|
|
5
|
+
"description": "Wrapper shape for REST responses under `/api/*` (Step 14.2). Five variants distinguished by the `kind` discriminator and which payload field is present (`items` for list kinds AND `'annotations.registered'`, `item` for single-resource kinds, `value` for `kind: 'config'` and `'sidecar.bumped'`). The `/api/scan` and `/api/health` responses are exempt, they carry the underlying `ScanResult` / `IHealthResponse` shape directly. The `/api/graph` response is also exempt, it returns the formatter's native textual output (text/plain or text/markdown). Step 14.5.d adds the required `kindRegistry` field on every payload-bearing list / single / config variant so the UI can render Provider-declared kinds (label, color, icon) without hardcoding visuals; the sibling `providerRegistry` field carries the registered Providers' own identity (label, color, chip visibility) so the UI renders the active-lens dropdown and the per-node provider chip from the real Provider set instead of a hardcoded list. Sentinel kinds (`health`, `scan`, `graph`) stay exempt because they don't carry an envelope payload. Step 9.6 closes the `'sidecar.bumped'` (R7) and `'annotations.registered'` (R7) gaps, both are payload-bearing but carry their own variant shapes (sidecar.bumped: `value` + `elapsedMs`, no `filters`/`counts`/`kindRegistry`; annotations.registered: `items` + `counts.total`, no `filters`/`kindRegistry`) because they project read-only kernel surfaces orthogonal to the kindRegistry. The change keeps `schemaVersion` at `'1'`, the BFF is greenfield (no released consumers depend on the prior shape), so a versioned migration buys nothing.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": ["schemaVersion", "kind"],
|
|
8
8
|
"properties": {
|
|
@@ -128,6 +128,47 @@
|
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
},
|
|
131
|
+
"providerRegistry": {
|
|
132
|
+
"type": "object",
|
|
133
|
+
"description": "Catalog of Providers registered in the current scope, keyed by Provider id. Built once per server boot from every registered Provider's `ui` block and embedded into every payload-bearing envelope so the UI renders the active-lens dropdown, the topbar lens chip, and the per-node provider chip from the real Provider set instead of a hardcoded list. Sentinel envelopes (`health`, `scan`, `graph`), action-result envelopes (`sidecar.bumped`), and the catalog envelopes (`annotations.registered`, `contributions.registered`) are exempt. Mirror of `kindRegistry`, parallel surface. The active lens itself (current value + filesystem-detected candidates) is NOT here; it is served by `GET /api/active-provider`, a per-project dynamic surface orthogonal to this static boot catalog.",
|
|
134
|
+
"additionalProperties": {
|
|
135
|
+
"type": "object",
|
|
136
|
+
"required": ["label", "color"],
|
|
137
|
+
"additionalProperties": false,
|
|
138
|
+
"properties": {
|
|
139
|
+
"label": { "type": "string", "minLength": 1 },
|
|
140
|
+
"color": { "type": "string", "pattern": "^#[0-9a-fA-F]{6}$" },
|
|
141
|
+
"colorDark": { "type": "string", "pattern": "^#[0-9a-fA-F]{6}$" },
|
|
142
|
+
"emoji": { "type": "string", "minLength": 1, "maxLength": 8 },
|
|
143
|
+
"icon": {
|
|
144
|
+
"oneOf": [
|
|
145
|
+
{
|
|
146
|
+
"type": "object",
|
|
147
|
+
"required": ["kind", "id"],
|
|
148
|
+
"additionalProperties": false,
|
|
149
|
+
"properties": {
|
|
150
|
+
"kind": { "const": "pi" },
|
|
151
|
+
"id": { "type": "string", "pattern": "^pi-[a-z0-9]+(-[a-z0-9]+)*$" }
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"type": "object",
|
|
156
|
+
"required": ["kind", "path"],
|
|
157
|
+
"additionalProperties": false,
|
|
158
|
+
"properties": {
|
|
159
|
+
"kind": { "const": "svg" },
|
|
160
|
+
"path": { "type": "string", "minLength": 1 }
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
},
|
|
165
|
+
"hideChip": {
|
|
166
|
+
"type": "boolean",
|
|
167
|
+
"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."
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
},
|
|
131
172
|
"counts": {
|
|
132
173
|
"type": "object",
|
|
133
174
|
"required": ["total"],
|
|
@@ -167,8 +208,8 @@
|
|
|
167
208
|
},
|
|
168
209
|
"oneOf": [
|
|
169
210
|
{
|
|
170
|
-
"description": "List envelope, `items` payload + `filters` + `counts` (with `returned`) + `kindRegistry` + `contributionsRegistry`. Used by `/api/nodes`, `/api/links`, `/api/issues`, `/api/plugins`.",
|
|
171
|
-
"required": ["items", "counts", "filters", "kindRegistry", "contributionsRegistry"],
|
|
211
|
+
"description": "List envelope, `items` payload + `filters` + `counts` (with `returned`) + `kindRegistry` + `providerRegistry` + `contributionsRegistry`. Used by `/api/nodes`, `/api/links`, `/api/issues`, `/api/plugins`.",
|
|
212
|
+
"required": ["items", "counts", "filters", "kindRegistry", "providerRegistry", "contributionsRegistry"],
|
|
172
213
|
"properties": {
|
|
173
214
|
"kind": { "enum": ["nodes", "links", "issues", "plugins"] },
|
|
174
215
|
"counts": { "required": ["total", "returned"] }
|
|
@@ -182,8 +223,8 @@
|
|
|
182
223
|
}
|
|
183
224
|
},
|
|
184
225
|
{
|
|
185
|
-
"description": "Single-resource envelope, `item` payload + `kindRegistry` + `contributionsRegistry`, no `counts` / `filters`. Used by `/api/nodes/:pathB64`.",
|
|
186
|
-
"required": ["item", "kindRegistry", "contributionsRegistry"],
|
|
226
|
+
"description": "Single-resource envelope, `item` payload + `kindRegistry` + `providerRegistry` + `contributionsRegistry`, no `counts` / `filters`. Used by `/api/nodes/:pathB64`.",
|
|
227
|
+
"required": ["item", "kindRegistry", "providerRegistry", "contributionsRegistry"],
|
|
187
228
|
"properties": {
|
|
188
229
|
"kind": { "const": "node" }
|
|
189
230
|
},
|
|
@@ -196,8 +237,8 @@
|
|
|
196
237
|
}
|
|
197
238
|
},
|
|
198
239
|
{
|
|
199
|
-
"description": "Value envelope, `value` payload + `kindRegistry` + `contributionsRegistry`, no `counts` / `filters`. Used by `/api/config`.",
|
|
200
|
-
"required": ["value", "kindRegistry", "contributionsRegistry"],
|
|
240
|
+
"description": "Value envelope, `value` payload + `kindRegistry` + `providerRegistry` + `contributionsRegistry`, no `counts` / `filters`. Used by `/api/config`.",
|
|
241
|
+
"required": ["value", "kindRegistry", "providerRegistry", "contributionsRegistry"],
|
|
201
242
|
"properties": {
|
|
202
243
|
"kind": { "const": "config" }
|
|
203
244
|
},
|
|
@@ -210,7 +251,7 @@
|
|
|
210
251
|
}
|
|
211
252
|
},
|
|
212
253
|
{
|
|
213
|
-
"description": "Action-result envelope, `value` + `elapsedMs` siblings, no `filters` / `counts` / `kindRegistry` / `contributionsRegistry`. Used by `POST /api/sidecar/bump` (Step 9.6.5, BFF half) where the response carries the bump report (`{ nodePath, version, status }`) plus the wall-clock duration. The registries are intentionally absent, the action result is orthogonal to
|
|
254
|
+
"description": "Action-result envelope, `value` + `elapsedMs` siblings, no `filters` / `counts` / `kindRegistry` / `providerRegistry` / `contributionsRegistry`. Used by `POST /api/sidecar/bump` (Step 9.6.5, BFF half) where the response carries the bump report (`{ nodePath, version, status }`) plus the wall-clock duration. The registries are intentionally absent, the action result is orthogonal to every catalog and the SPA already has them cached from a prior list call.",
|
|
214
255
|
"required": ["value", "elapsedMs"],
|
|
215
256
|
"properties": {
|
|
216
257
|
"kind": { "const": "sidecar.bumped" }
|
|
@@ -222,12 +263,13 @@
|
|
|
222
263
|
{ "required": ["filters"] },
|
|
223
264
|
{ "required": ["counts"] },
|
|
224
265
|
{ "required": ["kindRegistry"] },
|
|
266
|
+
{ "required": ["providerRegistry"] },
|
|
225
267
|
{ "required": ["contributionsRegistry"] }
|
|
226
268
|
]
|
|
227
269
|
}
|
|
228
270
|
},
|
|
229
271
|
{
|
|
230
|
-
"description": "Annotation-catalog envelope, `items` + `counts.total` only, no `filters` / `kindRegistry` / `contributionsRegistry` / `returned`. Used by `GET /api/annotations/registered` (Step 9.6.6, BFF half). The catalog is small (typically 0–50 entries), ships in its entirety on every response, and does not paginate; `counts.total` doubles as `items.length`.",
|
|
272
|
+
"description": "Annotation-catalog envelope, `items` + `counts.total` only, no `filters` / `kindRegistry` / `providerRegistry` / `contributionsRegistry` / `returned`. Used by `GET /api/annotations/registered` (Step 9.6.6, BFF half). The catalog is small (typically 0–50 entries), ships in its entirety on every response, and does not paginate; `counts.total` doubles as `items.length`.",
|
|
231
273
|
"required": ["items", "counts"],
|
|
232
274
|
"properties": {
|
|
233
275
|
"kind": { "const": "annotations.registered" },
|
|
@@ -241,13 +283,14 @@
|
|
|
241
283
|
{ "required": ["value"] },
|
|
242
284
|
{ "required": ["filters"] },
|
|
243
285
|
{ "required": ["kindRegistry"] },
|
|
286
|
+
{ "required": ["providerRegistry"] },
|
|
244
287
|
{ "required": ["contributionsRegistry"] },
|
|
245
288
|
{ "required": ["elapsedMs"] }
|
|
246
289
|
]
|
|
247
290
|
}
|
|
248
291
|
},
|
|
249
292
|
{
|
|
250
|
-
"description": "View-contributions-catalog envelope, `items` + `counts.total` only, no `filters` / `kindRegistry` / `contributionsRegistry` / `returned`. Used by `GET /api/contributions/registered`. Mirror of `annotations.registered`. The catalog ships in entirety; `counts.total` doubles as `items.length`. Each item is an `IRegisteredViewContribution` shape: `{ pluginId, extensionId, contributionId, slot, label?, tooltip?, icon?, emptyText?, emitWhenEmpty? }`.",
|
|
293
|
+
"description": "View-contributions-catalog envelope, `items` + `counts.total` only, no `filters` / `kindRegistry` / `providerRegistry` / `contributionsRegistry` / `returned`. Used by `GET /api/contributions/registered`. Mirror of `annotations.registered`. The catalog ships in entirety; `counts.total` doubles as `items.length`. Each item is an `IRegisteredViewContribution` shape: `{ pluginId, extensionId, contributionId, slot, label?, tooltip?, icon?, emptyText?, emitWhenEmpty? }`.",
|
|
251
294
|
"required": ["items", "counts"],
|
|
252
295
|
"properties": {
|
|
253
296
|
"kind": { "const": "contributions.registered" },
|
|
@@ -261,13 +304,14 @@
|
|
|
261
304
|
{ "required": ["value"] },
|
|
262
305
|
{ "required": ["filters"] },
|
|
263
306
|
{ "required": ["kindRegistry"] },
|
|
307
|
+
{ "required": ["providerRegistry"] },
|
|
264
308
|
{ "required": ["contributionsRegistry"] },
|
|
265
309
|
{ "required": ["elapsedMs"] }
|
|
266
310
|
]
|
|
267
311
|
}
|
|
268
312
|
},
|
|
269
313
|
{
|
|
270
|
-
"description": "Sentinel kinds, reserved for routes that do NOT carry an envelope payload at the wire level (`health`, `scan`, `graph`). They do not carry `kindRegistry` or `contributionsRegistry` either; clients that need
|
|
314
|
+
"description": "Sentinel kinds, reserved for routes that do NOT carry an envelope payload at the wire level (`health`, `scan`, `graph`). They do not carry `kindRegistry`, `providerRegistry`, or `contributionsRegistry` either; clients that need any of them must call a payload-bearing endpoint at boot.",
|
|
271
315
|
"properties": {
|
|
272
316
|
"kind": { "enum": ["health", "scan", "graph"] }
|
|
273
317
|
},
|
|
@@ -277,6 +321,7 @@
|
|
|
277
321
|
{ "required": ["item"] },
|
|
278
322
|
{ "required": ["value"] },
|
|
279
323
|
{ "required": ["kindRegistry"] },
|
|
324
|
+
{ "required": ["providerRegistry"] },
|
|
280
325
|
{ "required": ["contributionsRegistry"] },
|
|
281
326
|
{ "required": ["elapsedMs"] }
|
|
282
327
|
]
|
|
@@ -7,9 +7,87 @@
|
|
|
7
7
|
{ "$ref": "base.schema.json" }
|
|
8
8
|
],
|
|
9
9
|
"type": "object",
|
|
10
|
-
"required": ["version", "description"],
|
|
10
|
+
"required": ["version", "description", "presentation"],
|
|
11
11
|
"unevaluatedProperties": false,
|
|
12
12
|
"properties": {
|
|
13
|
+
"presentation": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"required": ["label", "color"],
|
|
16
|
+
"additionalProperties": false,
|
|
17
|
+
"description": "Presentation metadata the UI uses to render this Provider's identity: the lens-switcher dropdown label, the topbar active-lens chip, and the per-node provider chip on cards. Required so the UI never hardcodes a closed provider list, it reads every registered Provider's identity from the `providerRegistry` field embedded in REST envelopes (`api/rest-envelope.schema.json`). Named `presentation` (NOT `ui`) because the shared extension `ui` key is the view-contributions map declared only by extractor / analyzer kinds. Mirrors the per-kind `provider-kind.schema.json#/ui` shape (label + base color, optional dark variant + emoji + icon) plus `hideChip` for fallback Providers that should not badge every node.",
|
|
18
|
+
"properties": {
|
|
19
|
+
"label": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"minLength": 1,
|
|
22
|
+
"description": "Human-readable Provider name shown in the active-lens dropdown, the topbar lens chip, and the per-node provider chip (e.g. `'Claude'`, `'OpenAI Codex'`, `'Antigravity'`, `'Open Skills'`, `'Markdown'`)."
|
|
23
|
+
},
|
|
24
|
+
"color": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"pattern": "^#[0-9a-fA-F]{6}$",
|
|
27
|
+
"description": "Base hex color (light theme) for the Provider chip. The UI derives any tints it needs from this value; declaring a single base keeps the manifest small. Deliberately distinct per Provider (unlike kind colors which normalise across Providers) so the chip tells the user at a glance which platform a node came from."
|
|
28
|
+
},
|
|
29
|
+
"colorDark": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"pattern": "^#[0-9a-fA-F]{6}$",
|
|
32
|
+
"description": "Optional dark-theme variant of `color`. When absent, the UI falls back to `color`."
|
|
33
|
+
},
|
|
34
|
+
"emoji": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"minLength": 1,
|
|
37
|
+
"maxLength": 8,
|
|
38
|
+
"description": "Optional decorative emoji used as a fallback when `icon` is absent or fails to render. Bound to a small length so the UI can lay it out predictably alongside text."
|
|
39
|
+
},
|
|
40
|
+
"icon": {
|
|
41
|
+
"description": "Optional discriminated icon descriptor. The UI prefers `icon` over `emoji`; when both are absent, the UI falls back to the first letter of `label` colored with `color`.",
|
|
42
|
+
"oneOf": [
|
|
43
|
+
{
|
|
44
|
+
"type": "object",
|
|
45
|
+
"required": ["kind", "id"],
|
|
46
|
+
"additionalProperties": false,
|
|
47
|
+
"properties": {
|
|
48
|
+
"kind": { "const": "pi" },
|
|
49
|
+
"id": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"pattern": "^pi-[a-z0-9]+(-[a-z0-9]+)*$",
|
|
52
|
+
"description": "PrimeIcons identifier (e.g. `pi-cog`, `pi-bolt`). Matched verbatim against the `pi pi-<id>` class the UI emits."
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"type": "object",
|
|
58
|
+
"required": ["kind", "path"],
|
|
59
|
+
"additionalProperties": false,
|
|
60
|
+
"properties": {
|
|
61
|
+
"kind": { "const": "svg" },
|
|
62
|
+
"path": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"minLength": 1,
|
|
65
|
+
"description": "Raw SVG path data (the `d` attribute of one or more `<path>` elements, joined). The UI wraps it in `<svg viewBox=\"0 0 24 24\"><path d=\"...\"/></svg>` and tints it with `currentColor`."
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
"hideChip": {
|
|
72
|
+
"type": "boolean",
|
|
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
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"detect": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"required": ["markers"],
|
|
80
|
+
"additionalProperties": false,
|
|
81
|
+
"description": "Auto-detection markers for the active-provider lens. The lens resolver checks each marker path (relative to the scope root) and, when present, suggests this Provider as a candidate lens. Replaces the former hardcoded detection table: the set of detectable Providers now derives from the registered Providers themselves. Optional, a Provider with no `detect` block is never auto-suggested (it can still be selected manually). When several Providers match, the resolver returns the full candidate list in Provider iteration order and the first match is the default suggestion.",
|
|
82
|
+
"properties": {
|
|
83
|
+
"markers": {
|
|
84
|
+
"type": "array",
|
|
85
|
+
"minItems": 1,
|
|
86
|
+
"description": "Paths relative to the scope root whose existence signals this Provider's presence (e.g. `['.claude']`, `['.codex', 'AGENTS.md']`). A directory or a file both count; existence is the only test.",
|
|
87
|
+
"items": { "type": "string", "minLength": 1 }
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
13
91
|
"roots": {
|
|
14
92
|
"type": "array",
|
|
15
93
|
"description": "Path globs (relative to scope root) that this Provider claims. **Enforcement-grade since structure-as-truth refactor**: a Provider declaring `roots` only receives files that match at least one entry of the array; a Provider without `roots` acts as a fallback and receives files unmatched by every other Provider's roots. Two Providers whose `roots` both match the same file produce a `provider-ambiguous` issue and the file stays unclassified. `sm plugins doctor` warns when no file matched a specific Provider's roots in the latest scan.",
|
|
@@ -55,6 +55,11 @@
|
|
|
55
55
|
"minimum": 1,
|
|
56
56
|
"description": "Files larger than this are skipped with an `info`-level log entry. Default 1048576 (1 MiB). Protects against scanning accidental binary drops or generated artefacts."
|
|
57
57
|
},
|
|
58
|
+
"maxNodes": {
|
|
59
|
+
"type": "integer",
|
|
60
|
+
"minimum": 1,
|
|
61
|
+
"description": "Hard cap on the number of files the scan accepts after `.skillmapignore` filtering, before extractors run. Default 256. When the walker reaches the cap, additional files are dropped in stable provider-walker order and the scan is marked oversized in `scan_meta` (the UI raises a persistent banner pointing at the `.skillmapignore` editor in Settings → Project). Override per invocation with `--max-nodes N` on `sm scan` / `sm watch`, bidirectional (raises OR lowers the limit, the flag is a full override of this setting)."
|
|
62
|
+
},
|
|
58
63
|
"watch": {
|
|
59
64
|
"type": "object",
|
|
60
65
|
"additionalProperties": false,
|
|
@@ -37,6 +37,16 @@
|
|
|
37
37
|
"description": "Provider ids that participated in classification. Empty if no Provider matched.",
|
|
38
38
|
"items": { "type": "string" }
|
|
39
39
|
},
|
|
40
|
+
"recommendedNodeLimit": {
|
|
41
|
+
"type": "integer",
|
|
42
|
+
"minimum": 1,
|
|
43
|
+
"description": "Effective recommended cap on the number of files the walker accepted during this scan (`scan.maxNodes` from settings, default 256). Reported so the UI can decide whether to raise the persistent 'oversized graph' banner: a scan is oversized when `stats.filesWalked >= recommendedNodeLimit`. Absent on synthetic fixtures."
|
|
44
|
+
},
|
|
45
|
+
"overrideMaxNodes": {
|
|
46
|
+
"type": ["integer", "null"],
|
|
47
|
+
"minimum": 1,
|
|
48
|
+
"description": "Override applied via `--max-nodes <N>` on the verb that ran the scan (`sm scan`, `sm refresh`, `sm watch`), or `null` when no override was passed and the value above came from the setting. The override is bidirectional: it can raise the cap above the recommended limit (the UI banner stays visible until a re-scan lands below the recommended limit) or lower it (the banner also fires if `filesWalked` reaches the lowered override). Absent on synthetic fixtures."
|
|
49
|
+
},
|
|
40
50
|
"nodes": {
|
|
41
51
|
"type": "array",
|
|
42
52
|
"items": { "$ref": "node.schema.json" }
|